Merge branch 'develop' into project-naming-series-patch

This commit is contained in:
Marica 2021-01-28 14:32:11 +05:30 committed by GitHub
commit 9580adad0c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 1167 additions and 356 deletions

View File

@ -254,7 +254,8 @@ def create_account(**kwargs):
account_name = kwargs.get('account_name'), account_name = kwargs.get('account_name'),
account_type = kwargs.get('account_type'), account_type = kwargs.get('account_type'),
parent_account = kwargs.get('parent_account'), parent_account = kwargs.get('parent_account'),
company = kwargs.get('company') company = kwargs.get('company'),
account_currency = kwargs.get('account_currency')
)) ))
account.save() account.save()

View File

@ -159,8 +159,8 @@ class GLEntry(Document):
if not self.flags.from_repost and not self.voucher_type == 'Period Closing Voucher' \ if not self.flags.from_repost and not self.voucher_type == 'Period Closing Voucher' \
and self.cost_center and _check_is_group(): and self.cost_center and _check_is_group():
frappe.throw(_("""{0} {1}: Cost Center {2} is a group cost center and group cost centers cannot frappe.throw(_("""{0} {1}: Cost Center {2} is a group cost center and group cost centers cannot be used in transactions""").format(
be used in transactions""").format(self.voucher_type, self.voucher_no, frappe.bold(self.cost_center))) self.voucher_type, self.voucher_no, frappe.bold(self.cost_center)))
def validate_party(self): def validate_party(self):
validate_party_frozen_disabled(self.party_type, self.party) validate_party_frozen_disabled(self.party_type, self.party)
@ -170,7 +170,7 @@ class GLEntry(Document):
account_currency = get_account_currency(self.account) account_currency = get_account_currency(self.account)
if not self.account_currency: if not self.account_currency:
self.account_currency = company_currency self.account_currency = account_currency or company_currency
if account_currency != self.account_currency: if account_currency != self.account_currency:
frappe.throw(_("{0} {1}: Accounting Entry for {2} can only be made in currency: {3}") frappe.throw(_("{0} {1}: Accounting Entry for {2} can only be made in currency: {3}")

View File

@ -275,8 +275,11 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
supplier: function() { supplier: function() {
var me = this; 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; return;
erpnext.utils.get_party_details(this.frm, "erpnext.accounts.party.get_party_details", erpnext.utils.get_party_details(this.frm, "erpnext.accounts.party.get_party_details",
{ {
posting_date: this.frm.doc.posting_date, posting_date: this.frm.doc.posting_date,

View File

@ -57,8 +57,8 @@
"set_warehouse", "set_warehouse",
"rejected_warehouse", "rejected_warehouse",
"col_break_warehouse", "col_break_warehouse",
"set_from_warehouse",
"is_subcontracted", "is_subcontracted",
"supplier_warehouse",
"items_section", "items_section",
"update_stock", "update_stock",
"scan_barcode", "scan_barcode",
@ -515,6 +515,7 @@
}, },
{ {
"depends_on": "update_stock", "depends_on": "update_stock",
"description": "Sets 'Accepted Warehouse' in each row of the items table.",
"fieldname": "set_warehouse", "fieldname": "set_warehouse",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Set Accepted Warehouse", "label": "Set Accepted Warehouse",
@ -543,17 +544,6 @@
"options": "No\nYes", "options": "No\nYes",
"print_hide": 1 "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", "fieldname": "items_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
@ -1232,7 +1222,9 @@
"fieldname": "inter_company_invoice_reference", "fieldname": "inter_company_invoice_reference",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Inter Company Invoice Reference", "label": "Inter Company Invoice Reference",
"no_copy": 1,
"options": "Sales Invoice", "options": "Sales Invoice",
"print_hide": 1,
"read_only": 1 "read_only": 1
}, },
{ {
@ -1356,13 +1348,25 @@
"fieldtype": "Link", "fieldtype": "Link",
"label": "Represents Company", "label": "Represents Company",
"options": "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", "icon": "fa fa-file-text",
"idx": 204, "idx": 204,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2020-12-11 12:46:12.796378", "modified": "2020-12-26 20:49:03.305063",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Purchase Invoice", "name": "Purchase Invoice",

View File

@ -511,8 +511,8 @@ class PurchaseInvoice(BuyingController):
voucher_wise_stock_value = {} voucher_wise_stock_value = {}
if self.update_stock: if self.update_stock:
for d in frappe.get_all('Stock Ledger Entry', for d in frappe.get_all('Stock Ledger Entry',
fields = ["voucher_detail_no", "stock_value_difference"], filters={'voucher_no': self.name}): fields = ["voucher_detail_no", "stock_value_difference", "warehouse"], filters={'voucher_no': self.name}):
voucher_wise_stock_value.setdefault(d.voucher_detail_no, d.stock_value_difference) 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") valuation_tax_accounts = [d.account_head for d in self.get("taxes")
if d.category in ('Valuation', 'Total and Valuation') if d.category in ('Valuation', 'Total and Valuation')
@ -563,6 +563,7 @@ class PurchaseInvoice(BuyingController):
) )
else: else:
if not self.is_internal_transfer():
gl_entries.append( gl_entries.append(
self.get_gl_dict({ self.get_gl_dict({
"account": item.expense_account, "account": item.expense_account,
@ -582,7 +583,8 @@ class PurchaseInvoice(BuyingController):
"against": item.expense_account, "against": item.expense_account,
"cost_center": item.cost_center, "cost_center": item.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"), "remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"credit": flt(amount), "credit": flt(amount["base_amount"]),
"credit_in_account_currency": flt(amount["amount"]),
"project": item.project or self.project "project": item.project or self.project
}, item=item)) }, item=item))
@ -624,6 +626,7 @@ class PurchaseInvoice(BuyingController):
if expense_booked_in_pr: if expense_booked_in_pr:
expense_account = service_received_but_not_billed_account expense_account = service_received_but_not_billed_account
if not self.is_internal_transfer():
gl_entries.append(self.get_gl_dict({ gl_entries.append(self.get_gl_dict({
"account": expense_account, "account": expense_account,
"against": self.supplier, "against": self.supplier,
@ -795,10 +798,10 @@ class PurchaseInvoice(BuyingController):
# Stock ledger value is not matching with the warehouse amount # Stock ledger value is not matching with the warehouse amount
if (self.update_stock and voucher_wise_stock_value.get(item.name) and 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") 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 stock_adjustment_amt = warehouse_debit_amount - stock_amount
gl_entries.append( gl_entries.append(

View File

@ -1,4 +1,5 @@
{ {
"actions": [],
"autoname": "hash", "autoname": "hash",
"creation": "2013-05-22 12:43:10", "creation": "2013-05-22 12:43:10",
"doctype": "DocType", "doctype": "DocType",
@ -87,6 +88,7 @@
"po_detail", "po_detail",
"purchase_receipt", "purchase_receipt",
"pr_detail", "pr_detail",
"sales_invoice_item",
"item_weight_details", "item_weight_details",
"weight_per_unit", "weight_per_unit",
"total_weight", "total_weight",
@ -553,8 +555,8 @@
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 1, "hidden": 1,
"label": "Brand", "label": "Brand",
"print_hide": 1, "options": "Brand",
"options": "Brand" "print_hide": 1
}, },
{ {
"fetch_from": "item_code.item_group", "fetch_from": "item_code.item_group",
@ -562,9 +564,9 @@
"fieldname": "item_group", "fieldname": "item_group",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Item Group", "label": "Item Group",
"options": "Item Group",
"print_hide": 1, "print_hide": 1,
"read_only": 1, "read_only": 1
"options": "Item Group"
}, },
{ {
"description": "Tax detail table fetched from item master as a string and stored in this field.\nUsed for Taxes and Charges", "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 "read_only": 1
}, },
{ {
"depends_on": "eval:parent.is_internal_supplier && parent.update_stock",
"fieldname": "from_warehouse", "fieldname": "from_warehouse",
"fieldtype": "Link", "fieldtype": "Link",
"ignore_user_permissions": 1, "ignore_user_permissions": 1,
"label": "Supplier Warehouse", "label": "From Warehouse",
"options": "Warehouse" "options": "Warehouse"
}, },
{ {
@ -779,11 +782,20 @@
"no_copy": 1, "no_copy": 1,
"print_hide": 1, "print_hide": 1,
"read_only": 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, "idx": 1,
"istable": 1, "istable": 1,
"modified": "2020-08-20 11:48:01.398356", "links": [],
"modified": "2020-12-26 17:20:36.415791",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Purchase Invoice Item", "name": "Purchase Invoice Item",

View File

@ -130,16 +130,15 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
this.set_default_print_format(); this.set_default_print_format();
if (doc.docstatus == 1 && !doc.inter_company_invoice_reference) { if (doc.docstatus == 1 && !doc.inter_company_invoice_reference) {
frappe.model.with_doc("Customer", me.frm.doc.customer, function() { let internal = me.frm.doc.is_internal_customer;
var customer = frappe.model.get_doc("Customer", me.frm.doc.customer); if (internal) {
var internal = customer.is_internal_customer; let button_label = (me.frm.doc.company === me.frm.doc.represents_company) ? "Internal Purchase Invoice" :
var disabled = customer.disabled; "Inter Company Purchase Invoice";
if (internal == 1 && disabled == 0) {
me.frm.add_custom_button("Inter Company Invoice", function() { me.frm.add_custom_button(button_label, function() {
me.make_inter_company_invoice(); me.make_inter_company_invoice();
}, __('Create')); }, __('Create'));
} }
});
} }
}, },

View File

@ -60,6 +60,8 @@
"ignore_pricing_rule", "ignore_pricing_rule",
"sec_warehouse", "sec_warehouse",
"set_warehouse", "set_warehouse",
"column_break_55",
"set_target_warehouse",
"items_section", "items_section",
"update_stock", "update_stock",
"scan_barcode", "scan_barcode",
@ -1969,13 +1971,24 @@
"label": "Represents Company", "label": "Represents Company",
"options": "Company", "options": "Company",
"read_only": 1 "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", "icon": "fa fa-file-text",
"idx": 181, "idx": 181,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2020-12-11 12:48:31.769958", "modified": "2020-12-25 22:57:32.555067",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Sales Invoice", "name": "Sales Invoice",

View File

@ -6,7 +6,7 @@ import frappe, erpnext
import frappe.defaults import frappe.defaults
from frappe.utils import cint, flt, getdate, add_days, cstr, nowdate, get_link_to_form, formatdate from frappe.utils import cint, flt, getdate, add_days, cstr, nowdate, get_link_to_form, formatdate
from frappe import _, msgprint, throw 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 frappe.model.mapper import get_mapped_doc
from erpnext.controllers.selling_controller import SellingController from erpnext.controllers.selling_controller import SellingController
from erpnext.accounts.utils import get_account_currency from erpnext.accounts.utils import get_account_currency
@ -21,6 +21,8 @@ from erpnext.accounts.general_ledger import get_round_off_account_and_cost_cente
from erpnext.accounts.doctype.loyalty_program.loyalty_program import \ from erpnext.accounts.doctype.loyalty_program.loyalty_program import \
get_loyalty_program_details_with_points, get_loyalty_details, validate_loyalty_points get_loyalty_program_details_with_points, get_loyalty_details, validate_loyalty_points
from erpnext.accounts.deferred_revenue import validate_service_stop_date 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 from erpnext.healthcare.utils import manage_invoice_submit_cancel
@ -1534,7 +1536,7 @@ def validate_inter_company_transaction(doc, doctype):
details = get_inter_company_details(doc, doctype) 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 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}) 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.")) frappe.throw(_("Selected Price List should have buying and selling fields checked."))
party = details.get("party") party = details.get("party")
@ -1557,6 +1559,7 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
if doctype in ["Sales Invoice", "Sales Order"]: if doctype in ["Sales Invoice", "Sales Order"]:
source_doc = frappe.get_doc(doctype, source_name) source_doc = frappe.get_doc(doctype, source_name)
target_doctype = "Purchase Invoice" if doctype == "Sales Invoice" else "Purchase Order" 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' source_document_warehouse_field = 'target_warehouse'
target_document_warehouse_field = 'from_warehouse' target_document_warehouse_field = 'from_warehouse'
else: else:
@ -1570,6 +1573,7 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
def set_missing_values(source, target): def set_missing_values(source, target):
target.run_method("set_missing_values") target.run_method("set_missing_values")
set_purchase_references(target)
def update_details(source_doc, target_doc, source_parent): def update_details(source_doc, target_doc, source_parent):
target_doc.inter_company_invoice_reference = source_doc.name target_doc.inter_company_invoice_reference = source_doc.name
@ -1577,19 +1581,38 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
currency = frappe.db.get_value('Supplier', details.get('party'), 'default_currency') currency = frappe.db.get_value('Supplier', details.get('party'), 'default_currency')
target_doc.company = details.get("company") target_doc.company = details.get("company")
target_doc.supplier = details.get("party") 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 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: if currency:
target_doc.currency = 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: else:
currency = frappe.db.get_value('Customer', details.get('party'), 'default_currency') currency = frappe.db.get_value('Customer', details.get('party'), 'default_currency')
target_doc.company = details.get("company") target_doc.company = details.get("company")
target_doc.customer = details.get("party") target_doc.customer = details.get("party")
target_doc.selling_price_list = source_doc.buying_price_list 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: if currency:
target_doc.currency = 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 = { item_field_map = {
"doctype": target_doctype + " Item", "doctype": target_doctype + " Item",
"field_no_map": [ "field_no_map": [
@ -1597,25 +1620,33 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
"expense_account", "expense_account",
"cost_center", "cost_center",
"warehouse" "warehouse"
] ],
"field_map": {
'rate': 'rate',
}
} }
if doctype in ["Sales Invoice", "Sales Order"]:
item_field_map["field_map"].update({
"name": target_detail_field,
})
if source_doc.get('update_stock'): if source_doc.get('update_stock'):
item_field_map.update({ item_field_map["field_map"].update({
'field_map': {
source_document_warehouse_field: target_document_warehouse_field, source_document_warehouse_field: target_document_warehouse_field,
'batch_no': 'batch_no', 'batch_no': 'batch_no',
'serial_no': 'serial_no' 'serial_no': 'serial_no'
}
}) })
doclist = get_mapped_doc(doctype, source_name, { doclist = get_mapped_doc(doctype, source_name, {
doctype: { doctype: {
"doctype": target_doctype, "doctype": target_doctype,
"postprocess": update_details, "postprocess": update_details,
"set_target_warehouse": "set_from_warehouse",
"field_no_map": [ "field_no_map": [
"taxes_and_charges" "taxes_and_charges",
"set_warehouse",
"shipping_address"
] ]
}, },
doctype +" Item": item_field_map doctype +" Item": item_field_map
@ -1624,6 +1655,110 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
return doclist 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() @frappe.whitelist()
def get_loyalty_programs(customer): def get_loyalty_programs(customer):
''' sets applicable loyalty program to the customer or returns a list of applicable programs ''' ''' sets applicable loyalty program to the customer or returns a list of applicable programs '''

View File

@ -22,6 +22,7 @@ from erpnext.regional.india.utils import get_ewb_data
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry from erpnext.stock.doctype.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.purchase_receipt.test_purchase_receipt import make_purchase_receipt
from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
from erpnext.stock.utils import get_incoming_rate
class TestSalesInvoice(unittest.TestCase): class TestSalesInvoice(unittest.TestCase):
def make(self): def make(self):
@ -1770,59 +1771,82 @@ class TestSalesInvoice(unittest.TestCase):
self.assertEqual(target_doc.company, "_Test Company 1") self.assertEqual(target_doc.company, "_Test Company 1")
self.assertEqual(target_doc.supplier, "_Test Internal Supplier") self.assertEqual(target_doc.supplier, "_Test Internal Supplier")
# def test_internal_transfer_gl_entry(self): def test_internal_transfer_gl_entry(self):
# ## Create internal transfer account ## Create internal transfer account
# account = create_account(account_name="Unrealized Profit", account = create_account(account_name="Unrealized Profit",
# parent_account="Current Liabilities - TCP1", company="_Test Company with perpetual inventory") parent_account="Current Liabilities - TCP1", company="_Test Company with perpetual inventory")
# frappe.db.set_value('Company', '_Test Company with perpetual inventory', frappe.db.set_value('Company', '_Test Company with perpetual inventory',
# 'unrealized_profit_loss_account', account) 'unrealized_profit_loss_account', account)
# customer = create_internal_customer("_Test Internal Customer 2", "_Test Company with perpetual inventory", customer = create_internal_customer("_Test Internal Customer 2", "_Test Company with perpetual inventory",
# "_Test Company with perpetual inventory") "_Test Company with perpetual inventory")
# create_internal_supplier("_Test Internal Supplier 2", "_Test Company with perpetual inventory", create_internal_supplier("_Test Internal Supplier 2", "_Test Company with perpetual inventory",
# "_Test Company with perpetual inventory") "_Test Company with perpetual inventory")
# si = create_sales_invoice( si = create_sales_invoice(
# company = "_Test Company with perpetual inventory", company = "_Test Company with perpetual inventory",
# customer = customer, customer = customer,
# debit_to = "Debtors - TCP1", debit_to = "Debtors - TCP1",
# warehouse = "Stores - TCP1", warehouse = "Stores - TCP1",
# income_account = "Sales - TCP1", income_account = "Sales - TCP1",
# expense_account = "Cost of Goods Sold - TCP1", expense_account = "Cost of Goods Sold - TCP1",
# cost_center = "Main - TCP1", cost_center = "Main - TCP1",
# currency = "INR", currency = "INR",
# do_not_save = 1 do_not_save = 1
# ) )
# si.selling_price_list = "_Test Price List Rest of the World" si.selling_price_list = "_Test Price List Rest of the World"
# si.update_stock = 1 si.update_stock = 1
# si.items[0].target_warehouse = 'Work In Progress - TCP1' si.items[0].target_warehouse = 'Work In Progress - TCP1'
# add_taxes(si) add_taxes(si)
# si.save() si.save()
# si.submit()
# target_doc = make_inter_company_transaction("Sales Invoice", si.name) rate = 0.0
# target_doc.company = '_Test Company with perpetual inventory' for d in si.get('items'):
# target_doc.items[0].warehouse = 'Finished Goods - TCP1' rate = get_incoming_rate({
# add_taxes(target_doc) "item_code": d.item_code,
# target_doc.save() "warehouse": d.warehouse,
# target_doc.submit() "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 = [ rate = flt(rate, 2)
# ["_Test Account Excise Duty - TCP1", 0.0, 12.0, nowdate()],
# ["Unrealized Profit - TCP1", 12.0, 0.0, nowdate()]
# ]
# check_gl_entries(self, si.name, si_gl_entries, add_days(nowdate(), -1)) si.submit()
# pi_gl_entries = [ target_doc = make_inter_company_transaction("Sales Invoice", si.name)
# ["_Test Account Excise Duty - TCP1", 12.0 , 0.0, nowdate()], target_doc.company = '_Test Company with perpetual inventory'
# ["Unrealized Profit - TCP1", 0.0, 12.0, nowdate()] 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): def test_eway_bill_json(self):
si = make_sales_invoice_for_ewaybill() si = make_sales_invoice_for_ewaybill()

View File

@ -565,11 +565,12 @@
"print_hide": 1 "print_hide": 1
}, },
{ {
"depends_on": "eval: parent.is_internal_customer && parent.update_stock",
"fieldname": "target_warehouse", "fieldname": "target_warehouse",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 1, "hidden": 1,
"ignore_user_permissions": 1, "ignore_user_permissions": 1,
"label": "Customer Warehouse (Optional)", "label": "Target Warehouse",
"no_copy": 1, "no_copy": 1,
"options": "Warehouse", "options": "Warehouse",
"print_hide": 1 "print_hide": 1
@ -815,7 +816,7 @@
"idx": 1, "idx": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2020-09-23 19:59:04.879322", "modified": "2020-12-26 17:25:04.090630",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Sales Invoice Item", "name": "Sales Invoice Item",

View File

@ -49,7 +49,8 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
elif d.po_detail: elif d.po_detail:
purchase_receipt = ", ".join(po_pr_map.get(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 = { row = {
'item_code': d.item_code, 'item_code': d.item_code,
@ -315,6 +316,7 @@ def get_items(filters, additional_query_columns):
`tabPurchase Invoice Item`.`name`, `tabPurchase Invoice Item`.`parent`, `tabPurchase Invoice Item`.`name`, `tabPurchase Invoice Item`.`parent`,
`tabPurchase Invoice`.posting_date, `tabPurchase Invoice`.credit_to, `tabPurchase Invoice`.company, `tabPurchase Invoice`.posting_date, `tabPurchase Invoice`.credit_to, `tabPurchase Invoice`.company,
`tabPurchase Invoice`.supplier, `tabPurchase Invoice`.remarks, `tabPurchase Invoice`.base_net_total, `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_code`, `tabPurchase Invoice Item`.description,
`tabPurchase Invoice Item`.`item_name`, `tabPurchase Invoice Item`.`item_group`, `tabPurchase Invoice Item`.`item_name`, `tabPurchase Invoice Item`.`item_group`,
`tabPurchase Invoice Item`.`project`, `tabPurchase Invoice Item`.`purchase_order`, `tabPurchase Invoice Item`.`project`, `tabPurchase Invoice Item`.`purchase_order`,

View File

@ -76,7 +76,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
'company': d.company, 'company': d.company,
'sales_order': d.sales_order, 'sales_order': d.sales_order,
'delivery_note': d.delivery_note, '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, 'cost_center': d.cost_center,
'stock_qty': d.stock_qty, 'stock_qty': d.stock_qty,
'stock_uom': d.stock_uom 'stock_uom': d.stock_uom
@ -379,6 +379,7 @@ def get_items(filters, additional_query_columns):
select select
`tabSales Invoice Item`.name, `tabSales Invoice Item`.parent, `tabSales Invoice Item`.name, `tabSales Invoice Item`.parent,
`tabSales Invoice`.posting_date, `tabSales Invoice`.debit_to, `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`.project, `tabSales Invoice`.customer, `tabSales Invoice`.remarks,
`tabSales Invoice`.territory, `tabSales Invoice`.company, `tabSales Invoice`.base_net_total, `tabSales Invoice`.territory, `tabSales Invoice`.company, `tabSales Invoice`.base_net_total,
`tabSales Invoice Item`.item_code, `tabSales Invoice Item`.description, `tabSales Invoice Item`.item_code, `tabSales Invoice Item`.description,

View File

@ -14,13 +14,15 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
if not filters: filters = {} if not filters: filters = {}
invoice_list = get_invoices(filters, additional_query_columns) 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: if not invoice_list:
msgprint(_("No record found")) msgprint(_("No record found"))
return columns, invoice_list return columns, invoice_list
invoice_expense_map = get_invoice_expense_map(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, invoice_tax_map = get_invoice_tax_map(invoice_list,
invoice_expense_map, expense_accounts) invoice_expense_map, expense_accounts)
invoice_po_pr_map = get_invoice_po_pr_map(invoice_list) invoice_po_pr_map = get_invoice_po_pr_map(invoice_list)
@ -52,10 +54,17 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
# map expense values # map expense values
base_net_total = 0 base_net_total = 0
for expense_acc in expense_accounts: for expense_acc in expense_accounts:
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)) expense_amount = flt(invoice_expense_map.get(inv.name, {}).get(expense_acc))
base_net_total += expense_amount base_net_total += expense_amount
row.append(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 # net total
row.append(base_net_total or inv.base_net_total) row.append(base_net_total or inv.base_net_total)
@ -96,7 +105,8 @@ def get_columns(invoice_list, additional_table_columns):
"width": 80 "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: if invoice_list:
expense_accounts = frappe.db.sql_list("""select distinct expense_account expense_accounts = frappe.db.sql_list("""select distinct expense_account
@ -112,17 +122,25 @@ def get_columns(invoice_list, additional_table_columns):
and parent in (%s) order by account_head""" % and parent in (%s) order by account_head""" %
', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) ', '.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] 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: for account in tax_accounts:
if account not in expense_accounts: if account not in expense_accounts:
tax_columns.append(account + ":Currency/currency:120") 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", [_("Total Tax") + ":Currency/currency:120", _("Grand Total") + ":Currency/currency:120",
_("Rounded Total") + ":Currency/currency:120", _("Outstanding Amount") + ":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): def get_conditions(filters):
conditions = "" conditions = ""
@ -199,6 +217,19 @@ def get_invoice_expense_map(invoice_list):
return invoice_expense_map 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): def get_invoice_tax_map(invoice_list, invoice_expense_map, expense_accounts):
tax_details = frappe.db.sql(""" tax_details = frappe.db.sql("""
select parent, account_head, case add_deduct_tax when "Add" then sum(base_tax_amount_after_discount_amount) select parent, account_head, case add_deduct_tax when "Add" then sum(base_tax_amount_after_discount_amount)

View File

@ -15,13 +15,14 @@ def _execute(filters, additional_table_columns=None, additional_query_columns=No
if not filters: filters = frappe._dict({}) if not filters: filters = frappe._dict({})
invoice_list = get_invoices(filters, additional_query_columns) 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: if not invoice_list:
msgprint(_("No record found")) msgprint(_("No record found"))
return columns, invoice_list return columns, invoice_list
invoice_income_map = get_invoice_income_map(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, invoice_tax_map = get_invoice_tax_map(invoice_list,
invoice_income_map, income_accounts) invoice_income_map, income_accounts)
#Cost Center & Warehouse Map #Cost Center & Warehouse Map
@ -70,12 +71,22 @@ def _execute(filters, additional_table_columns=None, additional_query_columns=No
# map income values # map income values
base_net_total = 0 base_net_total = 0
for income_acc in income_accounts: for income_acc in income_accounts:
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)) income_amount = flt(invoice_income_map.get(inv.name, {}).get(income_acc))
base_net_total += income_amount base_net_total += income_amount
row.update({ row.update({
frappe.scrub(income_acc): income_amount 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 # net total
row.update({'net_total': base_net_total or inv.base_net_total}) row.update({'net_total': base_net_total or inv.base_net_total})
@ -230,6 +241,8 @@ def get_columns(invoice_list, additional_table_columns):
tax_accounts = [] tax_accounts = []
income_columns = [] income_columns = []
tax_columns = [] tax_columns = []
unrealized_profit_loss_accounts = []
unrealized_profit_loss_account_columns = []
if invoice_list: if invoice_list:
income_accounts = frappe.db.sql_list("""select distinct income_account income_accounts = frappe.db.sql_list("""select distinct income_account
@ -243,12 +256,18 @@ def get_columns(invoice_list, additional_table_columns):
and parent in (%s) order by account_head""" % and parent in (%s) order by account_head""" %
', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list])) ', '.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: for account in income_accounts:
income_columns.append({ income_columns.append({
"label": account, "label": account,
"fieldname": frappe.scrub(account), "fieldname": frappe.scrub(account),
"fieldtype": "Currency", "fieldtype": "Currency",
"options": 'currency', "options": "currency",
"width": 120 "width": 120
}) })
@ -258,7 +277,16 @@ def get_columns(invoice_list, additional_table_columns):
"label": account, "label": account,
"fieldname": frappe.scrub(account), "fieldname": frappe.scrub(account),
"fieldtype": "Currency", "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 "width": 120
}) })
@ -266,7 +294,7 @@ def get_columns(invoice_list, additional_table_columns):
"label": _("Net Total"), "label": _("Net Total"),
"fieldname": "net_total", "fieldname": "net_total",
"fieldtype": "Currency", "fieldtype": "Currency",
"options": 'currency', "options": "currency",
"width": 120 "width": 120
}] }]
@ -301,9 +329,10 @@ def get_columns(invoice_list, additional_table_columns):
} }
] ]
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): def get_conditions(filters):
conditions = "" conditions = ""
@ -368,7 +397,8 @@ def get_invoices(filters, additional_query_columns):
return frappe.db.sql(""" return frappe.db.sql("""
select name, posting_date, debit_to, project, customer, select name, posting_date, debit_to, project, customer,
customer_name, owner, remarks, territory, tax_id, customer_group, 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` from `tabSales Invoice`
where docstatus = 1 %s order by posting_date desc, name desc""".format(additional_query_columns or '') % where docstatus = 1 %s order by posting_date desc, name desc""".format(additional_query_columns or '') %
conditions, filters, as_dict=1) conditions, filters, as_dict=1)
@ -385,6 +415,19 @@ def get_invoice_income_map(invoice_list):
return invoice_income_map 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): def get_invoice_tax_map(invoice_list, invoice_income_map, income_accounts):
tax_details = frappe.db.sql("""select parent, account_head, tax_details = frappe.db.sql("""select parent, account_head,
sum(base_tax_amount_after_discount_amount) as tax_amount sum(base_tax_amount_after_discount_amount) as tax_amount

View File

@ -164,16 +164,16 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
if (doc.docstatus === 1 && !doc.inter_company_order_reference) { if (doc.docstatus === 1 && !doc.inter_company_order_reference) {
let me = this; let me = this;
frappe.model.with_doc("Supplier", me.frm.doc.supplier, () => { let internal = me.frm.doc.is_internal_supplier;
let supplier = frappe.model.get_doc("Supplier", me.frm.doc.supplier); if (internal) {
let internal = supplier.is_internal_supplier; let button_label = (me.frm.doc.company === me.frm.doc.represents_company) ? "Internal Sales Order" :
let disabled = supplier.disabled; "Inter Company Sales Order";
if (internal === 1 && disabled === 0) {
me.frm.add_custom_button("Inter Company Order", function() { me.frm.add_custom_button(button_label, function() {
me.make_inter_company_order(me.frm); me.make_inter_company_order(me.frm);
}, __('Create')); }, __('Create'));
} }
});
} }
} }

View File

@ -134,6 +134,8 @@
"ref_sq", "ref_sq",
"column_break_74", "column_break_74",
"party_account_currency", "party_account_currency",
"is_internal_supplier",
"represents_company",
"inter_company_order_reference" "inter_company_order_reference"
], ],
"fields": [ "fields": [
@ -1101,13 +1103,28 @@
{ {
"fieldname": "items_col_break", "fieldname": "items_col_break",
"fieldtype": "Column 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", "icon": "fa fa-file-text",
"idx": 105, "idx": 105,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2020-12-03 16:46:44.229351", "modified": "2021-01-20 22:07:23.487138",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Purchase Order", "name": "Purchase Order",

View File

@ -52,7 +52,10 @@ class Supplier(TransactionBase):
self.validate_internal_supplier() self.validate_internal_supplier()
def validate_internal_supplier(self): 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.throw(_("Internal Supplier for company {0} already exists").format(
frappe.bold(self.represents_company))) frappe.bold(self.represents_company)))

View File

@ -75,6 +75,9 @@ class AccountsController(TransactionBase):
self.ensure_supplier_is_not_blocked() self.ensure_supplier_is_not_blocked()
self.validate_date_with_fiscal_year() self.validate_date_with_fiscal_year()
self.validate_inter_company_reference()
self.set_incoming_rate()
if self.meta.get_field("currency"): if self.meta.get_field("currency"):
self.calculate_taxes_and_totals() self.calculate_taxes_and_totals()
@ -212,6 +215,17 @@ class AccountsController(TransactionBase):
validate_fiscal_year(self.get(date_field), self.fiscal_year, self.company, validate_fiscal_year(self.get(date_field), self.fiscal_year, self.company,
self.meta.get_label(date_field), self) 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): def validate_due_date(self):
if self.get('is_pos'): return if self.get('is_pos'): return
@ -454,8 +468,10 @@ class AccountsController(TransactionBase):
account_currency = get_account_currency(gl_dict.account) account_currency = get_account_currency(gl_dict.account)
if gl_dict.account and self.doctype not in ["Journal Entry", if gl_dict.account and self.doctype not in ["Journal Entry",
"Period Closing Voucher", "Payment Entry"]: "Period Closing Voucher", "Payment Entry", "Purchase Receipt", "Purchase Invoice", "Stock Entry"]:
self.validate_account_currency(gl_dict.account, account_currency) self.validate_account_currency(gl_dict.account, account_currency)
if gl_dict.account and self.doctype not in ["Journal Entry", "Period Closing Voucher", "Payment Entry"]:
set_balance_in_account_currency(gl_dict, account_currency, self.get("conversion_rate"), set_balance_in_account_currency(gl_dict, account_currency, self.get("conversion_rate"),
self.company_currency) self.company_currency)
@ -968,9 +984,9 @@ class AccountsController(TransactionBase):
It will an internal transfer if its an internal customer and representation It will an internal transfer if its an internal customer and representation
company is same as billing company 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' internal_party_field = 'is_internal_customer'
else: elif self.doctype in ('Purchase Invoice', 'Purchase Receipt', 'Purchase Order'):
internal_party_field = 'is_internal_supplier' internal_party_field = 'is_internal_supplier'
if self.get(internal_party_field) and (self.represents_company == self.company): if self.get(internal_party_field) and (self.represents_company == self.company):

View File

@ -44,7 +44,6 @@ class BuyingController(StockController):
self.validate_items() self.validate_items()
self.set_qty_as_per_stock_uom() self.set_qty_as_per_stock_uom()
self.validate_stock_or_nonstock_items() self.validate_stock_or_nonstock_items()
self.update_tax_category_for_internal_transfer()
self.validate_warehouse() self.validate_warehouse()
self.validate_from_warehouse() self.validate_from_warehouse()
self.set_supplier_address() self.set_supplier_address()
@ -100,11 +99,6 @@ class BuyingController(StockController):
msg = _('Tax Category has been changed to "Total" because all the Items are non-stock items') msg = _('Tax Category has been changed to "Total" because all the Items are non-stock items')
self.update_tax_category(msg) 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): def update_tax_category(self, msg):
tax_for_valuation = [d for d in self.get("taxes") tax_for_valuation = [d for d in self.get("taxes")
if d.category in ["Valuation", "Valuation and Total"]] if d.category in ["Valuation", "Valuation and Total"]]
@ -224,6 +218,48 @@ class BuyingController(StockController):
else: else:
item.valuation_rate = 0.0 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): def get_supplied_items_cost(self, item_row_id, reset_outgoing_rate=True):
supplied_items_cost = 0.0 supplied_items_cost = 0.0
for d in self.get("supplied_items"): for d in self.get("supplied_items"):
@ -559,6 +595,8 @@ class BuyingController(StockController):
from_warehouse_sle = self.get_sl_entries(d, { from_warehouse_sle = self.get_sl_entries(d, {
"actual_qty": -1 * pr_qty, "actual_qty": -1 * pr_qty,
"warehouse": d.from_warehouse, "warehouse": d.from_warehouse,
"outgoing_rate": d.rate,
"recalculate_rate": 1,
"dependant_sle_voucher_detail_no": d.name "dependant_sle_voucher_detail_no": d.name
}) })

View File

@ -3,7 +3,7 @@
from __future__ import unicode_literals from __future__ import unicode_literals
import frappe 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 frappe import _, throw
from erpnext.stock.get_item_details import get_bin_details from erpnext.stock.get_item_details import get_bin_details
from erpnext.stock.utils import get_incoming_rate from erpnext.stock.utils import get_incoming_rate
@ -49,7 +49,6 @@ class SellingController(StockController):
self.set_customer_address() self.set_customer_address()
self.validate_for_duplicate_items() self.validate_for_duplicate_items()
self.validate_target_warehouse() self.validate_target_warehouse()
self.set_incoming_rate()
def set_missing_values(self, for_validate=False): def set_missing_values(self, for_validate=False):
@ -312,7 +311,7 @@ class SellingController(StockController):
sales_order.update_reserved_qty(so_item_rows) sales_order.update_reserved_qty(so_item_rows)
def set_incoming_rate(self): 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 return
items = self.get("items") + (self.get("packed_items") or []) items = self.get("items") + (self.get("packed_items") or [])
@ -322,15 +321,26 @@ class SellingController(StockController):
d.incoming_rate = get_incoming_rate({ d.incoming_rate = get_incoming_rate({
"item_code": d.item_code, "item_code": d.item_code,
"warehouse": d.warehouse, "warehouse": d.warehouse,
"posting_date": self.posting_date, "posting_date": self.get('posting_date') or self.get('transaction_date'),
"posting_time": self.posting_time, "posting_time": self.get('posting_time') or nowtime(),
"qty": -1*flt(d.qty), "qty": -1 * flt(d.get('stock_qty') or d.get('actual_qty')),
"serial_no": d.serial_no, "serial_no": d.get('serial_no'),
"company": self.company, "company": self.company,
"voucher_type": self.doctype, "voucher_type": self.doctype,
"voucher_no": self.name, "voucher_no": self.name,
"allow_zero_valuation": d.get("allow_zero_valuation") "allow_zero_valuation": d.get("allow_zero_valuation")
}, raise_error_if_no_rate=False) }, 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"): elif self.get("return_against"):
# Get incoming rate of return entry from reference document # Get incoming rate of return entry from reference document
# based on original item cost as per valuation method # based on original item cost as per valuation method

View File

@ -24,6 +24,7 @@ class StockController(AccountsController):
self.validate_inspection() self.validate_inspection()
self.validate_serialized_batch() self.validate_serialized_batch()
self.validate_customer_provided_item() self.validate_customer_provided_item()
self.validate_internal_transfer()
self.validate_putaway_capacity() self.validate_putaway_capacity()
def make_gl_entries(self, gl_entries=None, from_repost=False): def make_gl_entries(self, gl_entries=None, from_repost=False):
@ -74,6 +75,7 @@ class StockController(AccountsController):
warehouse_with_no_account = [] warehouse_with_no_account = []
precision = frappe.get_precision("GL Entry", "debit_in_account_currency") precision = frappe.get_precision("GL Entry", "debit_in_account_currency")
for item_row in voucher_details: for item_row in voucher_details:
sle_list = sle_map.get(item_row.name) sle_list = sle_map.get(item_row.name)
if sle_list: if sle_list:
for sle in sle_list: for sle in sle_list:
@ -393,6 +395,32 @@ class StockController(AccountsController):
if frappe.db.get_value('Item', d.item_code, 'is_customer_provided_item'): if frappe.db.get_value('Item', d.item_code, 'is_customer_provided_item'):
d.allow_zero_valuation_rate = 1 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): def validate_putaway_capacity(self):
# if over receipt is attempted while 'apply putaway rule' is disabled # if over receipt is attempted while 'apply putaway rule' is disabled
# and if rule was applied on the transaction, validate it. # and if rule was applied on the transaction, validate it.

View File

@ -10,6 +10,7 @@ from erpnext.controllers.accounts_controller import validate_conversion_rate, \
validate_taxes_and_charges, validate_inclusive_tax validate_taxes_and_charges, validate_inclusive_tax
from erpnext.stock.get_item_details import _get_item_tax_template from erpnext.stock.get_item_details import _get_item_tax_template
from erpnext.accounts.doctype.pricing_rule.utils import get_applied_pricing_rules from erpnext.accounts.doctype.pricing_rule.utils import get_applied_pricing_rules
from erpnext.accounts.doctype.journal_entry.journal_entry import get_exchange_rate
class calculate_taxes_and_totals(object): class calculate_taxes_and_totals(object):
def __init__(self, doc): def __init__(self, doc):
@ -758,3 +759,35 @@ def get_rounded_tax_amount(itemised_tax, precision):
for taxes in itemised_tax.values(): for taxes in itemised_tax.values():
for tax_account in taxes: for tax_account in taxes:
taxes[tax_account]["tax_amount"] = flt(taxes[tax_account]["tax_amount"], precision) taxes[tax_account]["tax_amount"] = flt(taxes[tax_account]["tax_amount"], precision)
class init_landed_taxes_and_totals(object):
def __init__(self, doc):
self.doc = doc
self.tax_field = 'taxes' if self.doc.doctype == 'Landed Cost Voucher' else 'additional_costs'
self.set_account_currency()
self.set_exchange_rate()
self.set_amounts_in_company_currency()
def set_account_currency(self):
company_currency = erpnext.get_company_currency(self.doc.company)
for d in self.doc.get(self.tax_field):
if not d.account_currency:
account_currency = frappe.db.get_value('Account', d.expense_account, 'account_currency')
d.account_currency = account_currency or company_currency
def set_exchange_rate(self):
company_currency = erpnext.get_company_currency(self.doc.company)
for d in self.doc.get(self.tax_field):
if d.account_currency == company_currency:
d.exchange_rate = 1
elif not d.exchange_rate or d.exchange_rate == 1 or self.doc.posting_date:
d.exchange_rate = get_exchange_rate(self.doc.posting_date, account=d.expense_account,
account_currency=d.account_currency, company=self.doc.company)
if not d.exchange_rate:
frappe.throw(_("Row {0}: Exchange Rate is mandatory").format(d.idx))
def set_amounts_in_company_currency(self):
for d in self.doc.get(self.tax_field):
d.amount = flt(d.amount, d.precision("amount"))
d.base_amount = flt(d.amount * flt(d.exchange_rate), d.precision("base_amount"))

View File

@ -109,6 +109,14 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
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'); erpnext.accounts.dimensions.copy_dimension_from_first_row(frm, cdt, cdn, 'items');
} }
}); });
@ -227,6 +235,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
} }
}; };
this.frm.trigger('set_default_internal_warehouse');
return frappe.run_serially([ return frappe.run_serially([
() => set_value('currency', currency), () => set_value('currency', currency),
() => set_value('price_list_currency', currency), () => set_value('price_list_currency', currency),
@ -658,7 +668,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
args: item_args args: item_args
}, },
callback: function(r) { 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 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
this.calculate_taxes_and_totals(false); 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() { company: function() {
var me = this; var me = this;
var set_pricing = function() { var set_pricing = function() {
@ -810,7 +845,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
in_list(['Purchase Order', 'Purchase Receipt', 'Purchase Invoice'], this.frm.doctype)) { in_list(['Purchase Order', 'Purchase Receipt', 'Purchase Invoice'], this.frm.doctype)) {
erpnext.utils.get_shipping_address(this.frm, function(){ erpnext.utils.get_shipping_address(this.frm, function(){
set_party_account(set_pricing); set_party_account(set_pricing);
}) });
// Get default company billing address in Purchase Invoice, Order and Receipt // Get default company billing address in Purchase Invoice, Order and Receipt
frappe.call({ frappe.call({
@ -1977,6 +2012,14 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
this.autofill_warehouse(this.frm.doc.items, "warehouse", this.frm.doc.set_warehouse); 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) { autofill_warehouse : function (child_table, warehouse_field, warehouse) {
if (warehouse && child_table && child_table.length) { if (warehouse && child_table && child_table.length) {
let doctype = child_table[0].doctype; let doctype = child_table[0].doctype;

View File

@ -276,6 +276,12 @@ erpnext.utils.validate_mandatory = function(frm, label, value, trigger_on) {
erpnext.utils.get_shipping_address = function(frm, callback){ erpnext.utils.get_shipping_address = function(frm, callback){
if (frm.doc.company) { 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({ frappe.call({
method: "erpnext.accounts.custom.address.get_shipping_address", method: "erpnext.accounts.custom.address.get_shipping_address",
args: { args: {

View File

@ -40,14 +40,12 @@ erpnext.setup_auto_gst_taxation = (doctype) => {
callback: function(r) { callback: function(r) {
if(r.message) { if(r.message) {
frm.set_value('taxes_and_charges', r.message.taxes_and_charges); 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); 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', []);
} }
} }
}); });
} }
}); });
}; }

View File

@ -171,7 +171,7 @@ def get_regional_address_details(party_details, doctype, company):
if is_internal_transfer(party_details, doctype): if is_internal_transfer(party_details, doctype):
party_details.taxes_and_charges = '' party_details.taxes_and_charges = ''
party_details.taxes = '' party_details.taxes = []
return party_details return party_details
if doctype in ("Sales Invoice", "Delivery Note", "Sales Order"): if doctype in ("Sales Invoice", "Delivery Note", "Sales Order"):

View File

@ -84,7 +84,10 @@ class Customer(TransactionBase):
frappe.throw(_("{0} is not a company bank account").format(frappe.bold(self.default_bank_account))) frappe.throw(_("{0} is not a company bank account").format(frappe.bold(self.default_bank_account)))
def validate_internal_customer(self): 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.throw(_("Internal Customer for company {0} already exists").format(
frappe.bold(self.represents_company))) frappe.bold(self.represents_company)))

View File

@ -171,8 +171,10 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
this.frm.add_custom_button(__('Request for Raw Materials'), () => this.make_raw_material_request(), __('Create')); 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')); this.frm.add_custom_button(__('Purchase Order'), () => this.make_purchase_order(), __('Create'));
}
// maintenance // maintenance
if(flt(doc.per_delivered, 2) < 100 && (order_is_maintenance || order_is_a_custom_sale)) { if(flt(doc.per_delivered, 2) < 100 && (order_is_maintenance || order_is_a_custom_sale)) {
@ -193,16 +195,15 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
if (doc.docstatus === 1 && !doc.inter_company_order_reference) { if (doc.docstatus === 1 && !doc.inter_company_order_reference) {
let me = this; let me = this;
frappe.model.with_doc("Customer", me.frm.doc.customer, () => { let internal = me.frm.doc.is_internal_customer;
let customer = frappe.model.get_doc("Customer", me.frm.doc.customer); if (internal) {
let internal = customer.is_internal_customer; let button_label = (me.frm.doc.company === me.frm.doc.represents_company) ? "Internal Purchase Order" :
let disabled = customer.disabled; "Inter Company Purchase Order";
if (internal === 1 && disabled === 0) {
me.frm.add_custom_button("Inter Company Order", function() { me.frm.add_custom_button(button_label, function() {
me.make_inter_company_order(); me.make_inter_company_order();
}, __('Create')); }, __('Create'));
} }
});
} }
} }
// payment request // payment request

View File

@ -107,6 +107,8 @@
"tc_name", "tc_name",
"terms", "terms",
"more_info", "more_info",
"is_internal_customer",
"represents_company",
"inter_company_order_reference", "inter_company_order_reference",
"project", "project",
"party_account_currency", "party_account_currency",
@ -1103,7 +1105,8 @@
"hide_days": 1, "hide_days": 1,
"hide_seconds": 1, "hide_seconds": 1,
"label": "Inter Company Order Reference", "label": "Inter Company Order Reference",
"options": "Purchase Order" "options": "Purchase Order",
"read_only": 1
}, },
{ {
"description": "Track this Sales Order against any Project", "description": "Track this Sales Order against any Project",
@ -1455,13 +1458,29 @@
"hide_seconds": 1, "hide_seconds": 1,
"label": "Skip Delivery Note", "label": "Skip Delivery Note",
"print_hide": 1 "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", "icon": "fa fa-file-text",
"idx": 105, "idx": 105,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2020-10-30 13:59:18.628077", "modified": "2021-01-20 23:40:39.929296",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Selling", "module": "Selling",
"name": "Sales Order", "name": "Sales Order",

View File

@ -95,15 +95,21 @@ frappe.ui.form.on("Delivery Note", {
frm.page.set_inner_btn_group_as_primary(__('Create')); frm.page.set_inner_btn_group_as_primary(__('Create'));
} }
if (frm.doc.docstatus === 1 && frm.doc.is_internal_customer && !frm.doc.inter_company_reference) { if (frm.doc.docstatus == 1 && !frm.doc.inter_company_reference) {
frm.add_custom_button(__('Purchase Receipt'), function() { 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({ frappe.model.open_mapped_doc({
method: 'erpnext.stock.doctype.delivery_note.delivery_note.make_inter_company_purchase_receipt', method: 'erpnext.stock.doctype.delivery_note.delivery_note.make_inter_company_purchase_receipt',
frm: frm, frm: frm,
}) });
}, __('Create')); }, __('Create'));
} }
} }
}
}); });
frappe.ui.form.on("Delivery Note Item", { frappe.ui.form.on("Delivery Note Item", {
@ -297,15 +303,6 @@ erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend(
} }
}) })
}, },
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})); $.extend(cur_frm.cscript, new erpnext.stock.DeliveryNoteController({frm: cur_frm}));

View File

@ -53,7 +53,7 @@
"sec_warehouse", "sec_warehouse",
"set_warehouse", "set_warehouse",
"col_break_warehouse", "col_break_warehouse",
"to_warehouse", "set_target_warehouse",
"items_section", "items_section",
"scan_barcode", "scan_barcode",
"items", "items",
@ -117,6 +117,7 @@
"source", "source",
"column_break5", "column_break5",
"is_internal_customer", "is_internal_customer",
"represents_company",
"inter_company_reference", "inter_company_reference",
"per_billed", "per_billed",
"customer_group", "customer_group",
@ -502,18 +503,6 @@
"fieldname": "col_break_warehouse", "fieldname": "col_break_warehouse",
"fieldtype": "Column Break" "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", "fieldname": "items_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
@ -1261,13 +1250,34 @@
"no_copy": 1, "no_copy": 1,
"print_hide": 1, "print_hide": 1,
"read_only": 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", "icon": "fa fa-truck",
"idx": 146, "idx": 146,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2020-11-30 12:54:45.407289", "modified": "2020-12-26 17:07:59.194403",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Delivery Note", "name": "Delivery Note",

View File

@ -664,7 +664,8 @@ def make_inter_company_purchase_receipt(source_name, target_doc=None):
return make_inter_company_transaction("Delivery Note", source_name, target_doc) return make_inter_company_transaction("Delivery Note", source_name, target_doc)
def make_inter_company_transaction(doctype, source_name, target_doc=None): 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': if doctype == 'Delivery Note':
source_doc = frappe.get_doc(doctype, source_name) source_doc = frappe.get_doc(doctype, source_name)
@ -682,6 +683,7 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
def set_missing_values(source, target): def set_missing_values(source, target):
target.run_method("set_missing_values") target.run_method("set_missing_values")
set_purchase_references(target)
if target.doctype == 'Purchase Receipt': if target.doctype == 'Purchase Receipt':
master_doctype = 'Purchase Taxes and Charges Template' master_doctype = 'Purchase Taxes and Charges Template'
@ -697,20 +699,34 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
if target_doc.doctype == 'Purchase Receipt': if target_doc.doctype == 'Purchase Receipt':
target_doc.company = details.get("company") target_doc.company = details.get("company")
target_doc.supplier = details.get("party") 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.buying_price_list = source_doc.selling_price_list
target_doc.is_internal_supplier = 1 target_doc.is_internal_supplier = 1
target_doc.inter_company_reference = source_doc.name 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: else:
target_doc.company = details.get("company") target_doc.company = details.get("company")
target_doc.customer = details.get("party") target_doc.customer = details.get("party")
target_doc.company_address = source_doc.supplier_address 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.selling_price_list = source_doc.buying_price_list
target_doc.is_internal_customer = 1 target_doc.is_internal_customer = 1
target_doc.inter_company_reference = source_doc.name target_doc.inter_company_reference = source_doc.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, { doclist = get_mapped_doc(doctype, source_name, {
doctype: { doctype: {
"doctype": target_doctype, "doctype": target_doctype,
@ -722,7 +738,10 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
doctype +" Item": { doctype +" Item": {
"doctype": target_doctype + " Item", "doctype": target_doctype + " Item",
"field_map": { "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": [ "field_no_map": [
"warehouse" "warehouse"

View File

@ -458,7 +458,7 @@
"fieldname": "warehouse", "fieldname": "warehouse",
"fieldtype": "Link", "fieldtype": "Link",
"in_list_view": 1, "in_list_view": 1,
"label": "From Warehouse", "label": "Warehouse",
"oldfieldname": "warehouse", "oldfieldname": "warehouse",
"oldfieldtype": "Link", "oldfieldtype": "Link",
"options": "Warehouse", "options": "Warehouse",
@ -467,11 +467,12 @@
"width": "100px" "width": "100px"
}, },
{ {
"depends_on": "eval:parent.is_internal_customer",
"fieldname": "target_warehouse", "fieldname": "target_warehouse",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 1, "hidden": 1,
"ignore_user_permissions": 1, "ignore_user_permissions": 1,
"label": "Customer Warehouse (Optional)", "label": "Target Warehouse",
"no_copy": 1, "no_copy": 1,
"options": "Warehouse", "options": "Warehouse",
"print_hide": 1 "print_hide": 1
@ -748,7 +749,7 @@
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2020-12-07 19:59:27.119856", "modified": "2020-12-26 17:31:27.029803",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Delivery Note Item", "name": "Delivery Note Item",

View File

@ -1,4 +1,5 @@
{ {
"actions": [],
"creation": "2013-02-22 01:28:02", "creation": "2013-02-22 01:28:02",
"doctype": "DocType", "doctype": "DocType",
"document_type": "Document", "document_type": "Document",
@ -29,6 +30,8 @@
"options": "Item", "options": "Item",
"read_only": 1, "read_only": 1,
"reqd": 1, "reqd": 1,
"show_days": 1,
"show_seconds": 1,
"width": "100px" "width": "100px"
}, },
{ {
@ -41,6 +44,8 @@
"print_width": "300px", "print_width": "300px",
"read_only": 1, "read_only": 1,
"reqd": 1, "reqd": 1,
"show_days": 1,
"show_seconds": 1,
"width": "120px" "width": "120px"
}, },
{ {
@ -50,7 +55,9 @@
"no_copy": 1, "no_copy": 1,
"options": "Purchase Invoice\nPurchase Receipt", "options": "Purchase Invoice\nPurchase Receipt",
"print_hide": 1, "print_hide": 1,
"read_only": 1 "read_only": 1,
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "receipt_document", "fieldname": "receipt_document",
@ -59,25 +66,33 @@
"no_copy": 1, "no_copy": 1,
"options": "receipt_document_type", "options": "receipt_document_type",
"print_hide": 1, "print_hide": 1,
"read_only": 1 "read_only": 1,
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "col_break2", "fieldname": "col_break2",
"fieldtype": "Column Break" "fieldtype": "Column Break",
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "qty", "fieldname": "qty",
"fieldtype": "Float", "fieldtype": "Float",
"in_list_view": 1, "in_list_view": 1,
"label": "Qty", "label": "Qty",
"read_only": 1 "read_only": 1,
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "rate", "fieldname": "rate",
"fieldtype": "Currency", "fieldtype": "Currency",
"label": "Rate", "label": "Rate",
"options": "Company:company:default_currency", "options": "Company:company:default_currency",
"read_only": 1 "read_only": 1,
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "amount", "fieldname": "amount",
@ -88,14 +103,19 @@
"oldfieldtype": "Currency", "oldfieldtype": "Currency",
"options": "Company:company:default_currency", "options": "Company:company:default_currency",
"read_only": 1, "read_only": 1,
"reqd": 1 "reqd": 1,
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "applicable_charges", "fieldname": "applicable_charges",
"fieldtype": "Currency", "fieldtype": "Currency",
"in_list_view": 1, "in_list_view": 1,
"label": "Applicable Charges", "label": "Applicable Charges",
"options": "Company:company:default_currency" "options": "Company:company:default_currency",
"read_only_depends_on": "eval:parent.distribute_charges_based_on != 'Distribute Manually'",
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "purchase_receipt_item", "fieldname": "purchase_receipt_item",
@ -104,22 +124,30 @@
"label": "Purchase Receipt Item", "label": "Purchase Receipt Item",
"no_copy": 1, "no_copy": 1,
"print_hide": 1, "print_hide": 1,
"read_only": 1 "read_only": 1,
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "cost_center", "fieldname": "cost_center",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Cost Center", "label": "Cost Center",
"options": "Cost Center" "options": "Cost Center",
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "accounting_dimensions_section", "fieldname": "accounting_dimensions_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Accounting Dimensions" "label": "Accounting Dimensions",
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "dimension_col_break", "fieldname": "dimension_col_break",
"fieldtype": "Column Break" "fieldtype": "Column Break",
"show_days": 1,
"show_seconds": 1
}, },
{ {
"default": "0", "default": "0",
@ -128,12 +156,15 @@
"fieldtype": "Check", "fieldtype": "Check",
"hidden": 1, "hidden": 1,
"label": "Is Fixed Asset", "label": "Is Fixed Asset",
"read_only": 1 "read_only": 1,
"show_days": 1,
"show_seconds": 1
} }
], ],
"idx": 1, "idx": 1,
"istable": 1, "istable": 1,
"modified": "2020-09-18 17:26:09.703215", "links": [],
"modified": "2021-01-25 23:09:23.322282",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Landed Cost Item", "name": "Landed Cost Item",

View File

@ -6,8 +6,11 @@
"engine": "InnoDB", "engine": "InnoDB",
"field_order": [ "field_order": [
"expense_account", "expense_account",
"account_currency",
"exchange_rate",
"description", "description",
"col_break3", "col_break3",
"base_amount",
"amount" "amount"
], ],
"fields": [ "fields": [
@ -28,7 +31,7 @@
"fieldtype": "Currency", "fieldtype": "Currency",
"in_list_view": 1, "in_list_view": 1,
"label": "Amount", "label": "Amount",
"options": "Company:company:default_currency", "options": "account_currency",
"reqd": 1 "reqd": 1
}, },
{ {
@ -38,13 +41,33 @@
"in_list_view": 1, "in_list_view": 1,
"label": "Expense Account", "label": "Expense Account",
"mandatory_depends_on": "eval:cint(erpnext.is_perpetual_inventory_enabled(parent.company))", "mandatory_depends_on": "eval:cint(erpnext.is_perpetual_inventory_enabled(parent.company))",
"options": "Account", "options": "Account"
"print_hide": 1 },
{
"fieldname": "account_currency",
"fieldtype": "Link",
"label": "Account Currency",
"options": "Currency",
"read_only": 1
},
{
"fieldname": "exchange_rate",
"fieldtype": "Float",
"label": "Exchange Rate",
"precision": "9"
},
{
"fieldname": "base_amount",
"fieldtype": "Currency",
"label": "Base Amount",
"options": "Company:company:default_currency",
"read_only": 1
} }
], ],
"index_web_pages_for_search": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2020-12-04 00:22:14.373312", "modified": "2020-12-26 01:07:23.233604",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Landed Cost Taxes and Charges", "name": "Landed Cost Taxes and Charges",

View File

@ -1,6 +1,7 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt // License: GNU General Public License v3. See license.txt
{% include 'erpnext/stock/landed_taxes_and_charges_common.js' %};
frappe.provide("erpnext.stock"); frappe.provide("erpnext.stock");
@ -29,20 +30,9 @@ erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({
this.frm.add_fetch("receipt_document", "supplier", "supplier"); this.frm.add_fetch("receipt_document", "supplier", "supplier");
this.frm.add_fetch("receipt_document", "posting_date", "posting_date"); this.frm.add_fetch("receipt_document", "posting_date", "posting_date");
this.frm.add_fetch("receipt_document", "base_grand_total", "grand_total"); this.frm.add_fetch("receipt_document", "base_grand_total", "grand_total");
this.frm.set_query("expense_account", "taxes", function() {
return {
query: "erpnext.controllers.queries.tax_account_query",
filters: {
"account_type": ["Tax", "Chargeable", "Income Account", "Expenses Included In Valuation", "Expenses Included In Asset Valuation"],
"company": me.frm.doc.company
}
};
});
}, },
refresh: function(frm) { refresh: function() {
var help_content = var help_content =
`<br><br> `<br><br>
<table class="table table-bordered" style="background-color: #f9f9f9;"> <table class="table table-bordered" style="background-color: #f9f9f9;">
@ -72,6 +62,11 @@ erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({
</table>`; </table>`;
set_field_options("landed_cost_help", help_content); set_field_options("landed_cost_help", help_content);
if (this.frm.doc.company) {
let company_currency = frappe.get_doc(":Company", this.frm.doc.company).default_currency;
this.frm.set_currency_labels(["total_taxes_and_charges"], company_currency);
}
}, },
get_items_from_purchase_receipts: function() { get_items_from_purchase_receipts: function() {
@ -97,18 +92,19 @@ erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({
set_total_taxes_and_charges: function() { set_total_taxes_and_charges: function() {
var total_taxes_and_charges = 0.0; var total_taxes_and_charges = 0.0;
$.each(this.frm.doc.taxes || [], function(i, d) { $.each(this.frm.doc.taxes || [], function(i, d) {
total_taxes_and_charges += flt(d.amount) total_taxes_and_charges += flt(d.base_amount);
}); });
cur_frm.set_value("total_taxes_and_charges", total_taxes_and_charges); this.frm.set_value("total_taxes_and_charges", total_taxes_and_charges);
}, },
set_applicable_charges_for_item: function() { set_applicable_charges_for_item: function() {
var me = this; var me = this;
if(this.frm.doc.taxes.length) { if(this.frm.doc.taxes.length) {
var total_item_cost = 0.0; var total_item_cost = 0.0;
var based_on = this.frm.doc.distribute_charges_based_on.toLowerCase(); var based_on = this.frm.doc.distribute_charges_based_on.toLowerCase();
if (based_on != 'distribute manually') {
$.each(this.frm.doc.items || [], function(i, d) { $.each(this.frm.doc.items || [], function(i, d) {
total_item_cost += flt(d[based_on]) total_item_cost += flt(d[based_on])
}); });
@ -126,6 +122,7 @@ erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({
} }
refresh_field("items"); refresh_field("items");
} }
}
}, },
distribute_charges_based_on: function (frm) { distribute_charges_based_on: function (frm) {
this.set_applicable_charges_for_item(); this.set_applicable_charges_for_item();
@ -134,7 +131,16 @@ erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({
items_remove: () => { items_remove: () => {
this.trigger('set_applicable_charges_for_item'); this.trigger('set_applicable_charges_for_item');
} }
}); });
cur_frm.script_manager.make(erpnext.stock.LandedCostVoucher); cur_frm.script_manager.make(erpnext.stock.LandedCostVoucher);
frappe.ui.form.on('Landed Cost Taxes and Charges', {
expense_account: function(frm, cdt, cdn) {
frm.events.set_account_currency(frm, cdt, cdn);
},
amount: function(frm, cdt, cdn) {
frm.events.set_base_amount(frm, cdt, cdn);
}
});

View File

@ -1,4 +1,5 @@
{ {
"actions": [],
"autoname": "naming_series:", "autoname": "naming_series:",
"creation": "2014-07-11 11:33:42.547339", "creation": "2014-07-11 11:33:42.547339",
"doctype": "DocType", "doctype": "DocType",
@ -7,6 +8,9 @@
"field_order": [ "field_order": [
"naming_series", "naming_series",
"company", "company",
"column_break_2",
"posting_date",
"section_break_5",
"purchase_receipts", "purchase_receipts",
"purchase_receipt_items", "purchase_receipt_items",
"get_items_from_purchase_receipts", "get_items_from_purchase_receipts",
@ -30,7 +34,9 @@
"options": "MAT-LCV-.YYYY.-", "options": "MAT-LCV-.YYYY.-",
"print_hide": 1, "print_hide": 1,
"reqd": 1, "reqd": 1,
"set_only_once": 1 "set_only_once": 1,
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "company", "fieldname": "company",
@ -40,24 +46,32 @@
"label": "Company", "label": "Company",
"options": "Company", "options": "Company",
"remember_last_selected_value": 1, "remember_last_selected_value": 1,
"reqd": 1 "reqd": 1,
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "purchase_receipts", "fieldname": "purchase_receipts",
"fieldtype": "Table", "fieldtype": "Table",
"label": "Purchase Receipts", "label": "Purchase Receipts",
"options": "Landed Cost Purchase Receipt", "options": "Landed Cost Purchase Receipt",
"reqd": 1 "reqd": 1,
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "purchase_receipt_items", "fieldname": "purchase_receipt_items",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Purchase Receipt Items" "label": "Purchase Receipt Items",
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "get_items_from_purchase_receipts", "fieldname": "get_items_from_purchase_receipts",
"fieldtype": "Button", "fieldtype": "Button",
"label": "Get Items From Purchase Receipts" "label": "Get Items From Purchase Receipts",
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "items", "fieldname": "items",
@ -65,42 +79,56 @@
"label": "Purchase Receipt Items", "label": "Purchase Receipt Items",
"no_copy": 1, "no_copy": 1,
"options": "Landed Cost Item", "options": "Landed Cost Item",
"reqd": 1 "reqd": 1,
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "sec_break1", "fieldname": "sec_break1",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Applicable Charges" "label": "Applicable Charges",
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "taxes", "fieldname": "taxes",
"fieldtype": "Table", "fieldtype": "Table",
"label": "Taxes and Charges", "label": "Taxes and Charges",
"options": "Landed Cost Taxes and Charges", "options": "Landed Cost Taxes and Charges",
"reqd": 1 "reqd": 1,
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "section_break_9", "fieldname": "section_break_9",
"fieldtype": "Section Break" "fieldtype": "Section Break",
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "total_taxes_and_charges", "fieldname": "total_taxes_and_charges",
"fieldtype": "Currency", "fieldtype": "Currency",
"label": "Total Taxes and Charges", "label": "Total Taxes and Charges (Company Currency)",
"options": "Company:company:default_currency", "options": "Company:company:default_currency",
"read_only": 1, "read_only": 1,
"reqd": 1 "reqd": 1,
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "col_break1", "fieldname": "col_break1",
"fieldtype": "Column Break" "fieldtype": "Column Break",
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "distribute_charges_based_on", "fieldname": "distribute_charges_based_on",
"fieldtype": "Select", "fieldtype": "Select",
"label": "Distribute Charges Based On", "label": "Distribute Charges Based On",
"options": "Qty\nAmount", "options": "Qty\nAmount\nDistribute Manually",
"reqd": 1 "reqd": 1,
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "amended_from", "fieldname": "amended_from",
@ -109,21 +137,51 @@
"no_copy": 1, "no_copy": 1,
"options": "Landed Cost Voucher", "options": "Landed Cost Voucher",
"print_hide": 1, "print_hide": 1,
"read_only": 1 "read_only": 1,
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "sec_break2", "fieldname": "sec_break2",
"fieldtype": "Section Break" "fieldtype": "Section Break",
"show_days": 1,
"show_seconds": 1
}, },
{ {
"fieldname": "landed_cost_help", "fieldname": "landed_cost_help",
"fieldtype": "HTML", "fieldtype": "HTML",
"label": "Landed Cost Help" "label": "Landed Cost Help",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "column_break_2",
"fieldtype": "Column Break",
"show_days": 1,
"show_seconds": 1
},
{
"default": "Today",
"fieldname": "posting_date",
"fieldtype": "Date",
"label": "Posting Date",
"reqd": 1,
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "section_break_5",
"fieldtype": "Section Break",
"hide_border": 1,
"show_days": 1,
"show_seconds": 1
} }
], ],
"icon": "icon-usd", "icon": "icon-usd",
"index_web_pages_for_search": 1,
"is_submittable": 1, "is_submittable": 1,
"modified": "2019-11-21 15:34:10.846093", "links": [],
"modified": "2021-01-25 23:07:30.468423",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Landed Cost Voucher", "name": "Landed Cost Voucher",

View File

@ -9,6 +9,7 @@ from frappe.model.meta import get_field_precision
from frappe.model.document import Document from frappe.model.document import Document
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
from erpnext.accounts.doctype.account.account import get_account_currency from erpnext.accounts.doctype.account.account import get_account_currency
from erpnext.controllers.taxes_and_totals import init_landed_taxes_and_totals
class LandedCostVoucher(Document): class LandedCostVoucher(Document):
def get_items_from_purchase_receipts(self): def get_items_from_purchase_receipts(self):
@ -39,13 +40,15 @@ class LandedCostVoucher(Document):
def validate(self): def validate(self):
self.check_mandatory() self.check_mandatory()
self.validate_purchase_receipts()
init_landed_taxes_and_totals(self)
self.set_total_taxes_and_charges()
if not self.get("items"): if not self.get("items"):
self.get_items_from_purchase_receipts() self.get_items_from_purchase_receipts()
else:
self.set_applicable_charges_on_item()
self.validate_applicable_charges_for_item() self.validate_applicable_charges_for_item()
self.validate_purchase_receipts()
self.validate_expense_accounts()
self.set_total_taxes_and_charges()
def check_mandatory(self): def check_mandatory(self):
if not self.get("purchase_receipts"): if not self.get("purchase_receipts"):
@ -73,21 +76,37 @@ class LandedCostVoucher(Document):
frappe.throw(_("Row {0}: Cost center is required for an item {1}") frappe.throw(_("Row {0}: Cost center is required for an item {1}")
.format(item.idx, item.item_code)) .format(item.idx, item.item_code))
def validate_expense_accounts(self):
company_currency = erpnext.get_company_currency(self.company)
for account in self.taxes:
if get_account_currency(account.expense_account) != company_currency:
frappe.throw(_("Row {}: Expense account currency should be same as company's default currency.").format(account.idx)
+ _("Please select expense account with account currency as {}.").format(frappe.bold(company_currency)),
title=_("Invalid Account Currency"))
def set_total_taxes_and_charges(self): def set_total_taxes_and_charges(self):
self.total_taxes_and_charges = sum([flt(d.amount) for d in self.get("taxes")]) self.total_taxes_and_charges = sum([flt(d.base_amount) for d in self.get("taxes")])
def set_applicable_charges_on_item(self):
if self.get('taxes') and self.distribute_charges_based_on != 'Distribute Manually':
total_item_cost = 0.0
total_charges = 0.0
item_count = 0
based_on_field = frappe.scrub(self.distribute_charges_based_on)
for item in self.get('items'):
total_item_cost += item.get(based_on_field)
for item in self.get('items'):
item.applicable_charges = flt(flt(item.get(based_on_field)) * (flt(self.total_taxes_and_charges) / flt(total_item_cost)),
item.precision('applicable_charges'))
total_charges += item.applicable_charges
item_count += 1
if total_charges != self.total_taxes_and_charges:
diff = self.total_taxes_and_charges - total_charges
self.get('items')[item_count - 1].applicable_charges += diff
def validate_applicable_charges_for_item(self): def validate_applicable_charges_for_item(self):
based_on = self.distribute_charges_based_on.lower() based_on = self.distribute_charges_based_on.lower()
if based_on != 'distribute manually':
total = sum([flt(d.get(based_on)) for d in self.get("items")]) total = sum([flt(d.get(based_on)) for d in self.get("items")])
else:
# consider for proportion while distributing manually
total = sum([flt(d.get('applicable_charges')) for d in self.get("items")])
if not total: if not total:
frappe.throw(_("Total {0} for all items is zero, may be you should change 'Distribute Charges Based On'").format(based_on)) frappe.throw(_("Total {0} for all items is zero, may be you should change 'Distribute Charges Based On'").format(based_on))
@ -153,13 +172,13 @@ class LandedCostVoucher(Document):
docs = frappe.db.get_all('Asset', filters={ receipt_document_type: item.receipt_document, docs = frappe.db.get_all('Asset', filters={ receipt_document_type: item.receipt_document,
'item_code': item.item_code }, fields=['name', 'docstatus']) 'item_code': item.item_code }, fields=['name', 'docstatus'])
if not docs or len(docs) != item.qty: if not docs or len(docs) != item.qty:
frappe.throw(_('There are not enough asset created or linked to {0}.').format(item.receipt_document) frappe.throw(_('There are not enough asset created or linked to {0}. Please create or link {1} Assets with respective document.').format(
+ _('Please create or link {0} Assets with respective document.').format(item.qty)) item.receipt_document, item.qty))
if docs: if docs:
for d in docs: for d in docs:
if d.docstatus == 1: if d.docstatus == 1:
frappe.throw(_('{0} {1} has submitted Assets. Remove Item {2} from table to continue.') frappe.throw(_('{2} <b>{0}</b> has submitted Assets. Remove Item <b>{1}</b> from table to continue.').format(
.format(item.receipt_document_type, frappe.bold(item.receipt_document), frappe.bold(item.item_code))) item.receipt_document, item.item_code, item.receipt_document_type))
def update_rate_in_serial_no_for_non_asset_items(self, receipt_document): def update_rate_in_serial_no_for_non_asset_items(self, receipt_document):
for item in receipt_document.get("items"): for item in receipt_document.get("items"):

View File

@ -10,6 +10,7 @@ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt \
import get_gl_entries, test_records as pr_test_records, make_purchase_receipt import get_gl_entries, test_records as pr_test_records, make_purchase_receipt
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
from erpnext.accounts.doctype.account.test_account import get_inventory_account from erpnext.accounts.doctype.account.test_account import get_inventory_account
from erpnext.accounts.doctype.account.test_account import create_account
class TestLandedCostVoucher(unittest.TestCase): class TestLandedCostVoucher(unittest.TestCase):
def test_landed_cost_voucher(self): def test_landed_cost_voucher(self):
@ -162,8 +163,8 @@ class TestLandedCostVoucher(unittest.TestCase):
lcv = create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company, 123.22) lcv = create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company, 123.22)
self.assertEqual(lcv.items[0].applicable_charges, 41.07) self.assertEqual(flt(lcv.items[0].applicable_charges, 2), 41.07)
self.assertEqual(lcv.items[2].applicable_charges, 41.08) self.assertEqual(flt(lcv.items[2].applicable_charges, 2), 41.08)
def test_multiple_landed_cost_voucher_against_pr(self): def test_multiple_landed_cost_voucher_against_pr(self):
pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1", pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1",
@ -206,6 +207,46 @@ class TestLandedCostVoucher(unittest.TestCase):
self.assertEqual(pr.items[0].landed_cost_voucher_amount, 100) self.assertEqual(pr.items[0].landed_cost_voucher_amount, 100)
self.assertEqual(pr.items[1].landed_cost_voucher_amount, 100) self.assertEqual(pr.items[1].landed_cost_voucher_amount, 100)
def test_multi_currency_lcv(self):
## Create USD Shipping charges_account
usd_shipping = create_account(account_name="Shipping Charges USD",
parent_account="Duties and Taxes - TCP1", company="_Test Company with perpetual inventory",
account_currency="USD")
pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1",
supplier_warehouse = "Stores - TCP1")
pr.submit()
lcv = make_landed_cost_voucher(company = pr.company, receipt_document_type = "Purchase Receipt",
receipt_document=pr.name, charges=100, do_not_save=True)
lcv.append("taxes", {
"description": "Shipping Charges",
"expense_account": usd_shipping,
"amount": 10
})
lcv.save()
lcv.submit()
pr.load_from_db()
# Considering exchange rate from USD to INR as 62.9
self.assertEqual(lcv.total_taxes_and_charges, 729)
self.assertEqual(pr.items[0].landed_cost_voucher_amount, 729)
gl_entries = frappe.get_all("GL Entry", fields=["account", "credit", "credit_in_account_currency"],
filters={"voucher_no": pr.name, "account": ("in", ["Shipping Charges USD - TCP1", "Expenses Included In Valuation - TCP1"])})
expected_gl_entries = {
"Shipping Charges USD - TCP1": [629, 10],
"Expenses Included In Valuation - TCP1": [100, 100]
}
for entry in gl_entries:
amounts = expected_gl_entries.get(entry.account)
self.assertEqual(entry.credit, amounts[0])
self.assertEqual(entry.credit_in_account_currency, amounts[1])
def make_landed_cost_voucher(** args): def make_landed_cost_voucher(** args):
args = frappe._dict(args) args = frappe._dict(args)
ref_doc = frappe.get_doc(args.receipt_document_type, args.receipt_document) ref_doc = frappe.get_doc(args.receipt_document_type, args.receipt_document)

View File

@ -48,6 +48,7 @@
"set_warehouse", "set_warehouse",
"rejected_warehouse", "rejected_warehouse",
"col_break_warehouse", "col_break_warehouse",
"set_from_warehouse",
"is_subcontracted", "is_subcontracted",
"supplier_warehouse", "supplier_warehouse",
"items_section", "items_section",
@ -115,6 +116,7 @@
"per_returned", "per_returned",
"is_internal_supplier", "is_internal_supplier",
"inter_company_reference", "inter_company_reference",
"represents_company",
"subscription_detail", "subscription_detail",
"auto_repeat", "auto_repeat",
"printing_settings", "printing_settings",
@ -1087,7 +1089,9 @@
"fieldname": "inter_company_reference", "fieldname": "inter_company_reference",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Inter Company Reference", "label": "Inter Company Reference",
"no_copy": 1,
"options": "Delivery Note", "options": "Delivery Note",
"print_hide": 1,
"read_only": 1 "read_only": 1
}, },
{ {
@ -1121,13 +1125,29 @@
"no_copy": 1, "no_copy": 1,
"print_hide": 1, "print_hide": 1,
"read_only": 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", "icon": "fa fa-truck",
"idx": 261, "idx": 261,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2020-12-08 18:31:32.234503", "modified": "2020-12-26 20:49:39.106049",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Purchase Receipt", "name": "Purchase Receipt",

View File

@ -288,12 +288,15 @@ class PurchaseReceipt(BuyingController):
# Amount added through landed-cost-voucher # Amount added through landed-cost-voucher
if d.landed_cost_voucher_amount and landed_cost_entries: if d.landed_cost_voucher_amount and landed_cost_entries:
for account, amount in iteritems(landed_cost_entries[(d.item_code, d.name)]): for account, amount in iteritems(landed_cost_entries[(d.item_code, d.name)]):
account_currency = get_account_currency(account)
gl_entries.append(self.get_gl_dict({ gl_entries.append(self.get_gl_dict({
"account": account, "account": account,
"account_currency": account_currency,
"against": warehouse_account[d.warehouse]["account"], "against": warehouse_account[d.warehouse]["account"],
"cost_center": d.cost_center, "cost_center": d.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"), "remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"credit": flt(amount), "credit": flt(amount["base_amount"]),
"credit_in_account_currency": flt(amount["amount"]),
"project": d.project "project": d.project
}, item=d)) }, item=d))
@ -728,7 +731,13 @@ def get_item_account_wise_additional_cost(purchase_document):
for lcv in landed_cost_vouchers: for lcv in landed_cost_vouchers:
landed_cost_voucher_doc = frappe.get_doc("Landed Cost Voucher", lcv.parent) landed_cost_voucher_doc = frappe.get_doc("Landed Cost Voucher", lcv.parent)
#Use amount field for total item cost for manually cost distributed LCVs
if landed_cost_voucher_doc.distribute_charges_based_on == 'Distribute Manually':
based_on_field = 'amount'
else:
based_on_field = frappe.scrub(landed_cost_voucher_doc.distribute_charges_based_on) based_on_field = frappe.scrub(landed_cost_voucher_doc.distribute_charges_based_on)
total_item_cost = 0 total_item_cost = 0
for item in landed_cost_voucher_doc.items: for item in landed_cost_voucher_doc.items:
@ -738,9 +747,16 @@ def get_item_account_wise_additional_cost(purchase_document):
if item.receipt_document == purchase_document: if item.receipt_document == purchase_document:
for account in landed_cost_voucher_doc.taxes: for account in landed_cost_voucher_doc.taxes:
item_account_wise_cost.setdefault((item.item_code, item.purchase_receipt_item), {}) item_account_wise_cost.setdefault((item.item_code, item.purchase_receipt_item), {})
item_account_wise_cost[(item.item_code, item.purchase_receipt_item)].setdefault(account.expense_account, 0.0) item_account_wise_cost[(item.item_code, item.purchase_receipt_item)].setdefault(account.expense_account, {
item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][account.expense_account] += \ "amount": 0.0,
"base_amount": 0.0
})
item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][account.expense_account]["amount"] += \
account.amount * item.get(based_on_field) / total_item_cost account.amount * item.get(based_on_field) / total_item_cost
item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][account.expense_account]["base_amount"] += \
account.base_amount * item.get(based_on_field) / total_item_cost
return item_account_wise_cost return item_account_wise_cost

View File

@ -76,6 +76,7 @@
"purchase_order_item", "purchase_order_item",
"material_request_item", "material_request_item",
"purchase_receipt_item", "purchase_receipt_item",
"delivery_note_item",
"putaway_rule", "putaway_rule",
"section_break_45", "section_break_45",
"allow_zero_valuation_rate", "allow_zero_valuation_rate",
@ -819,11 +820,12 @@
"read_only": 1 "read_only": 1
}, },
{ {
"depends_on": "eval:parent.is_internal_supplier",
"fieldname": "from_warehouse", "fieldname": "from_warehouse",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 1, "hidden": 1,
"ignore_user_permissions": 1, "ignore_user_permissions": 1,
"label": "Supplier Warehouse", "label": "From Warehouse",
"options": "Warehouse" "options": "Warehouse"
}, },
{ {
@ -871,12 +873,20 @@
"fieldtype": "Float", "fieldtype": "Float",
"label": "Received Qty in Stock UOM", "label": "Received Qty in Stock UOM",
"print_hide": 1 "print_hide": 1
},
{
"fieldname": "delivery_note_item",
"fieldtype": "Data",
"label": "Delivery Note Item",
"no_copy": 1,
"print_hide": 1,
"read_only": 1
} }
], ],
"idx": 1, "idx": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2020-12-09 10:00:38.204294", "modified": "2020-12-26 16:50:56.479347",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Purchase Receipt Item", "name": "Purchase Receipt Item",

View File

@ -3,6 +3,8 @@
frappe.provide("erpnext.stock"); frappe.provide("erpnext.stock");
frappe.provide("erpnext.accounts.dimensions"); frappe.provide("erpnext.accounts.dimensions");
{% include 'erpnext/stock/landed_taxes_and_charges_common.js' %};
frappe.ui.form.on('Stock Entry', { frappe.ui.form.on('Stock Entry', {
setup: function(frm) { setup: function(frm) {
frm.set_indicator_formatter('item_code', function(doc) { frm.set_indicator_formatter('item_code', function(doc) {
@ -95,15 +97,6 @@ frappe.ui.form.on('Stock Entry', {
} }
}); });
frm.set_query("expense_account", "additional_costs", function() {
return {
query: "erpnext.controllers.queries.tax_account_query",
filters: {
"account_type": ["Tax", "Chargeable", "Income Account", "Expenses Included In Valuation", "Expenses Included In Asset Valuation"],
"company": frm.doc.company
}
};
});
frm.add_fetch("bom_no", "inspection_required", "inspection_required"); frm.add_fetch("bom_no", "inspection_required", "inspection_required");
erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype); erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
@ -559,7 +552,7 @@ frappe.ui.form.on('Stock Entry', {
calculate_total_additional_costs: function(frm) { calculate_total_additional_costs: function(frm) {
const total_additional_costs = frappe.utils.sum( const total_additional_costs = frappe.utils.sum(
(frm.doc.additional_costs || []).map(function(c) { return flt(c.amount); }) (frm.doc.additional_costs || []).map(function(c) { return flt(c.base_amount); })
); );
frm.set_value("total_additional_costs", frm.set_value("total_additional_costs",
@ -738,9 +731,19 @@ var validate_sample_quantity = function(frm, cdt, cdn) {
}; };
frappe.ui.form.on('Landed Cost Taxes and Charges', { frappe.ui.form.on('Landed Cost Taxes and Charges', {
amount: function(frm) { amount: function(frm, cdt, cdn) {
frm.events.set_base_amount(frm, cdt, cdn);
// Adding this check because same table in used in LCV
// This causes an error if you try to post an LCV immediately after a Stock Entry
if (frm.doc.doctype == 'Stock Entry') {
frm.events.calculate_amount(frm); frm.events.calculate_amount(frm);
} }
},
expense_account: function(frm, cdt, cdn) {
frm.events.set_account_currency(frm, cdt, cdn);
}
}); });
erpnext.stock.StockEntry = erpnext.stock.StockController.extend({ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({

View File

@ -19,6 +19,7 @@ from frappe.model.mapper import get_mapped_doc
from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit, get_serial_nos from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit, get_serial_nos
from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import OpeningEntryAccountError from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import OpeningEntryAccountError
from erpnext.accounts.general_ledger import process_gl_map from erpnext.accounts.general_ledger import process_gl_map
from erpnext.controllers.taxes_and_totals import init_landed_taxes_and_totals
import json import json
from six import string_types, itervalues, iteritems from six import string_types, itervalues, iteritems
@ -195,7 +196,7 @@ class StockEntry(StockController):
and (sed.t_warehouse is null or sed.t_warehouse = '')""", self.project, as_list=1) and (sed.t_warehouse is null or sed.t_warehouse = '')""", self.project, as_list=1)
amount = amount[0][0] if amount else 0 amount = amount[0][0] if amount else 0
additional_costs = frappe.db.sql(""" select ifnull(sum(sed.amount), 0) additional_costs = frappe.db.sql(""" select ifnull(sum(sed.base_amount), 0)
from from
`tabStock Entry` se, `tabLanded Cost Taxes and Charges` sed `tabStock Entry` se, `tabLanded Cost Taxes and Charges` sed
where where
@ -445,6 +446,7 @@ class StockEntry(StockController):
def calculate_rate_and_amount(self, reset_outgoing_rate=True, raise_error_if_no_rate=True): def calculate_rate_and_amount(self, reset_outgoing_rate=True, raise_error_if_no_rate=True):
self.set_basic_rate(reset_outgoing_rate, raise_error_if_no_rate) self.set_basic_rate(reset_outgoing_rate, raise_error_if_no_rate)
init_landed_taxes_and_totals(self)
self.distribute_additional_costs() self.distribute_additional_costs()
self.update_valuation_rate() self.update_valuation_rate()
self.set_total_incoming_outgoing_value() self.set_total_incoming_outgoing_value()
@ -533,7 +535,7 @@ class StockEntry(StockController):
if not any([d.item_code for d in self.items if d.t_warehouse]): if not any([d.item_code for d in self.items if d.t_warehouse]):
self.additional_costs = [] self.additional_costs = []
self.total_additional_costs = sum([flt(t.amount) for t in self.get("additional_costs")]) self.total_additional_costs = sum([flt(t.base_amount) for t in self.get("additional_costs")])
if self.purpose in ("Repack", "Manufacture"): if self.purpose in ("Repack", "Manufacture"):
incoming_items_cost = sum([flt(t.basic_amount) for t in self.get("items") if t.is_finished_item]) incoming_items_cost = sum([flt(t.basic_amount) for t in self.get("items") if t.is_finished_item])
@ -773,13 +775,19 @@ class StockEntry(StockController):
for d in self.get("items"): for d in self.get("items"):
if d.t_warehouse: if d.t_warehouse:
item_account_wise_additional_cost.setdefault((d.item_code, d.name), {}) item_account_wise_additional_cost.setdefault((d.item_code, d.name), {})
item_account_wise_additional_cost[(d.item_code, d.name)].setdefault(t.expense_account, 0.0) item_account_wise_additional_cost[(d.item_code, d.name)].setdefault(t.expense_account, {
"amount": 0.0,
"base_amount": 0.0
})
multiply_based_on = d.basic_amount if total_basic_amount else d.qty multiply_based_on = d.basic_amount if total_basic_amount else d.qty
item_account_wise_additional_cost[(d.item_code, d.name)][t.expense_account] += \ item_account_wise_additional_cost[(d.item_code, d.name)][t.expense_account]["amount"] += \
flt(t.amount * multiply_based_on) / divide_based_on flt(t.amount * multiply_based_on) / divide_based_on
item_account_wise_additional_cost[(d.item_code, d.name)][t.expense_account]["base_amount"] += \
flt(t.base_amount * multiply_based_on) / divide_based_on
if item_account_wise_additional_cost: if item_account_wise_additional_cost:
for d in self.get("items"): for d in self.get("items"):
for account, amount in iteritems(item_account_wise_additional_cost.get((d.item_code, d.name), {})): for account, amount in iteritems(item_account_wise_additional_cost.get((d.item_code, d.name), {})):
@ -790,7 +798,8 @@ class StockEntry(StockController):
"against": d.expense_account, "against": d.expense_account,
"cost_center": d.cost_center, "cost_center": d.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"), "remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"credit": amount "credit_in_account_currency": flt(amount["amount"]),
"credit": flt(amount["base_amount"])
}, item=d)) }, item=d))
gl_entries.append(self.get_gl_dict({ gl_entries.append(self.get_gl_dict({
@ -798,7 +807,7 @@ class StockEntry(StockController):
"against": account, "against": account,
"cost_center": d.cost_center, "cost_center": d.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"), "remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"credit": -1 * amount # put it as negative credit instead of debit purposefully "credit": -1 * amount['base_amount'] # put it as negative credit instead of debit purposefully
}, item=d)) }, item=d))
return process_gl_map(gl_entries) return process_gl_map(gl_entries)

View File

@ -0,0 +1,62 @@
let document_list = ['Landed Cost Voucher', 'Stock Entry'];
document_list.forEach((doctype) => {
frappe.ui.form.on(doctype, {
refresh: function(frm) {
let tax_field = frm.doc.doctype == 'Landed Cost Voucher' ? 'taxes' : 'additional_costs';
frm.set_query("expense_account", tax_field, function() {
return {
filters: {
"account_type": ['in', ["Tax", "Chargeable", "Income Account", "Expenses Included In Valuation", "Expenses Included In Asset Valuation"]],
"company": frm.doc.company
}
};
});
},
set_account_currency: function(frm, cdt, cdn) {
let row = locals[cdt][cdn];
if (row.expense_account) {
frappe.db.get_value('Account', row.expense_account, 'account_currency', function(value) {
frappe.model.set_value(cdt, cdn, "account_currency", value.account_currency);
frm.events.set_exchange_rate(frm, cdt, cdn);
});
}
},
set_exchange_rate: function(frm, cdt, cdn) {
let row = locals[cdt][cdn];
let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
if (row.account_currency == company_currency) {
row.exchange_rate = 1;
frm.set_df_property('taxes', 'hidden', 1, row.name, 'exchange_rate');
} else if (!row.exchange_rate || row.exchange_rate == 1) {
frm.set_df_property('taxes', 'hidden', 0, row.name, 'exchange_rate');
frappe.call({
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_exchange_rate",
args: {
posting_date: frm.doc.posting_date,
account: row.expense_account,
account_currency: row.account_currency,
company: frm.doc.company
},
callback: function(r) {
if (r.message) {
frappe.model.set_value(cdt, cdn, "exchange_rate", r.message);
}
}
});
}
frm.refresh_field('taxes');
},
set_base_amount: function(frm, cdt, cdn) {
let row = locals[cdt][cdn];
frappe.model.set_value(cdt, cdn, "base_amount",
flt(flt(row.amount)*row.exchange_rate, precision("base_amount", row)));
}
});
});

View File

@ -487,7 +487,6 @@ class update_entries_after(object):
self.wh_data.valuation_rate = new_stock_value / new_stock_qty self.wh_data.valuation_rate = new_stock_value / new_stock_qty
else: else:
self.wh_data.valuation_rate = sle.outgoing_rate self.wh_data.valuation_rate = sle.outgoing_rate
else: else:
if flt(self.wh_data.qty_after_transaction) >= 0 and sle.outgoing_rate: if flt(self.wh_data.qty_after_transaction) >= 0 and sle.outgoing_rate:
self.wh_data.valuation_rate = sle.outgoing_rate self.wh_data.valuation_rate = sle.outgoing_rate