fix: Item valuation for internal stock transfers (#24200)
* fix: Item valuation for internal stocktransfers
* fix: Consider conversion factor for invoices
* fix: Add missing set warehouse fields
* fix: Add validations and other fixes
* fix: Fixes in flow
* fix: Internal sales item link in Purchase Invoice
* fix: Debugging
* fix: Debug travis
* fix: Remove commented code
* fix: Rate forcing in sales order
* fix: Unreallized profit in Sales Register
* fix: Item wise gst sales register fix
* fix: From warehouse in Purchase Order
* fix: Target field in Sales Invoice
* fix: remove self
* fix: GST Purchasse register and other fixes
* fix: Add shipping_address to no field map
* fix: Ref doc map
* fix: Test Cases
* fix: address mapping between sales and purchase document
* fix: Import Error
* fix: Tax updation for internal invoices
* fix: Purchase Order and Invoice linking
* fix: Internal Party validation fix
* fix: validation
* fix(india): GST Taxes for intra state transfer
* fix(india): GST Taxes for intra state transfer
Co-authored-by: Nabin Hait <nabinhait@gmail.com>
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
index 4a952a3..06aa20b 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js
@@ -275,8 +275,11 @@
supplier: function() {
var me = this;
- if(this.frm.updating_party_details)
+
+ // Do not update if inter company reference is there as the details will already be updated
+ if(this.frm.updating_party_details || this.frm.doc.inter_company_invoice_reference)
return;
+
erpnext.utils.get_party_details(this.frm, "erpnext.accounts.party.get_party_details",
{
posting_date: this.frm.doc.posting_date,
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
index c64ffd8..451c936 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
@@ -57,8 +57,8 @@
"set_warehouse",
"rejected_warehouse",
"col_break_warehouse",
+ "set_from_warehouse",
"is_subcontracted",
- "supplier_warehouse",
"items_section",
"update_stock",
"scan_barcode",
@@ -515,6 +515,7 @@
},
{
"depends_on": "update_stock",
+ "description": "Sets 'Accepted Warehouse' in each row of the items table.",
"fieldname": "set_warehouse",
"fieldtype": "Link",
"label": "Set Accepted Warehouse",
@@ -544,17 +545,6 @@
"print_hide": 1
},
{
- "depends_on": "eval:doc.is_subcontracted==\"Yes\"",
- "fieldname": "supplier_warehouse",
- "fieldtype": "Link",
- "label": "Supplier Warehouse",
- "no_copy": 1,
- "options": "Warehouse",
- "print_hide": 1,
- "print_width": "50px",
- "width": "50px"
- },
- {
"fieldname": "items_section",
"fieldtype": "Section Break",
"oldfieldtype": "Section Break",
@@ -1232,7 +1222,9 @@
"fieldname": "inter_company_invoice_reference",
"fieldtype": "Link",
"label": "Inter Company Invoice Reference",
+ "no_copy": 1,
"options": "Sales Invoice",
+ "print_hide": 1,
"read_only": 1
},
{
@@ -1356,13 +1348,25 @@
"fieldtype": "Link",
"label": "Represents Company",
"options": "Company"
+ },
+ {
+ "depends_on": "eval:doc.update_stock && (doc.is_subcontracted==\"Yes\" || doc.is_internal_supplier)",
+ "description": "Sets 'From Warehouse' in each row of the items table.",
+ "fieldname": "set_from_warehouse",
+ "fieldtype": "Link",
+ "label": "Set From Warehouse",
+ "no_copy": 1,
+ "options": "Warehouse",
+ "print_hide": 1,
+ "print_width": "50px",
+ "width": "50px"
}
],
"icon": "fa fa-file-text",
"idx": 204,
"is_submittable": 1,
"links": [],
- "modified": "2020-12-11 12:46:12.796378",
+ "modified": "2020-12-26 20:49:03.305063",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice",
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index b52678e..56feb57 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -443,7 +443,7 @@
else:
self.stock_received_but_not_billed = None
self.expenses_included_in_valuation = None
-
+
self.negative_expense_to_be_booked = 0.0
gl_entries = []
@@ -457,7 +457,7 @@
self.make_internal_transfer_gl_entries(gl_entries)
gl_entries = make_regional_gl_entries(gl_entries, self)
-
+
gl_entries = merge_similar_entries(gl_entries)
self.make_payment_gl_entries(gl_entries)
@@ -480,7 +480,7 @@
grand_total = self.rounded_total if (self.rounding_adjustment and self.rounded_total) else self.grand_total
if grand_total and not self.is_internal_transfer():
- # Didnot use base_grand_total to book rounding loss gle
+ # Did not use base_grand_total to book rounding loss gle
grand_total_in_company_currency = flt(grand_total * self.conversion_rate,
self.precision("grand_total"))
gl_entries.append(
@@ -511,8 +511,8 @@
voucher_wise_stock_value = {}
if self.update_stock:
for d in frappe.get_all('Stock Ledger Entry',
- fields = ["voucher_detail_no", "stock_value_difference"], filters={'voucher_no': self.name}):
- voucher_wise_stock_value.setdefault(d.voucher_detail_no, d.stock_value_difference)
+ fields = ["voucher_detail_no", "stock_value_difference", "warehouse"], filters={'voucher_no': self.name}):
+ voucher_wise_stock_value.setdefault((d.voucher_detail_no, d.warehouse), d.stock_value_difference)
valuation_tax_accounts = [d.account_head for d in self.get("taxes")
if d.category in ('Valuation', 'Total and Valuation')
@@ -563,16 +563,17 @@
)
else:
- gl_entries.append(
- self.get_gl_dict({
- "account": item.expense_account,
- "against": self.supplier,
- "debit": warehouse_debit_amount,
- "remarks": self.get("remarks") or _("Accounting Entry for Stock"),
- "cost_center": item.cost_center,
- "project": item.project or self.project
- }, account_currency, item=item)
- )
+ if not self.is_internal_transfer():
+ gl_entries.append(
+ self.get_gl_dict({
+ "account": item.expense_account,
+ "against": self.supplier,
+ "debit": warehouse_debit_amount,
+ "remarks": self.get("remarks") or _("Accounting Entry for Stock"),
+ "cost_center": item.cost_center,
+ "project": item.project or self.project
+ }, account_currency, item=item)
+ )
# Amount added through landed-cost-voucher
if landed_cost_entries:
@@ -624,13 +625,14 @@
if expense_booked_in_pr:
expense_account = service_received_but_not_billed_account
- gl_entries.append(self.get_gl_dict({
- "account": expense_account,
- "against": self.supplier,
- "debit": amount,
- "cost_center": item.cost_center,
- "project": item.project or self.project
- }, account_currency, item=item))
+ if not self.is_internal_transfer():
+ gl_entries.append(self.get_gl_dict({
+ "account": expense_account,
+ "against": self.supplier,
+ "debit": amount,
+ "cost_center": item.cost_center,
+ "project": item.project or self.project
+ }, account_currency, item=item))
# If asset is bought through this document and not linked to PR
if self.update_stock and item.landed_cost_voucher_amount:
@@ -795,10 +797,10 @@
# Stock ledger value is not matching with the warehouse amount
if (self.update_stock and voucher_wise_stock_value.get(item.name) and
- warehouse_debit_amount != flt(voucher_wise_stock_value.get(item.name), net_amt_precision)):
+ warehouse_debit_amount != flt(voucher_wise_stock_value.get((item.name, item.warehouse)), net_amt_precision)):
cost_of_goods_sold_account = self.get_company_default("default_expense_account")
- stock_amount = flt(voucher_wise_stock_value.get(item.name), net_amt_precision)
+ stock_amount = flt(voucher_wise_stock_value.get((item.name, item.warehouse)), net_amt_precision)
stock_adjustment_amt = warehouse_debit_amount - stock_amount
gl_entries.append(
@@ -999,10 +1001,10 @@
self.delete_auto_created_batches()
self.make_gl_entries_on_cancel()
-
+
if self.update_stock == 1:
self.repost_future_sle_and_gle()
-
+
self.update_project()
frappe.db.set(self, 'status', 'Cancelled')
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 f6d76e5..1f7853d 100644
--- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
+++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
@@ -1,4 +1,5 @@
{
+ "actions": [],
"autoname": "hash",
"creation": "2013-05-22 12:43:10",
"doctype": "DocType",
@@ -87,6 +88,7 @@
"po_detail",
"purchase_receipt",
"pr_detail",
+ "sales_invoice_item",
"item_weight_details",
"weight_per_unit",
"total_weight",
@@ -553,8 +555,8 @@
"fieldtype": "Link",
"hidden": 1,
"label": "Brand",
- "print_hide": 1,
- "options": "Brand"
+ "options": "Brand",
+ "print_hide": 1
},
{
"fetch_from": "item_code.item_group",
@@ -562,9 +564,9 @@
"fieldname": "item_group",
"fieldtype": "Link",
"label": "Item Group",
+ "options": "Item Group",
"print_hide": 1,
- "read_only": 1,
- "options": "Item Group"
+ "read_only": 1
},
{
"description": "Tax detail table fetched from item master as a string and stored in this field.\nUsed for Taxes and Charges",
@@ -759,10 +761,11 @@
"read_only": 1
},
{
+ "depends_on": "eval:parent.is_internal_supplier && parent.update_stock",
"fieldname": "from_warehouse",
"fieldtype": "Link",
"ignore_user_permissions": 1,
- "label": "Supplier Warehouse",
+ "label": "From Warehouse",
"options": "Warehouse"
},
{
@@ -779,11 +782,20 @@
"no_copy": 1,
"print_hide": 1,
"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,
- "modified": "2020-08-20 11:48:01.398356",
+ "links": [],
+ "modified": "2020-12-26 17:20:36.415791",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice Item",
@@ -791,4 +803,4 @@
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC"
-}
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index 72199a9..f2a62cd 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -130,16 +130,15 @@
this.set_default_print_format();
if (doc.docstatus == 1 && !doc.inter_company_invoice_reference) {
- frappe.model.with_doc("Customer", me.frm.doc.customer, function() {
- var customer = frappe.model.get_doc("Customer", me.frm.doc.customer);
- var internal = customer.is_internal_customer;
- var disabled = customer.disabled;
- if (internal == 1 && disabled == 0) {
- me.frm.add_custom_button("Inter Company Invoice", function() {
- me.make_inter_company_invoice();
- }, __('Create'));
- }
- });
+ let internal = me.frm.doc.is_internal_customer;
+ if (internal) {
+ let button_label = (me.frm.doc.company === me.frm.doc.represents_company) ? "Internal Purchase Invoice" :
+ "Inter Company Purchase Invoice";
+
+ me.frm.add_custom_button(button_label, function() {
+ me.make_inter_company_invoice();
+ }, __('Create'));
+ }
}
},
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
index 6799fb9..447cee4 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
@@ -60,6 +60,8 @@
"ignore_pricing_rule",
"sec_warehouse",
"set_warehouse",
+ "column_break_55",
+ "set_target_warehouse",
"items_section",
"update_stock",
"scan_barcode",
@@ -1969,13 +1971,24 @@
"label": "Represents Company",
"options": "Company",
"read_only": 1
+ },
+ {
+ "fieldname": "column_break_55",
+ "fieldtype": "Column Break"
+ },
+ {
+ "depends_on": "eval: doc.is_internal_customer && doc.update_stock",
+ "fieldname": "set_target_warehouse",
+ "fieldtype": "Link",
+ "label": "Set Target Warehouse",
+ "options": "Warehouse"
}
],
"icon": "fa fa-file-text",
"idx": 181,
"is_submittable": 1,
"links": [],
- "modified": "2020-12-11 12:48:31.769958",
+ "modified": "2020-12-25 22:57:32.555067",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 566734e..7116a6a 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -6,7 +6,7 @@
import frappe.defaults
from frappe.utils import cint, flt, getdate, add_days, cstr, nowdate, get_link_to_form, formatdate
from frappe import _, msgprint, throw
-from erpnext.accounts.party import get_party_account, get_due_date
+from erpnext.accounts.party import get_party_account, get_due_date, get_party_details
from frappe.model.mapper import get_mapped_doc
from erpnext.controllers.selling_controller import SellingController
from erpnext.accounts.utils import get_account_currency
@@ -21,6 +21,8 @@
from erpnext.accounts.doctype.loyalty_program.loyalty_program import \
get_loyalty_program_details_with_points, get_loyalty_details, validate_loyalty_points
from erpnext.accounts.deferred_revenue import validate_service_stop_date
+from frappe.model.utils import get_fetch_values
+from frappe.contacts.doctype.address.address import get_address_display
from erpnext.healthcare.utils import manage_invoice_submit_cancel
@@ -1534,7 +1536,7 @@
details = get_inter_company_details(doc, doctype)
price_list = doc.selling_price_list if doctype in ["Sales Invoice", "Sales Order", "Delivery Note"] else doc.buying_price_list
valid_price_list = frappe.db.get_value("Price List", {"name": price_list, "buying": 1, "selling": 1})
- if not valid_price_list:
+ if not valid_price_list and not doc.is_internal_transfer():
frappe.throw(_("Selected Price List should have buying and selling fields checked."))
party = details.get("party")
@@ -1557,6 +1559,7 @@
if doctype in ["Sales Invoice", "Sales Order"]:
source_doc = frappe.get_doc(doctype, source_name)
target_doctype = "Purchase Invoice" if doctype == "Sales Invoice" else "Purchase Order"
+ target_detail_field = "sales_invoice_item" if doctype == "Sales Invoice" else "sales_order_item"
source_document_warehouse_field = 'target_warehouse'
target_document_warehouse_field = 'from_warehouse'
else:
@@ -1570,6 +1573,7 @@
def set_missing_values(source, target):
target.run_method("set_missing_values")
+ set_purchase_references(target)
def update_details(source_doc, target_doc, source_parent):
target_doc.inter_company_invoice_reference = source_doc.name
@@ -1577,19 +1581,38 @@
currency = frappe.db.get_value('Supplier', details.get('party'), 'default_currency')
target_doc.company = details.get("company")
target_doc.supplier = details.get("party")
+ target_doc.is_internal_supplier = 1
+ target_doc.ignore_pricing_rule = 1
target_doc.buying_price_list = source_doc.selling_price_list
+ # Invert Addresses
+ update_address(target_doc, 'supplier_address', 'address_display', source_doc.company_address)
+ update_address(target_doc, 'shipping_address', 'shipping_address_display', source_doc.customer_address)
+
if currency:
target_doc.currency = currency
+
+ update_taxes(target_doc, party=target_doc.supplier, party_type='Supplier', company=target_doc.company,
+ doctype=target_doc.doctype, party_address=target_doc.supplier_address,
+ company_address=target_doc.shipping_address)
+
else:
currency = frappe.db.get_value('Customer', details.get('party'), 'default_currency')
target_doc.company = details.get("company")
target_doc.customer = details.get("party")
target_doc.selling_price_list = source_doc.buying_price_list
+ update_address(target_doc, 'company_address', 'company_address_display', source_doc.supplier_address)
+ update_address(target_doc, 'shipping_address_name', 'shipping_address', source_doc.shipping_address)
+ update_address(target_doc, 'customer_address', 'address_display', source_doc.shipping_address)
+
if currency:
target_doc.currency = currency
+ update_taxes(target_doc, party=target_doc.customer, party_type='Customer', company=target_doc.company,
+ doctype=target_doc.doctype, party_address=target_doc.customer_address,
+ company_address=target_doc.company_address, shipping_address_name=target_doc.shipping_address_name)
+
item_field_map = {
"doctype": target_doctype + " Item",
"field_no_map": [
@@ -1597,25 +1620,33 @@
"expense_account",
"cost_center",
"warehouse"
- ]
+ ],
+ "field_map": {
+ 'rate': 'rate',
+ }
}
- if source_doc.get('update_stock'):
- item_field_map.update({
- 'field_map': {
- source_document_warehouse_field: target_document_warehouse_field,
- 'batch_no': 'batch_no',
- 'serial_no': 'serial_no'
- }
+ if doctype in ["Sales Invoice", "Sales Order"]:
+ item_field_map["field_map"].update({
+ "name": target_detail_field,
})
+ if source_doc.get('update_stock'):
+ item_field_map["field_map"].update({
+ source_document_warehouse_field: target_document_warehouse_field,
+ 'batch_no': 'batch_no',
+ 'serial_no': 'serial_no'
+ })
doclist = get_mapped_doc(doctype, source_name, {
doctype: {
"doctype": target_doctype,
"postprocess": update_details,
+ "set_target_warehouse": "set_from_warehouse",
"field_no_map": [
- "taxes_and_charges"
+ "taxes_and_charges",
+ "set_warehouse",
+ "shipping_address"
]
},
doctype +" Item": item_field_map
@@ -1624,6 +1655,110 @@
return doclist
+def set_purchase_references(doc):
+ # add internal PO or PR links if any
+ if doc.is_internal_transfer():
+ if doc.doctype == 'Purchase Receipt':
+ so_item_map = get_delivery_note_details(doc.inter_company_invoice_reference)
+
+ if so_item_map:
+ pd_item_map, parent_child_map, warehouse_map = \
+ get_pd_details('Purchase Order Item', so_item_map, 'sales_order_item')
+
+ update_pr_items(doc, so_item_map, pd_item_map, parent_child_map, warehouse_map)
+
+ elif doc.doctype == 'Purchase Invoice':
+ dn_item_map, so_item_map = get_sales_invoice_details(doc.inter_company_invoice_reference)
+ # First check for Purchase receipt
+ if list(dn_item_map.values()):
+ pd_item_map, parent_child_map, warehouse_map = \
+ get_pd_details('Purchase Receipt Item', dn_item_map, 'delivery_note_item')
+
+ update_pi_items(doc, 'pr_detail', 'purchase_receipt',
+ dn_item_map, pd_item_map, parent_child_map, warehouse_map)
+
+ if list(so_item_map.values()):
+ pd_item_map, parent_child_map, warehouse_map = \
+ get_pd_details('Purchase Order Item', so_item_map, 'sales_order_item')
+
+ update_pi_items(doc, 'po_detail', 'purchase_order',
+ so_item_map, pd_item_map, parent_child_map, warehouse_map)
+
+def update_pi_items(doc, detail_field, parent_field, sales_item_map,
+ purchase_item_map, parent_child_map, warehouse_map):
+ for item in doc.get('items'):
+ item.set(detail_field, purchase_item_map.get(sales_item_map.get(item.sales_invoice_item)))
+ item.set(parent_field, parent_child_map.get(sales_item_map.get(item.sales_invoice_item)))
+ if doc.update_stock:
+ item.warehouse = warehouse_map.get(sales_item_map.get(item.sales_invoice_item))
+
+def update_pr_items(doc, sales_item_map, purchase_item_map, parent_child_map, warehouse_map):
+ for item in doc.get('items'):
+ item.purchase_order_item = purchase_item_map.get(sales_item_map.get(item.delivery_note_item))
+ item.warehouse = warehouse_map.get(sales_item_map.get(item.delivery_note_item))
+ item.purchase_order = parent_child_map.get(sales_item_map.get(item.delivery_note_item))
+
+def get_delivery_note_details(internal_reference):
+ so_item_map = {}
+
+ si_item_details = frappe.get_all('Delivery Note Item', fields=['name', 'so_detail'],
+ filters={'parent': internal_reference})
+
+ for d in si_item_details:
+ so_item_map.setdefault(d.name, d.so_detail)
+
+ return so_item_map
+
+def get_sales_invoice_details(internal_reference):
+ dn_item_map = {}
+ so_item_map = {}
+
+ si_item_details = frappe.get_all('Sales Invoice Item', fields=['name', 'so_detail',
+ 'dn_detail'], filters={'parent': internal_reference})
+
+ for d in si_item_details:
+ if d.dn_detail:
+ dn_item_map.setdefault(d.name, d.dn_detail)
+ if d.so_detail:
+ so_item_map.setdefault(d.name, d.so_detail)
+
+ return dn_item_map, so_item_map
+
+def get_pd_details(doctype, sd_detail_map, sd_detail_field):
+ pd_item_map = {}
+ accepted_warehouse_map = {}
+ parent_child_map = {}
+
+ pd_item_details = frappe.get_all(doctype,
+ fields=[sd_detail_field, 'name', 'warehouse', 'parent'], filters={sd_detail_field: ('in', list(sd_detail_map.values()))})
+
+ for d in pd_item_details:
+ pd_item_map.setdefault(d.get(sd_detail_field), d.name)
+ parent_child_map.setdefault(d.get(sd_detail_field), d.parent)
+ accepted_warehouse_map.setdefault(d.get(sd_detail_field), d.warehouse)
+
+ return pd_item_map, parent_child_map, accepted_warehouse_map
+
+def update_taxes(doc, party=None, party_type=None, company=None, doctype=None, party_address=None,
+ company_address=None, shipping_address_name=None, master_doctype=None):
+ # Update Party Details
+ party_details = get_party_details(party=party, party_type=party_type, company=company,
+ doctype=doctype, party_address=party_address, company_address=company_address,
+ shipping_address=shipping_address_name)
+
+ # Update taxes and charges if any
+ doc.taxes_and_charges = party_details.get('taxes_and_charges')
+ doc.set('taxes', party_details.get('taxes'))
+
+def update_address(doc, address_field, address_display_field, address_name):
+ doc.set(address_field, address_name)
+ fetch_values = get_fetch_values(doc.doctype, address_field, address_name)
+
+ for key, value in fetch_values.items():
+ doc.set(key, value)
+
+ doc.set(address_display_field, get_address_display(doc.get(address_field)))
+
@frappe.whitelist()
def get_loyalty_programs(customer):
''' sets applicable loyalty program to the customer or returns a list of applicable programs '''
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index 3a6dbeb..e94e2cd 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.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):
@@ -688,7 +689,7 @@
self.assertTrue(gle)
def test_pos_gl_entry_with_perpetual_inventory(self):
- make_pos_profile(company="_Test Company with perpetual inventory", income_account = "Sales - TCP1",
+ make_pos_profile(company="_Test Company with perpetual inventory", income_account = "Sales - TCP1",
expense_account = "Cost of Goods Sold - TCP1", warehouse="Stores - TCP1", cost_center = "Main - TCP1", write_off_account="_Test Write Off - TCP1")
pr = make_purchase_receipt(company= "_Test Company with perpetual inventory", item_code= "_Test FG Item",warehouse= "Stores - TCP1",cost_center= "Main - TCP1")
@@ -745,7 +746,7 @@
self.assertEqual(pos_return.get('payments')[0].amount, -1000)
def test_pos_change_amount(self):
- make_pos_profile(company="_Test Company with perpetual inventory", income_account = "Sales - TCP1",
+ make_pos_profile(company="_Test Company with perpetual inventory", income_account = "Sales - TCP1",
expense_account = "Cost of Goods Sold - TCP1", warehouse="Stores - TCP1", cost_center = "Main - TCP1", write_off_account="_Test Write Off - TCP1")
pr = make_purchase_receipt(company= "_Test Company with perpetual inventory",
@@ -1770,59 +1771,82 @@
self.assertEqual(target_doc.company, "_Test Company 1")
self.assertEqual(target_doc.supplier, "_Test Internal Supplier")
- # def test_internal_transfer_gl_entry(self):
- # ## Create internal transfer account
- # account = create_account(account_name="Unrealized Profit",
- # parent_account="Current Liabilities - TCP1", company="_Test Company with perpetual inventory")
+ def test_internal_transfer_gl_entry(self):
+ ## Create internal transfer account
+ account = create_account(account_name="Unrealized Profit",
+ parent_account="Current Liabilities - TCP1", company="_Test Company with perpetual inventory")
- # frappe.db.set_value('Company', '_Test Company with perpetual inventory',
- # 'unrealized_profit_loss_account', account)
+ frappe.db.set_value('Company', '_Test Company with perpetual inventory',
+ 'unrealized_profit_loss_account', account)
- # customer = create_internal_customer("_Test Internal Customer 2", "_Test Company with perpetual inventory",
- # "_Test Company with perpetual inventory")
+ customer = create_internal_customer("_Test Internal Customer 2", "_Test Company with perpetual inventory",
+ "_Test Company with perpetual inventory")
- # create_internal_supplier("_Test Internal Supplier 2", "_Test Company with perpetual inventory",
- # "_Test Company with perpetual inventory")
+ create_internal_supplier("_Test Internal Supplier 2", "_Test Company with perpetual inventory",
+ "_Test Company with perpetual inventory")
- # si = create_sales_invoice(
- # company = "_Test Company with perpetual inventory",
- # customer = customer,
- # debit_to = "Debtors - TCP1",
- # warehouse = "Stores - TCP1",
- # income_account = "Sales - TCP1",
- # expense_account = "Cost of Goods Sold - TCP1",
- # cost_center = "Main - TCP1",
- # currency = "INR",
- # do_not_save = 1
- # )
+ si = create_sales_invoice(
+ company = "_Test Company with perpetual inventory",
+ customer = customer,
+ debit_to = "Debtors - TCP1",
+ warehouse = "Stores - TCP1",
+ income_account = "Sales - TCP1",
+ expense_account = "Cost of Goods Sold - TCP1",
+ cost_center = "Main - TCP1",
+ currency = "INR",
+ do_not_save = 1
+ )
- # si.selling_price_list = "_Test Price List Rest of the World"
- # si.update_stock = 1
- # si.items[0].target_warehouse = 'Work In Progress - TCP1'
- # add_taxes(si)
- # si.save()
- # si.submit()
+ si.selling_price_list = "_Test Price List Rest of the World"
+ si.update_stock = 1
+ si.items[0].target_warehouse = 'Work In Progress - TCP1'
+ add_taxes(si)
+ si.save()
- # target_doc = make_inter_company_transaction("Sales Invoice", si.name)
- # target_doc.company = '_Test Company with perpetual inventory'
- # target_doc.items[0].warehouse = 'Finished Goods - TCP1'
- # add_taxes(target_doc)
- # target_doc.save()
- # target_doc.submit()
+ 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)
- # si_gl_entries = [
- # ["_Test Account Excise Duty - TCP1", 0.0, 12.0, nowdate()],
- # ["Unrealized Profit - TCP1", 12.0, 0.0, nowdate()]
- # ]
+ rate = flt(rate, 2)
- # check_gl_entries(self, si.name, si_gl_entries, add_days(nowdate(), -1))
+ si.submit()
- # pi_gl_entries = [
- # ["_Test Account Excise Duty - TCP1", 12.0 , 0.0, nowdate()],
- # ["Unrealized Profit - TCP1", 0.0, 12.0, nowdate()]
- # ]
+ target_doc = make_inter_company_transaction("Sales Invoice", si.name)
+ target_doc.company = '_Test Company with perpetual inventory'
+ target_doc.items[0].warehouse = 'Finished Goods - TCP1'
+ add_taxes(target_doc)
+ target_doc.save()
+ target_doc.submit()
- # check_gl_entries(self, target_doc.name, pi_gl_entries, add_days(nowdate(), -1))
+ tax_amount = flt(rate * (12/100), 2)
+ si_gl_entries = [
+ ["_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", 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):
si = make_sales_invoice_for_ewaybill()
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 3695075..7a98aff 100644
--- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
+++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json
@@ -565,11 +565,12 @@
"print_hide": 1
},
{
+ "depends_on": "eval: parent.is_internal_customer && parent.update_stock",
"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
@@ -815,7 +816,7 @@
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2020-09-23 19:59:04.879322",
+ "modified": "2020-12-26 17:25:04.090630",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice Item",
diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
index eeb5140..cb4d9b4 100644
--- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
+++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py
@@ -49,7 +49,8 @@
elif d.po_detail:
purchase_receipt = ", ".join(po_pr_map.get(d.po_detail, []))
- expense_account = d.expense_account or aii_account_map.get(d.company)
+ expense_account = d.unrealized_profit_loss_account or d.expense_account \
+ or aii_account_map.get(d.company)
row = {
'item_code': d.item_code,
@@ -315,6 +316,7 @@
`tabPurchase Invoice Item`.`name`, `tabPurchase Invoice Item`.`parent`,
`tabPurchase Invoice`.posting_date, `tabPurchase Invoice`.credit_to, `tabPurchase Invoice`.company,
`tabPurchase Invoice`.supplier, `tabPurchase Invoice`.remarks, `tabPurchase Invoice`.base_net_total,
+ `tabPurchase Invoice`.unrealized_profit_loss_account,
`tabPurchase Invoice Item`.`item_code`, `tabPurchase Invoice Item`.description,
`tabPurchase Invoice Item`.`item_name`, `tabPurchase Invoice Item`.`item_group`,
`tabPurchase Invoice Item`.`project`, `tabPurchase Invoice Item`.`purchase_order`,
diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
index f54ceb0..998003a 100644
--- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
+++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
@@ -76,7 +76,7 @@
'company': d.company,
'sales_order': d.sales_order,
'delivery_note': d.delivery_note,
- 'income_account': d.income_account,
+ 'income_account': d.unrealized_profit_loss_account or d.income_account,
'cost_center': d.cost_center,
'stock_qty': d.stock_qty,
'stock_uom': d.stock_uom
@@ -379,6 +379,7 @@
select
`tabSales Invoice Item`.name, `tabSales Invoice Item`.parent,
`tabSales Invoice`.posting_date, `tabSales Invoice`.debit_to,
+ `tabSales Invoice`.unrealized_profit_loss_account,
`tabSales Invoice`.project, `tabSales Invoice`.customer, `tabSales Invoice`.remarks,
`tabSales Invoice`.territory, `tabSales Invoice`.company, `tabSales Invoice`.base_net_total,
`tabSales Invoice Item`.item_code, `tabSales Invoice Item`.description,
diff --git a/erpnext/accounts/report/purchase_register/purchase_register.py b/erpnext/accounts/report/purchase_register/purchase_register.py
index 9399e70..8ac749d 100644
--- a/erpnext/accounts/report/purchase_register/purchase_register.py
+++ b/erpnext/accounts/report/purchase_register/purchase_register.py
@@ -14,13 +14,15 @@
if not filters: filters = {}
invoice_list = get_invoices(filters, additional_query_columns)
- columns, expense_accounts, tax_accounts = get_columns(invoice_list, additional_table_columns)
+ columns, expense_accounts, tax_accounts, unrealized_profit_loss_accounts \
+ = get_columns(invoice_list, additional_table_columns)
if not invoice_list:
msgprint(_("No record found"))
return columns, invoice_list
invoice_expense_map = get_invoice_expense_map(invoice_list)
+ internal_invoice_map = get_internal_invoice_map(invoice_list)
invoice_expense_map, invoice_tax_map = get_invoice_tax_map(invoice_list,
invoice_expense_map, expense_accounts)
invoice_po_pr_map = get_invoice_po_pr_map(invoice_list)
@@ -52,10 +54,17 @@
# map expense values
base_net_total = 0
for expense_acc in expense_accounts:
- expense_amount = flt(invoice_expense_map.get(inv.name, {}).get(expense_acc))
+ if inv.is_internal_supplier and inv.company == inv.represents_company:
+ expense_amount = 0
+ else:
+ expense_amount = flt(invoice_expense_map.get(inv.name, {}).get(expense_acc))
base_net_total += expense_amount
row.append(expense_amount)
+ # Add amount in unrealized account
+ for account in unrealized_profit_loss_accounts:
+ row.append(flt(internal_invoice_map.get((inv.name, account))))
+
# net total
row.append(base_net_total or inv.base_net_total)
@@ -96,7 +105,8 @@
"width": 80
}
]
- expense_accounts = tax_accounts = expense_columns = tax_columns = []
+ expense_accounts = tax_accounts = expense_columns = tax_columns = unrealized_profit_loss_accounts = \
+ unrealized_profit_loss_account_columns = []
if invoice_list:
expense_accounts = frappe.db.sql_list("""select distinct expense_account
@@ -112,17 +122,25 @@
and parent in (%s) order by account_head""" %
', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]))
+ unrealized_profit_loss_accounts = frappe.db.sql_list("""SELECT distinct unrealized_profit_loss_account
+ from `tabPurchase Invoice` where docstatus = 1 and name in (%s)
+ and ifnull(unrealized_profit_loss_account, '') != ''
+ order by unrealized_profit_loss_account""" %
+ ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]))
expense_columns = [(account + ":Currency/currency:120") for account in expense_accounts]
+ unrealized_profit_loss_account_columns = [(account + ":Currency/currency:120") for account in unrealized_profit_loss_accounts]
+
for account in tax_accounts:
if account not in expense_accounts:
tax_columns.append(account + ":Currency/currency:120")
- columns = columns + expense_columns + [_("Net Total") + ":Currency/currency:120"] + tax_columns + \
+ columns = columns + expense_columns + unrealized_profit_loss_account_columns + \
+ [_("Net Total") + ":Currency/currency:120"] + tax_columns + \
[_("Total Tax") + ":Currency/currency:120", _("Grand Total") + ":Currency/currency:120",
_("Rounded Total") + ":Currency/currency:120", _("Outstanding Amount") + ":Currency/currency:120"]
- return columns, expense_accounts, tax_accounts
+ return columns, expense_accounts, tax_accounts, unrealized_profit_loss_accounts
def get_conditions(filters):
conditions = ""
@@ -199,6 +217,19 @@
return invoice_expense_map
+def get_internal_invoice_map(invoice_list):
+ unrealized_amount_details = frappe.db.sql("""SELECT name, unrealized_profit_loss_account,
+ base_net_total as amount from `tabPurchase Invoice` where name in (%s)
+ and is_internal_supplier = 1 and company = represents_company""" %
+ ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1)
+
+ internal_invoice_map = {}
+ for d in unrealized_amount_details:
+ if d.unrealized_profit_loss_account:
+ internal_invoice_map.setdefault((d.name, d.unrealized_profit_loss_account), d.amount)
+
+ return internal_invoice_map
+
def get_invoice_tax_map(invoice_list, invoice_expense_map, expense_accounts):
tax_details = frappe.db.sql("""
select parent, account_head, case add_deduct_tax when "Add" then sum(base_tax_amount_after_discount_amount)
diff --git a/erpnext/accounts/report/sales_register/sales_register.py b/erpnext/accounts/report/sales_register/sales_register.py
index b6e61b1..cb2c98b 100644
--- a/erpnext/accounts/report/sales_register/sales_register.py
+++ b/erpnext/accounts/report/sales_register/sales_register.py
@@ -15,13 +15,14 @@
if not filters: filters = frappe._dict({})
invoice_list = get_invoices(filters, additional_query_columns)
- columns, income_accounts, tax_accounts = get_columns(invoice_list, additional_table_columns)
+ columns, income_accounts, tax_accounts, unrealized_profit_loss_accounts = get_columns(invoice_list, additional_table_columns)
if not invoice_list:
msgprint(_("No record found"))
return columns, invoice_list
invoice_income_map = get_invoice_income_map(invoice_list)
+ internal_invoice_map = get_internal_invoice_map(invoice_list)
invoice_income_map, invoice_tax_map = get_invoice_tax_map(invoice_list,
invoice_income_map, income_accounts)
#Cost Center & Warehouse Map
@@ -70,12 +71,22 @@
# map income values
base_net_total = 0
for income_acc in income_accounts:
- income_amount = flt(invoice_income_map.get(inv.name, {}).get(income_acc))
+ if inv.is_internal_customer and inv.company == inv.represents_company:
+ income_amount = 0
+ else:
+ income_amount = flt(invoice_income_map.get(inv.name, {}).get(income_acc))
+
base_net_total += income_amount
row.update({
frappe.scrub(income_acc): income_amount
})
+ # Add amount in unrealized account
+ for account in unrealized_profit_loss_accounts:
+ row.update({
+ frappe.scrub(account): flt(internal_invoice_map.get((inv.name, account)))
+ })
+
# net total
row.update({'net_total': base_net_total or inv.base_net_total})
@@ -230,6 +241,8 @@
tax_accounts = []
income_columns = []
tax_columns = []
+ unrealized_profit_loss_accounts = []
+ unrealized_profit_loss_account_columns = []
if invoice_list:
income_accounts = frappe.db.sql_list("""select distinct income_account
@@ -243,12 +256,18 @@
and parent in (%s) order by account_head""" %
', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]))
+ unrealized_profit_loss_accounts = frappe.db.sql_list("""SELECT distinct unrealized_profit_loss_account
+ from `tabSales Invoice` where docstatus = 1 and name in (%s)
+ and ifnull(unrealized_profit_loss_account, '') != ''
+ order by unrealized_profit_loss_account""" %
+ ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]))
+
for account in income_accounts:
income_columns.append({
"label": account,
"fieldname": frappe.scrub(account),
"fieldtype": "Currency",
- "options": 'currency',
+ "options": "currency",
"width": 120
})
@@ -258,15 +277,24 @@
"label": account,
"fieldname": frappe.scrub(account),
"fieldtype": "Currency",
- "options": 'currency',
+ "options": "currency",
"width": 120
})
+ for account in unrealized_profit_loss_accounts:
+ unrealized_profit_loss_account_columns.append({
+ "label": account,
+ "fieldname": frappe.scrub(account),
+ "fieldtype": "Currency",
+ "options": "currency",
+ "width": 120
+ })
+
net_total_column = [{
"label": _("Net Total"),
"fieldname": "net_total",
"fieldtype": "Currency",
- "options": 'currency',
+ "options": "currency",
"width": 120
}]
@@ -301,9 +329,10 @@
}
]
- columns = columns + income_columns + net_total_column + tax_columns + total_columns
+ columns = columns + income_columns + unrealized_profit_loss_account_columns + \
+ net_total_column + tax_columns + total_columns
- return columns, income_accounts, tax_accounts
+ return columns, income_accounts, tax_accounts, unrealized_profit_loss_accounts
def get_conditions(filters):
conditions = ""
@@ -368,7 +397,8 @@
return frappe.db.sql("""
select name, posting_date, debit_to, project, customer,
customer_name, owner, remarks, territory, tax_id, customer_group,
- base_net_total, base_grand_total, base_rounded_total, outstanding_amount {0}
+ base_net_total, base_grand_total, base_rounded_total, outstanding_amount,
+ is_internal_customer, represents_company, company {0}
from `tabSales Invoice`
where docstatus = 1 %s order by posting_date desc, name desc""".format(additional_query_columns or '') %
conditions, filters, as_dict=1)
@@ -385,6 +415,19 @@
return invoice_income_map
+def get_internal_invoice_map(invoice_list):
+ unrealized_amount_details = frappe.db.sql("""SELECT name, unrealized_profit_loss_account,
+ base_net_total as amount from `tabSales Invoice` where name in (%s)
+ and is_internal_customer = 1 and company = represents_company""" %
+ ', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1)
+
+ internal_invoice_map = {}
+ for d in unrealized_amount_details:
+ if d.unrealized_profit_loss_account:
+ internal_invoice_map.setdefault((d.name, d.unrealized_profit_loss_account), d.amount)
+
+ return internal_invoice_map
+
def get_invoice_tax_map(invoice_list, invoice_income_map, income_accounts):
tax_details = frappe.db.sql("""select parent, account_head,
sum(base_tax_amount_after_discount_amount) as tax_amount
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js
index 83efa66..dd0f065 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.js
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.js
@@ -164,16 +164,16 @@
if (doc.docstatus === 1 && !doc.inter_company_order_reference) {
let me = this;
- frappe.model.with_doc("Supplier", me.frm.doc.supplier, () => {
- let supplier = frappe.model.get_doc("Supplier", me.frm.doc.supplier);
- let internal = supplier.is_internal_supplier;
- let disabled = supplier.disabled;
- if (internal === 1 && disabled === 0) {
- me.frm.add_custom_button("Inter Company Order", function() {
- me.make_inter_company_order(me.frm);
- }, __('Create'));
- }
- });
+ let internal = me.frm.doc.is_internal_supplier;
+ if (internal) {
+ let button_label = (me.frm.doc.company === me.frm.doc.represents_company) ? "Internal Sales Order" :
+ "Inter Company Sales Order";
+
+ me.frm.add_custom_button(button_label, function() {
+ me.make_inter_company_order(me.frm);
+ }, __('Create'));
+ }
+
}
}
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json
index 75da71c..ee2beea 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.json
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.json
@@ -134,6 +134,8 @@
"ref_sq",
"column_break_74",
"party_account_currency",
+ "is_internal_supplier",
+ "represents_company",
"inter_company_order_reference"
],
"fields": [
@@ -1101,13 +1103,28 @@
{
"fieldname": "items_col_break",
"fieldtype": "Column Break"
+ },
+ {
+ "default": "0",
+ "fetch_from": "supplier.is_internal_supplier",
+ "fieldname": "is_internal_supplier",
+ "fieldtype": "Check",
+ "label": "Is Internal Supplier"
+ },
+ {
+ "fetch_from": "supplier.represents_company",
+ "fieldname": "represents_company",
+ "fieldtype": "Link",
+ "label": "Represents Company",
+ "options": "Company",
+ "read_only": 1
}
],
"icon": "fa fa-file-text",
"idx": 105,
"is_submittable": 1,
"links": [],
- "modified": "2020-12-03 16:46:44.229351",
+ "modified": "2021-01-20 22:07:23.487138",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order",
diff --git a/erpnext/buying/doctype/supplier/supplier.py b/erpnext/buying/doctype/supplier/supplier.py
index 0ee9d18..edeb135 100644
--- a/erpnext/buying/doctype/supplier/supplier.py
+++ b/erpnext/buying/doctype/supplier/supplier.py
@@ -52,7 +52,10 @@
self.validate_internal_supplier()
def validate_internal_supplier(self):
- if self.is_internal_supplier and frappe.db.get_value("Supplier", {"represents_company": self.represents_company}, "name"):
+ internal_supplier = frappe.db.get_value("Supplier",
+ {"is_internal_supplier": 1, "represents_company": self.represents_company, "name": ("!=", self.name)}, "name")
+
+ if internal_supplier:
frappe.throw(_("Internal Supplier for company {0} already exists").format(
frappe.bold(self.represents_company)))
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index b9bfbaf..eaa8080 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -75,6 +75,9 @@
self.ensure_supplier_is_not_blocked()
self.validate_date_with_fiscal_year()
+ self.validate_inter_company_reference()
+
+ self.set_incoming_rate()
if self.meta.get_field("currency"):
self.calculate_taxes_and_totals()
@@ -110,12 +113,12 @@
self.set_inter_company_account()
validate_regional(self)
-
+
validate_einvoice_fields(self)
if self.doctype != 'Material Request':
apply_pricing_rule_on_transaction(self)
-
+
def before_cancel(self):
validate_einvoice_fields(self)
@@ -212,6 +215,17 @@
validate_fiscal_year(self.get(date_field), self.fiscal_year, self.company,
self.meta.get_label(date_field), self)
+ def validate_inter_company_reference(self):
+ if self.doctype not in ('Purchase Invoice', 'Purchase Receipt', 'Purchase Order'):
+ return
+
+ if self.is_internal_transfer():
+ if not (self.get('inter_company_reference') or self.get('inter_company_invoice_reference')
+ or self.get('inter_company_order_reference')):
+ msg = _("Internal Sale or Delivery Reference missing. ")
+ msg += _("Please create purchase from internal sale or delivery document itself")
+ frappe.throw(msg, title=_("Internal Sales Reference Missing"))
+
def validate_due_date(self):
if self.get('is_pos'): return
@@ -968,9 +982,9 @@
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', 'Sales Order'):
internal_party_field = 'is_internal_customer'
- else:
+ elif self.doctype in ('Purchase Invoice', 'Purchase Receipt', 'Purchase Order'):
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 4dee375..ab1f027 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -44,7 +44,6 @@
self.validate_items()
self.set_qty_as_per_stock_uom()
self.validate_stock_or_nonstock_items()
- self.update_tax_category_for_internal_transfer()
self.validate_warehouse()
self.validate_from_warehouse()
self.set_supplier_address()
@@ -100,11 +99,6 @@
msg = _('Tax Category has been changed to "Total" because all the Items are non-stock items')
self.update_tax_category(msg)
- def update_tax_category_for_internal_transfer(self):
- if self.doctype == 'Purchase Invoice' and self.is_internal_transfer():
- msg = _('Tax Category has been changed to "Total" as its an internal purchase.')
- self.update_tax_category(msg)
-
def update_tax_category(self, msg):
tax_for_valuation = [d for d in self.get("taxes")
if d.category in ["Valuation", "Valuation and Total"]]
@@ -224,6 +218,48 @@
else:
item.valuation_rate = 0.0
+ def set_incoming_rate(self):
+ if self.doctype not in ("Purchase Receipt", "Purchase Invoice", "Purchase Order"):
+ return
+
+ ref_doctype_map = {
+ "Purchase Order": "Sales Order Item",
+ "Purchase Receipt": "Delivery Note Item",
+ "Purchase Invoice": "Sales Invoice Item",
+ }
+
+ ref_doctype = ref_doctype_map.get(self.doctype)
+ items = self.get("items")
+ for d in items:
+ if not cint(self.get("is_return")):
+ # Get outgoing rate based on original item cost based on valuation method
+
+ if not d.get(frappe.scrub(ref_doctype)):
+ outgoing_rate = get_incoming_rate({
+ "item_code": d.item_code,
+ "warehouse": d.get('from_warehouse'),
+ "posting_date": self.get('posting_date') or self.get('transation_date'),
+ "posting_time": self.get('posting_time'),
+ "qty": -1 * flt(d.get('stock_qty')),
+ "serial_no": d.get('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, d.precision('rate'))
+ else:
+ rate = frappe.db.get_value(ref_doctype, d.get(frappe.scrub(ref_doctype)), 'rate')
+
+ if self.is_internal_transfer():
+ if rate != d.rate:
+ d.rate = rate
+ d.discount_percentage = 0
+ d.discount_amount = 0
+ frappe.msgprint(_("Row {0}: Item rate has been updated as per valuation rate since its an internal stock transfer")
+ .format(d.idx), alert=1)
+
def get_supplied_items_cost(self, item_row_id, reset_outgoing_rate=True):
supplied_items_cost = 0.0
for d in self.get("supplied_items"):
@@ -243,7 +279,7 @@
d.amount = flt(flt(d.consumed_qty) * flt(d.rate), d.precision("amount"))
supplied_items_cost += flt(d.amount)
-
+
return supplied_items_cost
def validate_for_subcontracting(self):
@@ -559,6 +595,8 @@
from_warehouse_sle = self.get_sl_entries(d, {
"actual_qty": -1 * pr_qty,
"warehouse": d.from_warehouse,
+ "outgoing_rate": d.rate,
+ "recalculate_rate": 1,
"dependant_sle_voucher_detail_no": d.name
})
diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py
index 812021f..e085048 100644
--- a/erpnext/controllers/selling_controller.py
+++ b/erpnext/controllers/selling_controller.py
@@ -3,7 +3,7 @@
from __future__ import unicode_literals
import frappe
-from frappe.utils import cint, flt, cstr, comma_or, get_link_to_form
+from frappe.utils import cint, flt, cstr, get_link_to_form, nowtime
from frappe import _, throw
from erpnext.stock.get_item_details import get_bin_details
from erpnext.stock.utils import get_incoming_rate
@@ -49,7 +49,6 @@
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):
@@ -191,7 +190,7 @@
for it in self.get("items"):
if not it.item_code:
continue
-
+
last_purchase_rate, is_stock_item = frappe.get_cached_value("Item", it.item_code, ["last_purchase_rate", "is_stock_item"])
last_purchase_rate_in_sales_uom = last_purchase_rate * (it.conversion_factor or 1)
if flt(it.base_net_rate) < flt(last_purchase_rate_in_sales_uom):
@@ -312,7 +311,7 @@
sales_order.update_reserved_qty(so_item_rows)
def set_incoming_rate(self):
- if self.doctype not in ("Delivery Note", "Sales Invoice"):
+ if self.doctype not in ("Delivery Note", "Sales Invoice", "Sales Order"):
return
items = self.get("items") + (self.get("packed_items") or [])
@@ -322,15 +321,26 @@
d.incoming_rate = get_incoming_rate({
"item_code": d.item_code,
"warehouse": d.warehouse,
- "posting_date": self.posting_date,
- "posting_time": self.posting_time,
- "qty": -1*flt(d.qty),
- "serial_no": d.serial_no,
+ "posting_date": self.get('posting_date') or self.get('transaction_date'),
+ "posting_time": self.get('posting_time') or nowtime(),
+ "qty": -1 * flt(d.get('stock_qty') or d.get('actual_qty')),
+ "serial_no": d.get('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)
+
+ # For internal transfers use incoming rate as the valuation rate
+ if self.is_internal_transfer():
+ rate = flt(d.incoming_rate * d.conversion_factor, d.precision('rate'))
+ if d.rate != rate:
+ d.rate = rate
+ d.discount_percentage = 0
+ d.discount_amount = 0
+ 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
# based on original item cost as per valuation method
@@ -391,7 +401,7 @@
})
if item_row.warehouse:
sle.dependant_sle_voucher_detail_no = item_row.name
-
+
return sle
def set_po_nos(self, for_validate=False):
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index e0fcf47..4b5e347 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -24,6 +24,7 @@
self.validate_inspection()
self.validate_serialized_batch()
self.validate_customer_provided_item()
+ self.validate_internal_transfer()
self.validate_putaway_capacity()
def make_gl_entries(self, gl_entries=None, from_repost=False):
@@ -74,6 +75,7 @@
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)
if sle_list:
for sle in sle_list:
@@ -218,7 +220,7 @@
""", (self.doctype, self.name), as_dict=True)
for sle in stock_ledger_entries:
- stock_ledger.setdefault(sle.voucher_detail_no, []).append(sle)
+ stock_ledger.setdefault(sle.voucher_detail_no, []).append(sle)
return stock_ledger
def make_batches(self, warehouse_field):
@@ -393,6 +395,32 @@
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()
+
+ def validate_in_transit_warehouses(self):
+ if (self.doctype == 'Sales Invoice' and self.get('update_stock')) or self.doctype == 'Delivery Note':
+ for item in self.get('items'):
+ if not item.target_warehouse:
+ frappe.throw(_("Row {0}: Target Warehouse is mandatory for internal transfers").format(item.idx))
+
+ if (self.doctype == 'Purchase Invoice' and self.get('update_stock')) or self.doctype == 'Purchase Receipt':
+ for item in self.get('items'):
+ if not item.from_warehouse:
+ frappe.throw(_("Row {0}: From Warehouse is mandatory for internal transfers").format(item.idx))
+
+ 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_putaway_capacity(self):
# if over receipt is attempted while 'apply putaway rule' is disabled
# and if rule was applied on the transaction, validate it.
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index 1d1f18b..9627600 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -105,10 +105,18 @@
frappe.ui.form.on(this.frm.doctype + " Item", {
items_add: function(frm, cdt, cdn) {
var item = frappe.get_doc(cdt, cdn);
- if(!item.warehouse && frm.doc.set_warehouse) {
+ if (!item.warehouse && frm.doc.set_warehouse) {
item.warehouse = frm.doc.set_warehouse;
}
+ if (!item.target_warehouse && frm.doc.set_target_warehouse) {
+ item.target_warehouse = frm.doc.set_target_warehouse;
+ }
+
+ if (!item.from_warehouse && frm.doc.set_from_warehouse) {
+ item.from_warehouse = frm.doc.set_from_warehouse;
+ }
+
erpnext.accounts.dimensions.copy_dimension_from_first_row(frm, cdt, cdn, 'items');
}
});
@@ -227,6 +235,8 @@
}
};
+ this.frm.trigger('set_default_internal_warehouse');
+
return frappe.run_serially([
() => set_value('currency', currency),
() => set_value('price_list_currency', currency),
@@ -658,7 +668,7 @@
args: item_args
},
callback: function(r) {
- frappe.model.set_value(item.doctype, item.name, 'rate', r.message);
+ frappe.model.set_value(item.doctype, item.name, 'rate', r.message * item.conversion_factor);
}
});
},
@@ -724,6 +734,31 @@
this.calculate_taxes_and_totals(false);
},
+ update_stock: function() {
+ this.frm.trigger('set_default_internal_warehouse');
+ },
+
+ set_default_internal_warehouse: function() {
+ let me = this;
+ if ((this.frm.doc.doctype === 'Sales Invoice' && me.frm.doc.update_stock)
+ || this.frm.doc.doctype == 'Delivery Note') {
+ if (this.frm.doc.is_internal_customer && this.frm.doc.company === this.frm.doc.represents_company) {
+ frappe.db.get_value('Company', this.frm.doc.company, 'default_in_transit_warehouse', function(value) {
+ me.frm.set_value('set_target_warehouse', value.default_in_transit_warehouse);
+ });
+ }
+ }
+
+ if ((this.frm.doc.doctype === 'Purchase Invoice' && me.frm.doc.update_stock)
+ || this.frm.doc.doctype == 'Purchase Receipt') {
+ if (this.frm.doc.is_internal_supplier && this.frm.doc.company === this.frm.doc.represents_company) {
+ frappe.db.get_value('Company', this.frm.doc.company, 'default_in_transit_warehouse', function(value) {
+ me.frm.set_value('set_from_warehouse', value.default_in_transit_warehouse);
+ });
+ }
+ }
+ },
+
company: function() {
var me = this;
var set_pricing = function() {
@@ -810,7 +845,7 @@
in_list(['Purchase Order', 'Purchase Receipt', 'Purchase Invoice'], this.frm.doctype)) {
erpnext.utils.get_shipping_address(this.frm, function(){
set_party_account(set_pricing);
- })
+ });
// Get default company billing address in Purchase Invoice, Order and Receipt
frappe.call({
@@ -1977,6 +2012,14 @@
this.autofill_warehouse(this.frm.doc.items, "warehouse", this.frm.doc.set_warehouse);
},
+ set_target_warehouse: function() {
+ this.autofill_warehouse(this.frm.doc.items, "target_warehouse", this.frm.doc.set_target_warehouse);
+ },
+
+ set_from_warehouse: function() {
+ this.autofill_warehouse(this.frm.doc.items, "from_warehouse", this.frm.doc.set_from_warehouse);
+ },
+
autofill_warehouse : function (child_table, warehouse_field, warehouse) {
if (warehouse && child_table && child_table.length) {
let doctype = child_table[0].doctype;
diff --git a/erpnext/public/js/utils/party.js b/erpnext/public/js/utils/party.js
index 770704e..808dd5a 100644
--- a/erpnext/public/js/utils/party.js
+++ b/erpnext/public/js/utils/party.js
@@ -276,6 +276,12 @@
erpnext.utils.get_shipping_address = function(frm, callback){
if (frm.doc.company) {
+ if (!(frm.doc.inter_com_order_reference || frm.doc.internal_invoice_reference ||
+ frm.doc.internal_order_reference)) {
+ if (callback) {
+ return callback();
+ }
+ }
frappe.call({
method: "erpnext.accounts.custom.address.get_shipping_address",
args: {
diff --git a/erpnext/regional/india/taxes.js b/erpnext/regional/india/taxes.js
index 87baece..f09d3d0 100644
--- a/erpnext/regional/india/taxes.js
+++ b/erpnext/regional/india/taxes.js
@@ -40,14 +40,12 @@
callback: function(r) {
if(r.message) {
frm.set_value('taxes_and_charges', r.message.taxes_and_charges);
+ frm.set_value('taxes', r.message.taxes);
frm.set_value('place_of_supply', r.message.place_of_supply);
- } else if (frm.doc.is_internal_supplier || frm.doc.is_internal_customer) {
- frm.set_value('taxes_and_charges', '');
- frm.set_value('taxes', []);
}
}
});
}
});
-};
+}
diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py
index 0d82638..e89885f 100644
--- a/erpnext/regional/india/utils.py
+++ b/erpnext/regional/india/utils.py
@@ -171,7 +171,7 @@
if is_internal_transfer(party_details, doctype):
party_details.taxes_and_charges = ''
- party_details.taxes = ''
+ party_details.taxes = []
return party_details
if doctype in ("Sales Invoice", "Delivery Note", "Sales Order"):
diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py
index 29214ee..bf8b7fc 100644
--- a/erpnext/selling/doctype/customer/customer.py
+++ b/erpnext/selling/doctype/customer/customer.py
@@ -84,7 +84,10 @@
frappe.throw(_("{0} is not a company bank account").format(frappe.bold(self.default_bank_account)))
def validate_internal_customer(self):
- if self.is_internal_customer and frappe.db.get_value('Customer', {"represents_company": self.represents_company}, "name"):
+ internal_customer = frappe.db.get_value("Customer",
+ {"is_internal_customer": 1, "represents_company": self.represents_company, "name": ("!=", self.name)}, "name")
+
+ if internal_customer:
frappe.throw(_("Internal Customer for company {0} already exists").format(
frappe.bold(self.represents_company)))
diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js
index d4fb07c..78f9df9 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.js
+++ b/erpnext/selling/doctype/sales_order/sales_order.js
@@ -171,8 +171,10 @@
this.frm.add_custom_button(__('Request for Raw Materials'), () => this.make_raw_material_request(), __('Create'));
}
- // make purchase order
+ // Make Purchase Order
+ if (!this.frm.doc.is_internal_customer) {
this.frm.add_custom_button(__('Purchase Order'), () => this.make_purchase_order(), __('Create'));
+ }
// maintenance
if(flt(doc.per_delivered, 2) < 100 && (order_is_maintenance || order_is_a_custom_sale)) {
@@ -193,16 +195,15 @@
if (doc.docstatus === 1 && !doc.inter_company_order_reference) {
let me = this;
- frappe.model.with_doc("Customer", me.frm.doc.customer, () => {
- let customer = frappe.model.get_doc("Customer", me.frm.doc.customer);
- let internal = customer.is_internal_customer;
- let disabled = customer.disabled;
- if (internal === 1 && disabled === 0) {
- me.frm.add_custom_button("Inter Company Order", function() {
- me.make_inter_company_order();
- }, __('Create'));
- }
- });
+ let internal = me.frm.doc.is_internal_customer;
+ if (internal) {
+ let button_label = (me.frm.doc.company === me.frm.doc.represents_company) ? "Internal Purchase Order" :
+ "Inter Company Purchase Order";
+
+ me.frm.add_custom_button(button_label, function() {
+ me.make_inter_company_order();
+ }, __('Create'));
+ }
}
}
// payment request
diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json
index 3d64ac3..0a5c665 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.json
+++ b/erpnext/selling/doctype/sales_order/sales_order.json
@@ -107,6 +107,8 @@
"tc_name",
"terms",
"more_info",
+ "is_internal_customer",
+ "represents_company",
"inter_company_order_reference",
"project",
"party_account_currency",
@@ -1103,7 +1105,8 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Inter Company Order Reference",
- "options": "Purchase Order"
+ "options": "Purchase Order",
+ "read_only": 1
},
{
"description": "Track this Sales Order against any Project",
@@ -1455,13 +1458,29 @@
"hide_seconds": 1,
"label": "Skip Delivery Note",
"print_hide": 1
+ },
+ {
+ "default": "0",
+ "fetch_from": "customer.is_internal_customer",
+ "fieldname": "is_internal_customer",
+ "fieldtype": "Check",
+ "label": "Is Internal Customer",
+ "read_only": 1
+ },
+ {
+ "fetch_from": "customer.represents_company",
+ "fieldname": "represents_company",
+ "fieldtype": "Link",
+ "label": "Represents Company",
+ "options": "Company",
+ "read_only": 1
}
],
"icon": "fa fa-file-text",
"idx": 105,
"is_submittable": 1,
"links": [],
- "modified": "2020-10-30 13:59:18.628077",
+ "modified": "2021-01-20 23:40:39.929296",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Order",
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js
index ee18042..334bdea 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.js
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.js
@@ -95,13 +95,19 @@
frm.page.set_inner_btn_group_as_primary(__('Create'));
}
- if (frm.doc.docstatus === 1 && frm.doc.is_internal_customer && !frm.doc.inter_company_reference) {
- frm.add_custom_button(__('Purchase Receipt'), function() {
- frappe.model.open_mapped_doc({
- method: 'erpnext.stock.doctype.delivery_note.delivery_note.make_inter_company_purchase_receipt',
- frm: frm,
- })
- }, __('Create'));
+ if (frm.doc.docstatus == 1 && !frm.doc.inter_company_reference) {
+ let internal = me.frm.doc.is_internal_customer;
+ if (internal) {
+ let button_label = (me.frm.doc.company === me.frm.doc.represents_company) ? "Internal Purchase Receipt" :
+ "Inter Company Purchase Receipt";
+
+ me.frm.add_custom_button(button_label, function() {
+ frappe.model.open_mapped_doc({
+ method: 'erpnext.stock.doctype.delivery_note.delivery_note.make_inter_company_purchase_receipt',
+ frm: frm,
+ });
+ }, __('Create'));
+ }
}
}
});
@@ -297,15 +303,6 @@
}
})
},
-
- to_warehouse: function() {
- let packed_items_table = this.frm.doc["packed_items"];
- this.autofill_warehouse(this.frm.doc["items"], "target_warehouse", this.frm.doc.to_warehouse);
- if (packed_items_table && packed_items_table.length) {
- this.autofill_warehouse(packed_items_table, "target_warehouse", this.frm.doc.to_warehouse);
- }
- }
-
});
$.extend(cur_frm.cscript, new erpnext.stock.DeliveryNoteController({frm: cur_frm}));
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json
index c9f8d08..f595aad 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.json
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.json
@@ -53,7 +53,7 @@
"sec_warehouse",
"set_warehouse",
"col_break_warehouse",
- "to_warehouse",
+ "set_target_warehouse",
"items_section",
"scan_barcode",
"items",
@@ -117,6 +117,7 @@
"source",
"column_break5",
"is_internal_customer",
+ "represents_company",
"inter_company_reference",
"per_billed",
"customer_group",
@@ -503,18 +504,6 @@
"fieldtype": "Column Break"
},
{
- "description": "Required only for sample item.",
- "fieldname": "to_warehouse",
- "fieldtype": "Link",
- "in_standard_filter": 1,
- "label": "To Warehouse",
- "no_copy": 1,
- "oldfieldname": "to_warehouse",
- "oldfieldtype": "Link",
- "options": "Warehouse",
- "print_hide": 1
- },
- {
"fieldname": "items_section",
"fieldtype": "Section Break",
"oldfieldtype": "Section Break",
@@ -1261,13 +1250,34 @@
"no_copy": 1,
"print_hide": 1,
"read_only": 1
+ },
+ {
+ "depends_on": "eval: doc.is_internal_customer",
+ "fieldname": "set_target_warehouse",
+ "fieldtype": "Link",
+ "in_standard_filter": 1,
+ "label": "Set Target Warehouse",
+ "no_copy": 1,
+ "oldfieldname": "to_warehouse",
+ "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-11-30 12:54:45.407289",
+ "modified": "2020-12-26 17:07:59.194403",
"modified_by": "Administrator",
"module": "Stock",
"name": "Delivery Note",
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index a30cadf..fa5a7fb 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -664,7 +664,8 @@
return make_inter_company_transaction("Delivery Note", source_name, target_doc)
def make_inter_company_transaction(doctype, source_name, target_doc=None):
- from erpnext.accounts.doctype.sales_invoice.sales_invoice import validate_inter_company_transaction, get_inter_company_details
+ from erpnext.accounts.doctype.sales_invoice.sales_invoice import (validate_inter_company_transaction,
+ get_inter_company_details, update_address, update_taxes, set_purchase_references)
if doctype == 'Delivery Note':
source_doc = frappe.get_doc(doctype, source_name)
@@ -682,6 +683,7 @@
def set_missing_values(source, target):
target.run_method("set_missing_values")
+ set_purchase_references(target)
if target.doctype == 'Purchase Receipt':
master_doctype = 'Purchase Taxes and Charges Template'
@@ -697,21 +699,35 @@
if target_doc.doctype == 'Purchase Receipt':
target_doc.company = details.get("company")
target_doc.supplier = details.get("party")
- target_doc.supplier_address = source_doc.company_address
- target_doc.shipping_address = source_doc.shipping_address_name or source_doc.customer_address
target_doc.buying_price_list = source_doc.selling_price_list
target_doc.is_internal_supplier = 1
target_doc.inter_company_reference = source_doc.name
+
+ # Invert the address on target doc creation
+ update_address(target_doc, 'supplier_address', 'address_display', source_doc.company_address)
+ update_address(target_doc, 'shipping_address', 'shipping_address_display', source_doc.customer_address)
+
+ update_taxes(target_doc, party=target_doc.supplier, party_type='Supplier', company=target_doc.company,
+ doctype=target_doc.doctype, party_address=target_doc.supplier_address,
+ company_address=target_doc.shipping_address)
else:
target_doc.company = details.get("company")
target_doc.customer = details.get("party")
target_doc.company_address = source_doc.supplier_address
- target_doc.shipping_address_name = source_doc.shipping_address
target_doc.selling_price_list = source_doc.buying_price_list
target_doc.is_internal_customer = 1
target_doc.inter_company_reference = source_doc.name
- doclist = get_mapped_doc(doctype, source_name, {
+ # Invert the address on target doc creation
+ update_address(target_doc, 'company_address', 'company_address_display', source_doc.supplier_address)
+ update_address(target_doc, 'shipping_address_name', 'shipping_address', source_doc.shipping_address)
+ update_address(target_doc, 'customer_address', 'address_display', source_doc.shipping_address)
+
+ update_taxes(target_doc, party=target_doc.customer, party_type='Customer', company=target_doc.company,
+ doctype=target_doc.doctype, party_address=target_doc.customer_address,
+ company_address=target_doc.company_address, shipping_address_name=target_doc.shipping_address_name)
+
+ doclist = get_mapped_doc(doctype, source_name, {
doctype: {
"doctype": target_doctype,
"postprocess": update_details,
@@ -722,7 +738,10 @@
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',
+ 'batch_no': 'batch_no',
+ 'serial_no': 'serial_no'
},
"field_no_map": [
"warehouse"
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 4bbf3de..9de088d 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 55f0f0c..32d349f 100755
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
@@ -48,6 +48,7 @@
"set_warehouse",
"rejected_warehouse",
"col_break_warehouse",
+ "set_from_warehouse",
"is_subcontracted",
"supplier_warehouse",
"items_section",
@@ -115,6 +116,7 @@
"per_returned",
"is_internal_supplier",
"inter_company_reference",
+ "represents_company",
"subscription_detail",
"auto_repeat",
"printing_settings",
@@ -1087,7 +1089,9 @@
"fieldname": "inter_company_reference",
"fieldtype": "Link",
"label": "Inter Company Reference",
+ "no_copy": 1,
"options": "Delivery Note",
+ "print_hide": 1,
"read_only": 1
},
{
@@ -1121,13 +1125,29 @@
"no_copy": 1,
"print_hide": 1,
"read_only": 1
+ },
+ {
+ "depends_on": "eval: doc.is_internal_supplier",
+ "description": "Sets 'From Warehouse' in each row of the items table.",
+ "fieldname": "set_from_warehouse",
+ "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-08 18:31:32.234503",
+ "modified": "2020-12-26 20:49:39.106049",
"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 662e50c..e991192 100644
--- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
+++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
@@ -76,6 +76,7 @@
"purchase_order_item",
"material_request_item",
"purchase_receipt_item",
+ "delivery_note_item",
"putaway_rule",
"section_break_45",
"allow_zero_valuation_rate",
@@ -819,11 +820,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"
},
{
@@ -871,12 +873,20 @@
"fieldtype": "Float",
"label": "Received Qty in Stock UOM",
"print_hide": 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-09 10:00:38.204294",
+ "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 5b9ada0..2b2a7a2 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -41,7 +41,7 @@
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 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 @@
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 @@
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 @@
# 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 @@
# 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 @@
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 @@
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,7 +374,7 @@
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):
# Update item's incoming rate on transaction
item_code = frappe.db.get_value(sle.voucher_type + " Item", sle.voucher_detail_no, "item_code")
@@ -487,7 +487,6 @@
self.wh_data.valuation_rate = new_stock_value / new_stock_qty
else:
self.wh_data.valuation_rate = sle.outgoing_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 +630,7 @@
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 +765,7 @@
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 +793,7 @@
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 +802,7 @@
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