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 b41b376cca8..58dc1e630e7 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -89,6 +89,7 @@ "po_detail", "purchase_receipt", "pr_detail", + "sales_invoice_item", "item_weight_details", "weight_per_unit", "total_weight", @@ -787,12 +788,20 @@ "fieldtype": "Currency", "label": "Outgoing Rate", "read_only": 1 + }, + { + "fieldname": "sales_invoice_item", + "fieldtype": "Data", + "label": "Sales Invoice Item", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-12-23 17:30:57.458876", + "modified": "2020-12-24 12:09:53.611463", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index ff06d03aef6..3cff0ed7c32 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -180,7 +180,7 @@ class SalesInvoice(SellingController): # this sequence because outstanding may get -ve self.make_gl_entries() - + if self.update_stock == 1: self.repost_future_sle_and_gle() @@ -262,10 +262,10 @@ class SalesInvoice(SellingController): self.update_stock_ledger() self.make_gl_entries_on_cancel() - + if self.update_stock == 1: self.repost_future_sle_and_gle() - + frappe.db.set(self, 'status', 'Cancelled') if frappe.db.get_single_value('Selling Settings', 'sales_update_frequency') == "Each Transaction": @@ -1628,7 +1628,8 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None): 'field_map': { source_document_warehouse_field: target_document_warehouse_field, 'batch_no': 'batch_no', - 'serial_no': 'serial_no' + 'serial_no': 'serial_no', + 'name': 'sales_invoice_item' } }) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 0f1aa23064c..1e2bda17c91 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -75,6 +75,7 @@ class AccountsController(TransactionBase): self.ensure_supplier_is_not_blocked() self.validate_date_with_fiscal_year() + self.set_incoming_rate() if self.meta.get_field("currency"): self.calculate_taxes_and_totals() diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 83352272131..71e6669f428 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -66,8 +66,6 @@ class BuyingController(StockController): if self.doctype in ("Purchase Receipt", "Purchase Invoice"): self.update_valuation_rate() - self.set_out_going_rate() - def set_missing_values(self, for_validate=False): super(BuyingController, self).set_missing_values(for_validate) @@ -220,31 +218,39 @@ class BuyingController(StockController): else: item.valuation_rate = 0.0 - def set_out_going_rate(self): + def set_incoming_rate(self): if self.doctype not in ("Purchase Receipt", "Purchase Invoice"): return + ref_doctype = 'Delivery Note Item' if self.doctype == 'Purchase Receipt' else 'Sales Invoice Item' + items = self.get("items") for d in items: - if not cint(self.get("is_return")) and d.get("target_warehouse"): + if not cint(self.get("is_return")) and d.get("from_warehouse"): # Get outgoing rate based on original item cost based on valuation method - d.outgoing_rate = get_incoming_rate({ - "item_code": d.item_code, - "warehouse": d.target_warehouse, - "posting_date": self.posting_date, - "posting_time": self.posting_time, - "qty": -1 * flt(d.qty), - "serial_no": d.serial_no, - "company": self.company, - "voucher_type": self.doctype, - "voucher_no": self.name, - "allow_zero_valuation": d.get("allow_zero_valuation") - }, raise_error_if_no_rate=False) - elif self.get("return_against"): - # Get incoming rate of return entry from reference document - # based on original item cost as per valuation method - d.outgoing_rate = get_rate_for_return(self.doctype, self.name, d.item_code, self.return_against, item_row=d) + if not d.get('sales_invoice_item'): + outgoing_rate = get_incoming_rate({ + "item_code": d.item_code, + "warehouse": d.from_warehouse, + "posting_date": self.posting_date, + "posting_time": self.posting_time, + "qty": -1 * flt(d.stock_qty), + "serial_no": d.serial_no, + "company": self.company, + "voucher_type": self.doctype, + "voucher_no": self.name, + "allow_zero_valuation": d.get("allow_zero_valuation") + }, raise_error_if_no_rate=False) + + rate = flt(outgoing_rate * d.conversion_factor) + else: + rate = frappe.db.get_value(ref_doctype, d.get(frappe.scrub(ref_doctype)), 'rate') + + if rate != d.rate: + frappe.msgprint(_("Row {0}: Item rate has been updated as per valuation rate since its an internal stock transfer") + .format(d.idx), alert=1) + d.rate = rate def get_supplied_items_cost(self, item_row_id, reset_outgoing_rate=True): supplied_items_cost = 0.0 @@ -581,7 +587,8 @@ class BuyingController(StockController): from_warehouse_sle = self.get_sl_entries(d, { "actual_qty": -1 * pr_qty, "warehouse": d.from_warehouse, - "outgoing_rate": d.outgoing_rate, + "outgoing_rate": d.rate, + "recalculate_rate": 1, "dependant_sle_voucher_detail_no": d.name }) @@ -592,8 +599,10 @@ class BuyingController(StockController): "serial_no": cstr(d.serial_no).strip() }) if self.is_return: + outgoing_rate = get_rate_for_return(self.doctype, self.name, d.item_code, self.return_against, item_row=d) + sle.update({ - "outgoing_rate": d.outgoing_rate, + "outgoing_rate": outgoing_rate, "recalculate_rate": 1 }) if d.from_warehouse: diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index eb314de49ca..c3cac9b2726 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -49,7 +49,6 @@ class SellingController(StockController): self.set_customer_address() self.validate_for_duplicate_items() self.validate_target_warehouse() - self.set_incoming_rate() def set_missing_values(self, for_validate=False): @@ -324,7 +323,7 @@ class SellingController(StockController): "warehouse": d.warehouse, "posting_date": self.posting_date, "posting_time": self.posting_time, - "qty": -1*flt(d.qty), + "qty": -1*flt(d.stock_qty), "serial_no": d.serial_no, "company": self.company, "voucher_type": self.doctype, @@ -334,8 +333,9 @@ class SellingController(StockController): # For internal transfers use incoming rate as the valuation rate if self.get('is_internal_customer') and d.get('target_warehouse'): - d.rate = d.incoming_rate - frappe.msgprint(_("Row {0}: Item rate updated as the valuation rate since its an internal transfer").format(d.idx)) + d.rate = flt(d.incoming_rate * d.conversion_factor) + frappe.msgprint(_("Row {0}: Item rate has been updated as per valuation rate since its an internal stock transfer") + .format(d.idx), alert=1) elif self.get("return_against"): # Get incoming rate of return entry from reference document diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index a30cadf0a04..711e603c9fe 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -722,7 +722,8 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None): doctype +" Item": { "doctype": target_doctype + " Item", "field_map": { - source_document_warehouse_field: target_document_warehouse_field + source_document_warehouse_field: target_document_warehouse_field, + 'name': 'delivery_note_item' }, "field_no_map": [ "warehouse" 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 5de52a40516..417eb5e78fd 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -77,6 +77,7 @@ "purchase_order_item", "material_request_item", "purchase_receipt_item", + "delivery_note_item", "section_break_45", "allow_zero_valuation_rate", "bom", @@ -868,12 +869,20 @@ "fieldtype": "Currency", "label": "Outgoing Rate", "read_only": 1 + }, + { + "fieldname": "delivery_note_item", + "fieldtype": "Data", + "label": "Delivery Note Item", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2020-12-23 17:33:19.479325", + "modified": "2020-12-24 12:10:46.943722", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 5b9ada0ee56..19d493ceb04 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -41,7 +41,7 @@ def make_sl_entries(sl_entries, allow_negative_stock=False, via_landed_cost_vouc if sle.get("actual_qty") or sle.get("voucher_type")=="Stock Reconciliation": sle_doc = make_entry(sle, allow_negative_stock, via_landed_cost_voucher) - + args = sle_doc.as_dict() update_bin(args, allow_negative_stock, via_landed_cost_voucher) @@ -65,7 +65,7 @@ def make_entry(args, allow_negative_stock=False, via_landed_cost_voucher=False): def repost_future_sle(args=None, voucher_type=None, voucher_no=None, allow_negative_stock=False, via_landed_cost_voucher=False): if not args and voucher_type and voucher_no: args = get_args_for_voucher(voucher_type, voucher_no) - + distinct_item_warehouses = [(d.item_code, d.warehouse) for d in args] i = 0 @@ -80,7 +80,7 @@ def repost_future_sle(args=None, voucher_type=None, voucher_no=None, allow_negat for item_wh, new_sle in iteritems(obj.new_items): if item_wh not in distinct_item_warehouses: args.append(new_sle) - + i += 1 def get_args_for_voucher(voucher_type, voucher_no): @@ -127,7 +127,7 @@ class update_entries_after(object): self.initialize_previous_data(self.args) self.build() - + def get_precision(self): company_base_currency = frappe.get_cached_value('Company', self.company, "default_currency") self.precision = get_field_precision(frappe.get_meta("Stock Ledger Entry").get_field("stock_value"), @@ -213,13 +213,13 @@ class update_entries_after(object): # includes current entry! args = self.data[self.args.warehouse].previous_sle \ or frappe._dict({"item_code": self.item_code, "warehouse": self.args.warehouse}) - + return list(self.get_sle_after_datetime(args)) def get_dependent_entries_to_fix(self, entries_to_fix, sle): dependant_sle = get_sle_by_voucher_detail_no(sle.dependant_sle_voucher_detail_no, excluded_sle=sle.name) - + if not dependant_sle: return elif dependant_sle.item_code == self.item_code and dependant_sle.warehouse == self.args.warehouse: @@ -251,7 +251,7 @@ class update_entries_after(object): # Get dynamic incoming/outgoing rate self.get_dynamic_incoming_outgoing_rate(sle) - + if sle.serial_no: self.get_serialized_values(sle) self.wh_data.qty_after_transaction += flt(sle.actual_qty) @@ -329,7 +329,7 @@ class update_entries_after(object): rate = get_rate_for_return(sle.voucher_type, sle.voucher_no, sle.item_code, voucher_detail_no=sle.voucher_detail_no) else: if sle.voucher_type in ("Purchase Receipt", "Purchase Invoice"): - rate_field = "valuation_rate" + rate_field = "valuation_rate" else: rate_field = "incoming_rate" @@ -344,7 +344,7 @@ class update_entries_after(object): ref_doctype = "Packed Item" else: ref_doctype = "Purchase Receipt Item Supplied" - + rate = frappe.db.get_value(ref_doctype, {"parent_detail_docname": sle.voucher_detail_no, "item_code": sle.item_code}, rate_field) @@ -374,8 +374,22 @@ class update_entries_after(object): stock_entry.db_update() for d in stock_entry.items: d.db_update() - + 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) + 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) + + for d in purchase_document_list: + frappe.db.set_value(purchase_doctype + ' Item', {ref_field: sle.voucher_detail_no}, 'rate', outgoing_rate) + update_taxes_and_totals(purchase_doctype, d.name, outgoing_rate) + # Update item's incoming rate on transaction item_code = frappe.db.get_value(sle.voucher_type + " Item", sle.voucher_detail_no, "item_code") if item_code == sle.item_code: @@ -386,6 +400,13 @@ 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) @@ -488,6 +509,7 @@ class update_entries_after(object): 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 @@ -631,7 +653,7 @@ class update_entries_after(object): frappe.throw(message, NegativeStockError, title='Insufficient Stock') else: raise NegativeStockError(message) - + def update_bin(self): # update bin for each warehouse for warehouse, data in iteritems(self.data): @@ -766,7 +788,7 @@ def update_qty_in_future_sle(args, allow_negative_stock=None): frappe.db.sql(""" update `tabStock Ledger Entry` set qty_after_transaction = qty_after_transaction + {qty} - where + where item_code = %(item_code)s and warehouse = %(warehouse)s and voucher_no != %(voucher_no)s @@ -794,7 +816,7 @@ def validate_negative_qty_in_future_sle(args, allow_negative_stock=None): frappe.get_desk_link('Warehouse', args.warehouse), sle[0]["posting_date"], sle[0]["posting_time"], frappe.get_desk_link(sle[0]["voucher_type"], sle[0]["voucher_no"])) - + frappe.throw(message, NegativeStockError, title='Insufficient Stock') def get_future_sle_with_negative_qty(args): @@ -803,7 +825,7 @@ def get_future_sle_with_negative_qty(args): qty_after_transaction, posting_date, posting_time, voucher_type, voucher_no from `tabStock Ledger Entry` - where + where item_code = %(item_code)s and warehouse = %(warehouse)s and voucher_no != %(voucher_no)s