From afef92a88b4e8bf4d305b1d176e4f314f71325f2 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sat, 26 Dec 2020 19:11:04 +0530 Subject: [PATCH] fix: Add validations and other fixes --- .../purchase_invoice_item.json | 3 +- .../sales_invoice/test_sales_invoice.py | 32 ++++++++++++-- .../sales_invoice_item.json | 3 +- erpnext/controllers/accounts_controller.py | 4 +- erpnext/controllers/buying_controller.py | 2 +- erpnext/controllers/selling_controller.py | 2 +- erpnext/controllers/stock_controller.py | 43 ++++++++++++++++++- .../doctype/delivery_note/delivery_note.json | 12 +++++- .../delivery_note_item.json | 7 +-- .../purchase_receipt/purchase_receipt.json | 11 ++++- .../purchase_receipt_item.json | 5 ++- erpnext/stock/stock_ledger.py | 20 ++++----- 12 files changed, 115 insertions(+), 29 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json index 5af18d8670b..1f7853dbf71 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -761,6 +761,7 @@ "read_only": 1 }, { + "depends_on": "eval:parent.is_internal_supplier && parent.update_stock", "fieldname": "from_warehouse", "fieldtype": "Link", "ignore_user_permissions": 1, @@ -794,7 +795,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2020-12-25 23:43:57.272553", + "modified": "2020-12-26 17:20:36.415791", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 3398fc30834..99fbb113b16 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -22,6 +22,7 @@ from erpnext.regional.india.utils import get_ewb_data from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice +from erpnext.stock.utils import get_incoming_rate class TestSalesInvoice(unittest.TestCase): def make(self): @@ -1801,6 +1802,24 @@ class TestSalesInvoice(unittest.TestCase): si.items[0].target_warehouse = 'Work In Progress - TCP1' add_taxes(si) si.save() + + rate = 0.0 + for d in si.get('items'): + rate = get_incoming_rate({ + "item_code": d.item_code, + "warehouse": d.warehouse, + "posting_date": si.posting_date, + "posting_time": si.posting_time, + "qty": -1 * flt(d.get('stock_qty')), + "serial_no": d.serial_no, + "company": si.company, + "voucher_type": 'Sales Invoice', + "voucher_no": si.name, + "allow_zero_valuation": d.get("allow_zero_valuation") + }, raise_error_if_no_rate=False) + + rate = flt(rate, 2) + si.submit() target_doc = make_inter_company_transaction("Sales Invoice", si.name) @@ -1810,18 +1829,23 @@ class TestSalesInvoice(unittest.TestCase): target_doc.save() target_doc.submit() + tax_amount = flt(rate * (12/100), 2) si_gl_entries = [ - ["_Test Account Excise Duty - TCP1", 0.0, 12.0, nowdate()], - ["Unrealized Profit - TCP1", 12.0, 0.0, nowdate()] + ["_Test Account Excise Duty - TCP1", 0.0, tax_amount, nowdate()], + ["Unrealized Profit - TCP1", tax_amount, 0.0, nowdate()] ] check_gl_entries(self, si.name, si_gl_entries, add_days(nowdate(), -1)) pi_gl_entries = [ - ["_Test Account Excise Duty - TCP1", 12.0 , 0.0, nowdate()], - ["Unrealized Profit - TCP1", 0.0, 12.0, nowdate()] + ["_Test Account Excise Duty - TCP1", tax_amount , 0.0, nowdate()], + ["Unrealized Profit - TCP1", 0.0, tax_amount, nowdate()] ] + # Sale and Purchase both should be at valuation rate + self.assertEqual(si.items[0].rate, rate) + self.assertEqual(target_doc.items[0].rate, rate) + check_gl_entries(self, target_doc.name, pi_gl_entries, add_days(nowdate(), -1)) def test_eway_bill_json(self): diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json index aff11f19ed5..7a98afff364 100644 --- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json +++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json @@ -565,6 +565,7 @@ "print_hide": 1 }, { + "depends_on": "eval: parent.is_internal_customer && parent.update_stock", "fieldname": "target_warehouse", "fieldtype": "Link", "hidden": 1, @@ -815,7 +816,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2020-12-25 22:46:26.778091", + "modified": "2020-12-26 17:25:04.090630", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Item", diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 1e2bda17c91..89497c21191 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -963,9 +963,9 @@ class AccountsController(TransactionBase): It will an internal transfer if its an internal customer and representation company is same as billing company """ - if self.doctype == 'Sales Invoice': + if self.doctype in ('Sales Invoice', 'Delivery Note'): internal_party_field = 'is_internal_customer' - else: + elif self.doctype in ('Purchase Invoice', 'Purchase Invoice Item'): internal_party_field = 'is_internal_supplier' if self.get(internal_party_field) and (self.represents_company == self.company): diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 71e6669f428..f8aaf95d080 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -235,7 +235,7 @@ class BuyingController(StockController): "warehouse": d.from_warehouse, "posting_date": self.posting_date, "posting_time": self.posting_time, - "qty": -1 * flt(d.stock_qty), + "qty": -1 * flt(d.get('stock_qty')), "serial_no": d.serial_no, "company": self.company, "voucher_type": self.doctype, diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index c3cac9b2726..d052d8ec901 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -323,7 +323,7 @@ class SellingController(StockController): "warehouse": d.warehouse, "posting_date": self.posting_date, "posting_time": self.posting_time, - "qty": -1*flt(d.stock_qty), + "qty": -1 * flt(d.get('stock_qty') or d.get('actual_qty')), "serial_no": d.serial_no, "company": self.company, "voucher_type": self.doctype, diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 787f0b330db..1b8a5d892f2 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -23,6 +23,7 @@ class StockController(AccountsController): self.validate_inspection() self.validate_serialized_batch() self.validate_customer_provided_item() + self.validate_internal_transfer() def make_gl_entries(self, gl_entries=None, from_repost=False): if self.docstatus == 2: @@ -72,7 +73,13 @@ class StockController(AccountsController): warehouse_with_no_account = [] precision = frappe.get_precision("GL Entry", "debit_in_account_currency") for item_row in voucher_details: - sle_list = sle_map.get((item_row.name, item_row.warehouse)) + + if self.doctype == 'Stock Entry': + warehouse_field = 's_warehouse' + else: + warehouse_field = 'warehouse' + + sle_list = sle_map.get((item_row.name, item_row.get(warehouse_field))) if sle_list: for sle in sle_list: if warehouse_account.get(sle.warehouse): @@ -391,6 +398,40 @@ class StockController(AccountsController): if frappe.db.get_value('Item', d.item_code, 'is_customer_provided_item'): d.allow_zero_valuation_rate = 1 + def validate_internal_transfer(self): + if self.doctype in ('Sales Invoice', 'Delivery Note', 'Purchase Invoice', 'Purchase Receipt') \ + and self.is_internal_transfer(): + self.validate_in_transit_warehouses() + self.validate_multi_currency() + self.validate_packed_items() + self.validate_inter_company_reference() + + def validate_in_transit_warehouses(self): + if self.doctype in ('Sales Invoice', 'Delivery Note'): + for item in self.get('items'): + if not item.target_warehouse: + frappe.throw(_("Row {0}: Target Warehouse is mandatory for internal transfers")) + + if self.doctype in ('Purchase Invoice', 'Purchase Receipt'): + for item in self.get('items'): + if not item.from_warehouse: + frappe.throw(_("Row {0}: From Warehouse is mandatory for internal transfers")) + + def validate_multi_currency(self): + if self.currency != self.company_currency: + frappe.throw(_("Internal transfers can only be done in company's default currency")) + + def validate_packed_items(self): + if self.doctype in ('Sales Invoice', 'Delivery Note Item') and self.get('packed_items'): + frappe.throw(_("Packed Items cannot be transferred internally")) + + def validate_inter_company_reference(self): + if self.doctype in ('Purchase Invoice', 'Purchase Receipt'): + if not (self.get('inter_company_reference') or self.get('inter_company_invoice_reference')): + msg = _("Internal Sale or Delivery Reference needed for internal purchase") + msg += _("Please create purchase from the internal sale or delivery document itself") + frappe.throw(msg) + def repost_future_sle_and_gle(self): args = frappe._dict({ "posting_date": self.posting_date, diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index 38fc892deca..f595aade917 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -117,6 +117,7 @@ "source", "column_break5", "is_internal_customer", + "represents_company", "inter_company_reference", "per_billed", "customer_group", @@ -1261,13 +1262,22 @@ "oldfieldtype": "Link", "options": "Warehouse", "print_hide": 1 + }, + { + "description": "Company which internal customer represents.", + "fetch_from": "customer.represents_company", + "fieldname": "represents_company", + "fieldtype": "Link", + "label": "Represents Company", + "options": "Company", + "read_only": 1 } ], "icon": "fa fa-truck", "idx": 146, "is_submittable": 1, "links": [], - "modified": "2020-12-25 23:11:46.584226", + "modified": "2020-12-26 17:07:59.194403", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note", diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json index 4bbf3de5940..9de088df0ee 100644 --- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json +++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json @@ -458,7 +458,7 @@ "fieldname": "warehouse", "fieldtype": "Link", "in_list_view": 1, - "label": "From Warehouse", + "label": "Warehouse", "oldfieldname": "warehouse", "oldfieldtype": "Link", "options": "Warehouse", @@ -467,11 +467,12 @@ "width": "100px" }, { + "depends_on": "eval:parent.is_internal_customer", "fieldname": "target_warehouse", "fieldtype": "Link", "hidden": 1, "ignore_user_permissions": 1, - "label": "Customer Warehouse (Optional)", + "label": "Target Warehouse", "no_copy": 1, "options": "Warehouse", "print_hide": 1 @@ -748,7 +749,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2020-12-07 19:59:27.119856", + "modified": "2020-12-26 17:31:27.029803", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note Item", diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index 06fcc0b1585..f5581c1f98a 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -115,6 +115,7 @@ "per_returned", "is_internal_supplier", "inter_company_reference", + "represents_company", "subscription_detail", "auto_repeat", "printing_settings", @@ -1123,13 +1124,21 @@ "fieldtype": "Link", "label": "Set From Warehouse", "options": "Warehouse" + }, + { + "fetch_from": "supplier.represents_company", + "fieldname": "represents_company", + "fieldtype": "Link", + "label": "Represents Company", + "options": "Company", + "read_only": 1 } ], "icon": "fa fa-truck", "idx": 261, "is_submittable": 1, "links": [], - "modified": "2020-12-25 23:15:01.451518", + "modified": "2020-12-26 17:10:00.798835", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt", diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json index 3697c10b215..6ea8bb689cc 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -819,11 +819,12 @@ "read_only": 1 }, { + "depends_on": "eval:parent.is_internal_supplier", "fieldname": "from_warehouse", "fieldtype": "Link", "hidden": 1, "ignore_user_permissions": 1, - "label": "Supplier Warehouse", + "label": "From Warehouse", "options": "Warehouse" }, { @@ -875,7 +876,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2020-12-25 22:33:12.057271", + "modified": "2020-12-26 16:50:56.479347", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 19d493ceb04..779581779df 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -377,14 +377,14 @@ class update_entries_after(object): def update_rate_on_delivery_and_sales_return(self, sle, outgoing_rate): if frappe.db.get_value(sle.voucher_type, sle.voucher_no, 'is_internal_customer'): - frappe.db.set_value(doctype + " Item", voucher_detail_no, "rate", rate) + frappe.db.set_value(sle.voucher_type + " Item", sle.voucher_detail_no, "rate", outgoing_rate) update_taxes_and_totals(sle.voucher_type, sle.voucher_no, outgoing_rate) purchase_doctype = 'Purchase Invoice' if sle.voucher_type == 'Sales Invoice' else 'Purchase Receipt' ref_field = frappe.scrub(sle.voucher_type + ' Item') purchase_document_list = frappe.db.get_all(purchase_doctype, {'inter_company_invoice_reference': - sle.voucher_no}, ['name'], as_dict=1) + sle.voucher_no}, ['name']) for d in purchase_document_list: frappe.db.set_value(purchase_doctype + ' Item', {ref_field: sle.voucher_detail_no}, 'rate', outgoing_rate) @@ -400,13 +400,6 @@ class update_entries_after(object): {"parent_detail_docname": sle.voucher_detail_no, "item_code": sle.item_code}, "incoming_rate", outgoing_rate) - def update_taxes_and_totals(doctype, docname, rate): - ref_doc = frappe.get_doc(doctype, docname) - ref_doc.calculate_taxes_and_totals() - ref_doc.db_update() - for d in ref_doc.items: - d.db_update() - def update_rate_on_purchase_receipt(self, sle, outgoing_rate): if frappe.db.exists(sle.voucher_type + " Item", sle.voucher_detail_no): frappe.db.set_value(sle.voucher_type + " Item", sle.voucher_detail_no, "base_net_rate", outgoing_rate) @@ -508,8 +501,6 @@ class update_entries_after(object): self.wh_data.valuation_rate = new_stock_value / new_stock_qty else: self.wh_data.valuation_rate = sle.outgoing_rate - - print(self.wh_data.valuation_rate, "$#$#$#$#") else: if flt(self.wh_data.qty_after_transaction) >= 0 and sle.outgoing_rate: self.wh_data.valuation_rate = sle.outgoing_rate @@ -667,6 +658,13 @@ class update_entries_after(object): bin_doc.flags.via_stock_ledger_entry = True bin_doc.save(ignore_permissions=True) +def update_taxes_and_totals(doctype, docname, rate): + ref_doc = frappe.get_doc(doctype, docname) + ref_doc.calculate_taxes_and_totals() + ref_doc.db_update() + for d in ref_doc.items: + d.db_update() + def get_previous_sle(args, for_update=False): """ get the last sle on or before the current time-bucket,