Browse Source

fix: Check for backdated validation only for transaction company (#23639)

* fix: Check SLE only for transaction company

* fix: Add tests

* fix: Move backdated entry validation from transaction base to stock ledger entry

* chore: Add tests

Co-authored-by: Nabin Hait <nabinhait@gmail.com>
develop
Deepesh Garg 4 years ago
committed by GitHub
parent
commit
8859b71956
No known key found for this signature in database GPG Key ID: 4AEE18F83AFDEB23
  1. 55
      erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
  2. 28
      erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
  3. 41
      erpnext/utilities/transaction_base.py

55
erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py

@ -6,7 +6,7 @@ import unittest
import json
import frappe, erpnext
import frappe.defaults
from frappe.utils import cint, flt, cstr, today, random_string
from frappe.utils import cint, flt, cstr, today, random_string, add_days
from erpnext.stock.doctype.purchase_receipt.purchase_receipt import make_purchase_invoice
from erpnext.stock.doctype.item.test_item import create_item
from erpnext import set_perpetual_inventory
@ -665,6 +665,59 @@ class TestPurchaseReceipt(unittest.TestCase):
warehouse.account = ''
warehouse.save()
def test_backdated_purchase_receipt(self):
# make purchase receipt for default company
make_purchase_receipt(company="_Test Company 4", warehouse="Stores - _TC4")
# try to make another backdated PR
posting_date = add_days(today(), -1)
pr = make_purchase_receipt(company="_Test Company 4", warehouse="Stores - _TC4",
do_not_submit=True)
pr.set_posting_time = 1
pr.posting_date = posting_date
pr.save()
self.assertRaises(frappe.ValidationError, pr.submit)
# make purchase receipt for other company backdated
pr = make_purchase_receipt(company="_Test Company 5", warehouse="Stores - _TC5",
do_not_submit=True)
pr.set_posting_time = 1
pr.posting_date = posting_date
pr.submit()
# Allowed to submit for other company's PR
self.assertEqual(pr.docstatus, 1)
def test_backdated_purchase_receipt_for_same_company_different_warehouse(self):
# make purchase receipt for default company
make_purchase_receipt(company="_Test Company 4", warehouse="Stores - _TC4")
# try to make another backdated PR
posting_date = add_days(today(), -1)
pr = make_purchase_receipt(company="_Test Company 4", warehouse="Stores - _TC4",
do_not_submit=True)
pr.set_posting_time = 1
pr.posting_date = posting_date
pr.save()
self.assertRaises(frappe.ValidationError, pr.submit)
# make purchase receipt for other company backdated
pr = make_purchase_receipt(company="_Test Company 4", warehouse="Finished Goods - _TC4",
do_not_submit=True)
pr.set_posting_time = 1
pr.posting_date = posting_date
pr.submit()
# Allowed to submit for other company's PR
self.assertEqual(pr.docstatus, 1)
def get_sl_entries(voucher_type, voucher_no):
return frappe.db.sql(""" select actual_qty, warehouse, stock_value_difference
from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s

28
erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py

@ -5,7 +5,7 @@
from __future__ import unicode_literals
import frappe
from frappe import _
from frappe.utils import flt, getdate, add_days, formatdate
from frappe.utils import flt, getdate, add_days, formatdate, get_datetime, date_diff
from frappe.model.document import Document
from datetime import date
from erpnext.controllers.item_variant import ItemTemplateCannotHaveStock
@ -33,6 +33,8 @@ class StockLedgerEntry(Document):
self.scrub_posting_time()
self.validate_and_set_fiscal_year()
self.block_transactions_against_group_warehouse()
self.validate_with_last_transaction_posting_time()
self.validate_future_posting()
def on_submit(self):
self.check_stock_frozen_date()
@ -139,6 +141,30 @@ class StockLedgerEntry(Document):
from erpnext.stock.utils import is_group_warehouse
is_group_warehouse(self.warehouse)
def validate_with_last_transaction_posting_time(self):
last_transaction_time = frappe.db.sql("""
select MAX(timestamp(posting_date, posting_time)) as posting_time
from `tabStock Ledger Entry`
where docstatus = 1 and item_code = %s
and warehouse = %s""", (self.item_code, self.warehouse))[0][0]
cur_doc_posting_datetime = "%s %s" % (self.posting_date, self.get("posting_time") or "00:00:00")
if last_transaction_time and get_datetime(cur_doc_posting_datetime) < get_datetime(last_transaction_time):
msg = _("Last Stock Transaction for item {0} under warehouse {1} was on {2}.").format(frappe.bold(self.item_code),
frappe.bold(self.warehouse), frappe.bold(last_transaction_time))
msg += "<br><br>" + _("Stock Transactions for Item {0} under warehouse {1} cannot be posted before this time.").format(
frappe.bold(self.item_code), frappe.bold(self.warehouse))
msg += "<br><br>" + _("Please remove this item and try to submit again or update the posting time.")
frappe.throw(msg, title=_("Backdated Stock Entry"))
def validate_future_posting(self):
if date_diff(self.posting_date, getdate()) > 0:
msg = _("Posting future stock transactions are not allowed due to Immutable Ledger")
frappe.throw(msg, title=_("Future Posting Not Allowed"))
def on_doctype_update():
if not frappe.db.has_index('tabStock Ledger Entry', 'posting_sort_index'):
frappe.db.commit()

41
erpnext/utilities/transaction_base.py

@ -29,28 +29,6 @@ class TransactionBase(StatusUpdater):
except ValueError:
frappe.throw(_('Invalid Posting Time'))
self.validate_future_posting()
self.validate_with_last_transaction_posting_time()
def is_stock_transaction(self):
if self.doctype not in ["Sales Invoice", "Purchase Invoice", "Stock Entry", "Stock Reconciliation",
"Delivery Note", "Purchase Receipt", "Fees"]:
return False
if self.doctype in ["Sales Invoice", "Purchase Invoice"]:
if not (self.get("update_stock") or self.get("is_pos")):
return False
return True
def validate_future_posting(self):
if not self.is_stock_transaction():
return
if getattr(self, 'set_posting_time', None) and date_diff(self.posting_date, nowdate()) > 0:
msg = _("Posting future transactions are not allowed due to Immutable Ledger")
frappe.throw(msg, title=_("Future Posting Not Allowed"))
def add_calendar_event(self, opts, force=False):
if cstr(self.contact_by) != cstr(self._prev.contact_by) or \
cstr(self.contact_date) != cstr(self._prev.contact_date) or force or \
@ -180,25 +158,6 @@ class TransactionBase(StatusUpdater):
return ret
def validate_with_last_transaction_posting_time(self):
if not self.is_stock_transaction():
return
for item in self.get('items'):
last_transaction_time = frappe.db.sql("""
select MAX(timestamp(posting_date, posting_time)) as posting_time
from `tabStock Ledger Entry`
where docstatus = 1 and item_code = %s """, (item.item_code))[0][0]
cur_doc_posting_datetime = "%s %s" % (self.posting_date, self.get("posting_time") or "00:00:00")
if last_transaction_time and get_datetime(cur_doc_posting_datetime) < get_datetime(last_transaction_time):
msg = _("Last Stock Transaction for item {0} was on {1}.").format(frappe.bold(item.item_code), frappe.bold(last_transaction_time))
msg += "<br><br>" + _("Stock Transactions for Item {0} cannot be posted before this time.").format(frappe.bold(item.item_code))
msg += "<br><br>" + _("Please remove this item and try to submit again or update the posting time.")
frappe.throw(msg, title=_("Backdated Stock Entry"))
def delete_events(ref_type, ref_name):
events = frappe.db.sql_list(""" SELECT
distinct `tabEvent`.name

Loading…
Cancel
Save