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_type = kwargs.get('account_type'),
parent_account = kwargs.get('parent_account'),
company = kwargs.get('company')
company = kwargs.get('company'),
account_currency = kwargs.get('account_currency')
))
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' \
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
be used in transactions""").format(self.voucher_type, self.voucher_no, frappe.bold(self.cost_center)))
frappe.throw(_("""{0} {1}: Cost Center {2} is a group cost center and group cost centers cannot be used in transactions""").format(
self.voucher_type, self.voucher_no, frappe.bold(self.cost_center)))
def validate_party(self):
validate_party_frozen_disabled(self.party_type, self.party)
@ -170,7 +170,7 @@ class GLEntry(Document):
account_currency = get_account_currency(self.account)
if not self.account_currency:
self.account_currency = company_currency
self.account_currency = account_currency or company_currency
if account_currency != self.account_currency:
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() {
var me = this;
if(this.frm.updating_party_details)
// Do not update if inter company reference is there as the details will already be updated
if(this.frm.updating_party_details || this.frm.doc.inter_company_invoice_reference)
return;
erpnext.utils.get_party_details(this.frm, "erpnext.accounts.party.get_party_details",
{
posting_date: this.frm.doc.posting_date,

View File

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

View File

@ -480,7 +480,7 @@ class PurchaseInvoice(BuyingController):
grand_total = self.rounded_total if (self.rounding_adjustment and self.rounded_total) else self.grand_total
if grand_total and not self.is_internal_transfer():
# Didnot use base_grand_total to book rounding loss gle
# Did not use base_grand_total to book rounding loss gle
grand_total_in_company_currency = flt(grand_total * self.conversion_rate,
self.precision("grand_total"))
gl_entries.append(
@ -511,8 +511,8 @@ class PurchaseInvoice(BuyingController):
voucher_wise_stock_value = {}
if self.update_stock:
for d in frappe.get_all('Stock Ledger Entry',
fields = ["voucher_detail_no", "stock_value_difference"], filters={'voucher_no': self.name}):
voucher_wise_stock_value.setdefault(d.voucher_detail_no, d.stock_value_difference)
fields = ["voucher_detail_no", "stock_value_difference", "warehouse"], filters={'voucher_no': self.name}):
voucher_wise_stock_value.setdefault((d.voucher_detail_no, d.warehouse), d.stock_value_difference)
valuation_tax_accounts = [d.account_head for d in self.get("taxes")
if d.category in ('Valuation', 'Total and Valuation')
@ -563,16 +563,17 @@ class PurchaseInvoice(BuyingController):
)
else:
gl_entries.append(
self.get_gl_dict({
"account": item.expense_account,
"against": self.supplier,
"debit": warehouse_debit_amount,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"cost_center": item.cost_center,
"project": item.project or self.project
}, account_currency, item=item)
)
if not self.is_internal_transfer():
gl_entries.append(
self.get_gl_dict({
"account": item.expense_account,
"against": self.supplier,
"debit": warehouse_debit_amount,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"cost_center": item.cost_center,
"project": item.project or self.project
}, account_currency, item=item)
)
# Amount added through landed-cost-voucher
if landed_cost_entries:
@ -582,7 +583,8 @@ class PurchaseInvoice(BuyingController):
"against": item.expense_account,
"cost_center": item.cost_center,
"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
}, item=item))
@ -624,13 +626,14 @@ class PurchaseInvoice(BuyingController):
if expense_booked_in_pr:
expense_account = service_received_but_not_billed_account
gl_entries.append(self.get_gl_dict({
"account": expense_account,
"against": self.supplier,
"debit": amount,
"cost_center": item.cost_center,
"project": item.project or self.project
}, account_currency, item=item))
if not self.is_internal_transfer():
gl_entries.append(self.get_gl_dict({
"account": expense_account,
"against": self.supplier,
"debit": amount,
"cost_center": item.cost_center,
"project": item.project or self.project
}, account_currency, item=item))
# If asset is bought through this document and not linked to PR
if self.update_stock and item.landed_cost_voucher_amount:
@ -795,10 +798,10 @@ class PurchaseInvoice(BuyingController):
# Stock ledger value is not matching with the warehouse amount
if (self.update_stock and voucher_wise_stock_value.get(item.name) and
warehouse_debit_amount != flt(voucher_wise_stock_value.get(item.name), net_amt_precision)):
warehouse_debit_amount != flt(voucher_wise_stock_value.get((item.name, item.warehouse)), net_amt_precision)):
cost_of_goods_sold_account = self.get_company_default("default_expense_account")
stock_amount = flt(voucher_wise_stock_value.get(item.name), net_amt_precision)
stock_amount = flt(voucher_wise_stock_value.get((item.name, item.warehouse)), net_amt_precision)
stock_adjustment_amt = warehouse_debit_amount - stock_amount
gl_entries.append(

View File

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

View File

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

View File

@ -60,6 +60,8 @@
"ignore_pricing_rule",
"sec_warehouse",
"set_warehouse",
"column_break_55",
"set_target_warehouse",
"items_section",
"update_stock",
"scan_barcode",
@ -1969,13 +1971,24 @@
"label": "Represents Company",
"options": "Company",
"read_only": 1
},
{
"fieldname": "column_break_55",
"fieldtype": "Column Break"
},
{
"depends_on": "eval: doc.is_internal_customer && doc.update_stock",
"fieldname": "set_target_warehouse",
"fieldtype": "Link",
"label": "Set Target Warehouse",
"options": "Warehouse"
}
],
"icon": "fa fa-file-text",
"idx": 181,
"is_submittable": 1,
"links": [],
"modified": "2020-12-11 12:48:31.769958",
"modified": "2020-12-25 22:57:32.555067",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",

View File

@ -6,7 +6,7 @@ import frappe, erpnext
import frappe.defaults
from frappe.utils import cint, flt, getdate, add_days, cstr, nowdate, get_link_to_form, formatdate
from frappe import _, msgprint, throw
from erpnext.accounts.party import get_party_account, get_due_date
from erpnext.accounts.party import get_party_account, get_due_date, get_party_details
from frappe.model.mapper import get_mapped_doc
from erpnext.controllers.selling_controller import SellingController
from erpnext.accounts.utils import get_account_currency
@ -21,6 +21,8 @@ from erpnext.accounts.general_ledger import get_round_off_account_and_cost_cente
from erpnext.accounts.doctype.loyalty_program.loyalty_program import \
get_loyalty_program_details_with_points, get_loyalty_details, validate_loyalty_points
from erpnext.accounts.deferred_revenue import validate_service_stop_date
from frappe.model.utils import get_fetch_values
from frappe.contacts.doctype.address.address import get_address_display
from erpnext.healthcare.utils import manage_invoice_submit_cancel
@ -1534,7 +1536,7 @@ def validate_inter_company_transaction(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
valid_price_list = frappe.db.get_value("Price List", {"name": price_list, "buying": 1, "selling": 1})
if not valid_price_list:
if not valid_price_list and not doc.is_internal_transfer():
frappe.throw(_("Selected Price List should have buying and selling fields checked."))
party = details.get("party")
@ -1557,6 +1559,7 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
if doctype in ["Sales Invoice", "Sales Order"]:
source_doc = frappe.get_doc(doctype, source_name)
target_doctype = "Purchase Invoice" if doctype == "Sales Invoice" else "Purchase Order"
target_detail_field = "sales_invoice_item" if doctype == "Sales Invoice" else "sales_order_item"
source_document_warehouse_field = 'target_warehouse'
target_document_warehouse_field = 'from_warehouse'
else:
@ -1570,6 +1573,7 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
def set_missing_values(source, target):
target.run_method("set_missing_values")
set_purchase_references(target)
def update_details(source_doc, target_doc, source_parent):
target_doc.inter_company_invoice_reference = source_doc.name
@ -1577,19 +1581,38 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
currency = frappe.db.get_value('Supplier', details.get('party'), 'default_currency')
target_doc.company = details.get("company")
target_doc.supplier = details.get("party")
target_doc.is_internal_supplier = 1
target_doc.ignore_pricing_rule = 1
target_doc.buying_price_list = source_doc.selling_price_list
# Invert Addresses
update_address(target_doc, 'supplier_address', 'address_display', source_doc.company_address)
update_address(target_doc, 'shipping_address', 'shipping_address_display', source_doc.customer_address)
if currency:
target_doc.currency = currency
update_taxes(target_doc, party=target_doc.supplier, party_type='Supplier', company=target_doc.company,
doctype=target_doc.doctype, party_address=target_doc.supplier_address,
company_address=target_doc.shipping_address)
else:
currency = frappe.db.get_value('Customer', details.get('party'), 'default_currency')
target_doc.company = details.get("company")
target_doc.customer = details.get("party")
target_doc.selling_price_list = source_doc.buying_price_list
update_address(target_doc, 'company_address', 'company_address_display', source_doc.supplier_address)
update_address(target_doc, 'shipping_address_name', 'shipping_address', source_doc.shipping_address)
update_address(target_doc, 'customer_address', 'address_display', source_doc.shipping_address)
if currency:
target_doc.currency = currency
update_taxes(target_doc, party=target_doc.customer, party_type='Customer', company=target_doc.company,
doctype=target_doc.doctype, party_address=target_doc.customer_address,
company_address=target_doc.company_address, shipping_address_name=target_doc.shipping_address_name)
item_field_map = {
"doctype": target_doctype + " Item",
"field_no_map": [
@ -1597,25 +1620,33 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
"expense_account",
"cost_center",
"warehouse"
]
],
"field_map": {
'rate': 'rate',
}
}
if source_doc.get('update_stock'):
item_field_map.update({
'field_map': {
source_document_warehouse_field: target_document_warehouse_field,
'batch_no': 'batch_no',
'serial_no': 'serial_no'
}
if doctype in ["Sales Invoice", "Sales Order"]:
item_field_map["field_map"].update({
"name": target_detail_field,
})
if source_doc.get('update_stock'):
item_field_map["field_map"].update({
source_document_warehouse_field: target_document_warehouse_field,
'batch_no': 'batch_no',
'serial_no': 'serial_no'
})
doclist = get_mapped_doc(doctype, source_name, {
doctype: {
"doctype": target_doctype,
"postprocess": update_details,
"set_target_warehouse": "set_from_warehouse",
"field_no_map": [
"taxes_and_charges"
"taxes_and_charges",
"set_warehouse",
"shipping_address"
]
},
doctype +" Item": item_field_map
@ -1624,6 +1655,110 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
return doclist
def set_purchase_references(doc):
# add internal PO or PR links if any
if doc.is_internal_transfer():
if doc.doctype == 'Purchase Receipt':
so_item_map = get_delivery_note_details(doc.inter_company_invoice_reference)
if so_item_map:
pd_item_map, parent_child_map, warehouse_map = \
get_pd_details('Purchase Order Item', so_item_map, 'sales_order_item')
update_pr_items(doc, so_item_map, pd_item_map, parent_child_map, warehouse_map)
elif doc.doctype == 'Purchase Invoice':
dn_item_map, so_item_map = get_sales_invoice_details(doc.inter_company_invoice_reference)
# First check for Purchase receipt
if list(dn_item_map.values()):
pd_item_map, parent_child_map, warehouse_map = \
get_pd_details('Purchase Receipt Item', dn_item_map, 'delivery_note_item')
update_pi_items(doc, 'pr_detail', 'purchase_receipt',
dn_item_map, pd_item_map, parent_child_map, warehouse_map)
if list(so_item_map.values()):
pd_item_map, parent_child_map, warehouse_map = \
get_pd_details('Purchase Order Item', so_item_map, 'sales_order_item')
update_pi_items(doc, 'po_detail', 'purchase_order',
so_item_map, pd_item_map, parent_child_map, warehouse_map)
def update_pi_items(doc, detail_field, parent_field, sales_item_map,
purchase_item_map, parent_child_map, warehouse_map):
for item in doc.get('items'):
item.set(detail_field, purchase_item_map.get(sales_item_map.get(item.sales_invoice_item)))
item.set(parent_field, parent_child_map.get(sales_item_map.get(item.sales_invoice_item)))
if doc.update_stock:
item.warehouse = warehouse_map.get(sales_item_map.get(item.sales_invoice_item))
def update_pr_items(doc, sales_item_map, purchase_item_map, parent_child_map, warehouse_map):
for item in doc.get('items'):
item.purchase_order_item = purchase_item_map.get(sales_item_map.get(item.delivery_note_item))
item.warehouse = warehouse_map.get(sales_item_map.get(item.delivery_note_item))
item.purchase_order = parent_child_map.get(sales_item_map.get(item.delivery_note_item))
def get_delivery_note_details(internal_reference):
so_item_map = {}
si_item_details = frappe.get_all('Delivery Note Item', fields=['name', 'so_detail'],
filters={'parent': internal_reference})
for d in si_item_details:
so_item_map.setdefault(d.name, d.so_detail)
return so_item_map
def get_sales_invoice_details(internal_reference):
dn_item_map = {}
so_item_map = {}
si_item_details = frappe.get_all('Sales Invoice Item', fields=['name', 'so_detail',
'dn_detail'], filters={'parent': internal_reference})
for d in si_item_details:
if d.dn_detail:
dn_item_map.setdefault(d.name, d.dn_detail)
if d.so_detail:
so_item_map.setdefault(d.name, d.so_detail)
return dn_item_map, so_item_map
def get_pd_details(doctype, sd_detail_map, sd_detail_field):
pd_item_map = {}
accepted_warehouse_map = {}
parent_child_map = {}
pd_item_details = frappe.get_all(doctype,
fields=[sd_detail_field, 'name', 'warehouse', 'parent'], filters={sd_detail_field: ('in', list(sd_detail_map.values()))})
for d in pd_item_details:
pd_item_map.setdefault(d.get(sd_detail_field), d.name)
parent_child_map.setdefault(d.get(sd_detail_field), d.parent)
accepted_warehouse_map.setdefault(d.get(sd_detail_field), d.warehouse)
return pd_item_map, parent_child_map, accepted_warehouse_map
def update_taxes(doc, party=None, party_type=None, company=None, doctype=None, party_address=None,
company_address=None, shipping_address_name=None, master_doctype=None):
# Update Party Details
party_details = get_party_details(party=party, party_type=party_type, company=company,
doctype=doctype, party_address=party_address, company_address=company_address,
shipping_address=shipping_address_name)
# Update taxes and charges if any
doc.taxes_and_charges = party_details.get('taxes_and_charges')
doc.set('taxes', party_details.get('taxes'))
def update_address(doc, address_field, address_display_field, address_name):
doc.set(address_field, address_name)
fetch_values = get_fetch_values(doc.doctype, address_field, address_name)
for key, value in fetch_values.items():
doc.set(key, value)
doc.set(address_display_field, get_address_display(doc.get(address_field)))
@frappe.whitelist()
def get_loyalty_programs(customer):
''' sets applicable loyalty program to the customer or returns a list of applicable programs '''

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

View File

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

View File

@ -49,7 +49,8 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
elif d.po_detail:
purchase_receipt = ", ".join(po_pr_map.get(d.po_detail, []))
expense_account = d.expense_account or aii_account_map.get(d.company)
expense_account = d.unrealized_profit_loss_account or d.expense_account \
or aii_account_map.get(d.company)
row = {
'item_code': d.item_code,
@ -315,6 +316,7 @@ def get_items(filters, additional_query_columns):
`tabPurchase Invoice Item`.`name`, `tabPurchase Invoice Item`.`parent`,
`tabPurchase Invoice`.posting_date, `tabPurchase Invoice`.credit_to, `tabPurchase Invoice`.company,
`tabPurchase Invoice`.supplier, `tabPurchase Invoice`.remarks, `tabPurchase Invoice`.base_net_total,
`tabPurchase Invoice`.unrealized_profit_loss_account,
`tabPurchase Invoice Item`.`item_code`, `tabPurchase Invoice Item`.description,
`tabPurchase Invoice Item`.`item_name`, `tabPurchase Invoice Item`.`item_group`,
`tabPurchase Invoice Item`.`project`, `tabPurchase Invoice Item`.`purchase_order`,

View File

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

View File

@ -14,13 +14,15 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
if not filters: filters = {}
invoice_list = get_invoices(filters, additional_query_columns)
columns, expense_accounts, tax_accounts = get_columns(invoice_list, additional_table_columns)
columns, expense_accounts, tax_accounts, unrealized_profit_loss_accounts \
= get_columns(invoice_list, additional_table_columns)
if not invoice_list:
msgprint(_("No record found"))
return columns, invoice_list
invoice_expense_map = get_invoice_expense_map(invoice_list)
internal_invoice_map = get_internal_invoice_map(invoice_list)
invoice_expense_map, invoice_tax_map = get_invoice_tax_map(invoice_list,
invoice_expense_map, expense_accounts)
invoice_po_pr_map = get_invoice_po_pr_map(invoice_list)
@ -52,10 +54,17 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
# map expense values
base_net_total = 0
for expense_acc in expense_accounts:
expense_amount = flt(invoice_expense_map.get(inv.name, {}).get(expense_acc))
if inv.is_internal_supplier and inv.company == inv.represents_company:
expense_amount = 0
else:
expense_amount = flt(invoice_expense_map.get(inv.name, {}).get(expense_acc))
base_net_total += expense_amount
row.append(expense_amount)
# Add amount in unrealized account
for account in unrealized_profit_loss_accounts:
row.append(flt(internal_invoice_map.get((inv.name, account))))
# net total
row.append(base_net_total or inv.base_net_total)
@ -96,7 +105,8 @@ def get_columns(invoice_list, additional_table_columns):
"width": 80
}
]
expense_accounts = tax_accounts = expense_columns = tax_columns = []
expense_accounts = tax_accounts = expense_columns = tax_columns = unrealized_profit_loss_accounts = \
unrealized_profit_loss_account_columns = []
if invoice_list:
expense_accounts = frappe.db.sql_list("""select distinct expense_account
@ -112,17 +122,25 @@ def get_columns(invoice_list, additional_table_columns):
and parent in (%s) order by account_head""" %
', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]))
unrealized_profit_loss_accounts = frappe.db.sql_list("""SELECT distinct unrealized_profit_loss_account
from `tabPurchase Invoice` where docstatus = 1 and name in (%s)
and ifnull(unrealized_profit_loss_account, '') != ''
order by unrealized_profit_loss_account""" %
', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]))
expense_columns = [(account + ":Currency/currency:120") for account in expense_accounts]
unrealized_profit_loss_account_columns = [(account + ":Currency/currency:120") for account in unrealized_profit_loss_accounts]
for account in tax_accounts:
if account not in expense_accounts:
tax_columns.append(account + ":Currency/currency:120")
columns = columns + expense_columns + [_("Net Total") + ":Currency/currency:120"] + tax_columns + \
columns = columns + expense_columns + unrealized_profit_loss_account_columns + \
[_("Net Total") + ":Currency/currency:120"] + tax_columns + \
[_("Total Tax") + ":Currency/currency:120", _("Grand Total") + ":Currency/currency:120",
_("Rounded Total") + ":Currency/currency:120", _("Outstanding Amount") + ":Currency/currency:120"]
return columns, expense_accounts, tax_accounts
return columns, expense_accounts, tax_accounts, unrealized_profit_loss_accounts
def get_conditions(filters):
conditions = ""
@ -199,6 +217,19 @@ def get_invoice_expense_map(invoice_list):
return invoice_expense_map
def get_internal_invoice_map(invoice_list):
unrealized_amount_details = frappe.db.sql("""SELECT name, unrealized_profit_loss_account,
base_net_total as amount from `tabPurchase Invoice` where name in (%s)
and is_internal_supplier = 1 and company = represents_company""" %
', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1)
internal_invoice_map = {}
for d in unrealized_amount_details:
if d.unrealized_profit_loss_account:
internal_invoice_map.setdefault((d.name, d.unrealized_profit_loss_account), d.amount)
return internal_invoice_map
def get_invoice_tax_map(invoice_list, invoice_expense_map, expense_accounts):
tax_details = frappe.db.sql("""
select parent, account_head, case add_deduct_tax when "Add" then sum(base_tax_amount_after_discount_amount)

View File

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

View File

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

View File

@ -134,6 +134,8 @@
"ref_sq",
"column_break_74",
"party_account_currency",
"is_internal_supplier",
"represents_company",
"inter_company_order_reference"
],
"fields": [
@ -1101,13 +1103,28 @@
{
"fieldname": "items_col_break",
"fieldtype": "Column Break"
},
{
"default": "0",
"fetch_from": "supplier.is_internal_supplier",
"fieldname": "is_internal_supplier",
"fieldtype": "Check",
"label": "Is Internal Supplier"
},
{
"fetch_from": "supplier.represents_company",
"fieldname": "represents_company",
"fieldtype": "Link",
"label": "Represents Company",
"options": "Company",
"read_only": 1
}
],
"icon": "fa fa-file-text",
"idx": 105,
"is_submittable": 1,
"links": [],
"modified": "2020-12-03 16:46:44.229351",
"modified": "2021-01-20 22:07:23.487138",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order",

View File

@ -52,7 +52,10 @@ class Supplier(TransactionBase):
self.validate_internal_supplier()
def validate_internal_supplier(self):
if self.is_internal_supplier and frappe.db.get_value("Supplier", {"represents_company": self.represents_company}, "name"):
internal_supplier = frappe.db.get_value("Supplier",
{"is_internal_supplier": 1, "represents_company": self.represents_company, "name": ("!=", self.name)}, "name")
if internal_supplier:
frappe.throw(_("Internal Supplier for company {0} already exists").format(
frappe.bold(self.represents_company)))

View File

@ -75,6 +75,9 @@ class AccountsController(TransactionBase):
self.ensure_supplier_is_not_blocked()
self.validate_date_with_fiscal_year()
self.validate_inter_company_reference()
self.set_incoming_rate()
if self.meta.get_field("currency"):
self.calculate_taxes_and_totals()
@ -212,6 +215,17 @@ class AccountsController(TransactionBase):
validate_fiscal_year(self.get(date_field), self.fiscal_year, self.company,
self.meta.get_label(date_field), self)
def validate_inter_company_reference(self):
if self.doctype not in ('Purchase Invoice', 'Purchase Receipt', 'Purchase Order'):
return
if self.is_internal_transfer():
if not (self.get('inter_company_reference') or self.get('inter_company_invoice_reference')
or self.get('inter_company_order_reference')):
msg = _("Internal Sale or Delivery Reference missing. ")
msg += _("Please create purchase from internal sale or delivery document itself")
frappe.throw(msg, title=_("Internal Sales Reference Missing"))
def validate_due_date(self):
if self.get('is_pos'): return
@ -454,8 +468,10 @@ class AccountsController(TransactionBase):
account_currency = get_account_currency(gl_dict.account)
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)
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"),
self.company_currency)
@ -968,9 +984,9 @@ class AccountsController(TransactionBase):
It will an internal transfer if its an internal customer and representation
company is same as billing company
"""
if self.doctype == 'Sales Invoice':
if self.doctype in ('Sales Invoice', 'Delivery Note', 'Sales Order'):
internal_party_field = 'is_internal_customer'
else:
elif self.doctype in ('Purchase Invoice', 'Purchase Receipt', 'Purchase Order'):
internal_party_field = 'is_internal_supplier'
if self.get(internal_party_field) and (self.represents_company == self.company):

View File

@ -44,7 +44,6 @@ class BuyingController(StockController):
self.validate_items()
self.set_qty_as_per_stock_uom()
self.validate_stock_or_nonstock_items()
self.update_tax_category_for_internal_transfer()
self.validate_warehouse()
self.validate_from_warehouse()
self.set_supplier_address()
@ -100,11 +99,6 @@ class BuyingController(StockController):
msg = _('Tax Category has been changed to "Total" because all the Items are non-stock items')
self.update_tax_category(msg)
def update_tax_category_for_internal_transfer(self):
if self.doctype == 'Purchase Invoice' and self.is_internal_transfer():
msg = _('Tax Category has been changed to "Total" as its an internal purchase.')
self.update_tax_category(msg)
def update_tax_category(self, msg):
tax_for_valuation = [d for d in self.get("taxes")
if d.category in ["Valuation", "Valuation and Total"]]
@ -224,6 +218,48 @@ class BuyingController(StockController):
else:
item.valuation_rate = 0.0
def set_incoming_rate(self):
if self.doctype not in ("Purchase Receipt", "Purchase Invoice", "Purchase Order"):
return
ref_doctype_map = {
"Purchase Order": "Sales Order Item",
"Purchase Receipt": "Delivery Note Item",
"Purchase Invoice": "Sales Invoice Item",
}
ref_doctype = ref_doctype_map.get(self.doctype)
items = self.get("items")
for d in items:
if not cint(self.get("is_return")):
# Get outgoing rate based on original item cost based on valuation method
if not d.get(frappe.scrub(ref_doctype)):
outgoing_rate = get_incoming_rate({
"item_code": d.item_code,
"warehouse": d.get('from_warehouse'),
"posting_date": self.get('posting_date') or self.get('transation_date'),
"posting_time": self.get('posting_time'),
"qty": -1 * flt(d.get('stock_qty')),
"serial_no": d.get('serial_no'),
"company": self.company,
"voucher_type": self.doctype,
"voucher_no": self.name,
"allow_zero_valuation": d.get("allow_zero_valuation")
}, raise_error_if_no_rate=False)
rate = flt(outgoing_rate * d.conversion_factor, d.precision('rate'))
else:
rate = frappe.db.get_value(ref_doctype, d.get(frappe.scrub(ref_doctype)), 'rate')
if self.is_internal_transfer():
if rate != d.rate:
d.rate = rate
d.discount_percentage = 0
d.discount_amount = 0
frappe.msgprint(_("Row {0}: Item rate has been updated as per valuation rate since its an internal stock transfer")
.format(d.idx), alert=1)
def get_supplied_items_cost(self, item_row_id, reset_outgoing_rate=True):
supplied_items_cost = 0.0
for d in self.get("supplied_items"):
@ -559,6 +595,8 @@ class BuyingController(StockController):
from_warehouse_sle = self.get_sl_entries(d, {
"actual_qty": -1 * pr_qty,
"warehouse": d.from_warehouse,
"outgoing_rate": d.rate,
"recalculate_rate": 1,
"dependant_sle_voucher_detail_no": d.name
})

View File

@ -3,7 +3,7 @@
from __future__ import unicode_literals
import frappe
from frappe.utils import cint, flt, cstr, comma_or, get_link_to_form
from frappe.utils import cint, flt, cstr, get_link_to_form, nowtime
from frappe import _, throw
from erpnext.stock.get_item_details import get_bin_details
from erpnext.stock.utils import get_incoming_rate
@ -49,7 +49,6 @@ class SellingController(StockController):
self.set_customer_address()
self.validate_for_duplicate_items()
self.validate_target_warehouse()
self.set_incoming_rate()
def set_missing_values(self, for_validate=False):
@ -312,7 +311,7 @@ class SellingController(StockController):
sales_order.update_reserved_qty(so_item_rows)
def set_incoming_rate(self):
if self.doctype not in ("Delivery Note", "Sales Invoice"):
if self.doctype not in ("Delivery Note", "Sales Invoice", "Sales Order"):
return
items = self.get("items") + (self.get("packed_items") or [])
@ -322,15 +321,26 @@ class SellingController(StockController):
d.incoming_rate = get_incoming_rate({
"item_code": d.item_code,
"warehouse": d.warehouse,
"posting_date": self.posting_date,
"posting_time": self.posting_time,
"qty": -1*flt(d.qty),
"serial_no": d.serial_no,
"posting_date": self.get('posting_date') or self.get('transaction_date'),
"posting_time": self.get('posting_time') or nowtime(),
"qty": -1 * flt(d.get('stock_qty') or d.get('actual_qty')),
"serial_no": d.get('serial_no'),
"company": self.company,
"voucher_type": self.doctype,
"voucher_no": self.name,
"allow_zero_valuation": d.get("allow_zero_valuation")
}, raise_error_if_no_rate=False)
# For internal transfers use incoming rate as the valuation rate
if self.is_internal_transfer():
rate = flt(d.incoming_rate * d.conversion_factor, d.precision('rate'))
if d.rate != rate:
d.rate = rate
d.discount_percentage = 0
d.discount_amount = 0
frappe.msgprint(_("Row {0}: Item rate has been updated as per valuation rate since its an internal stock transfer")
.format(d.idx), alert=1)
elif self.get("return_against"):
# Get incoming rate of return entry from reference document
# based on original item cost as per valuation method

View File

@ -24,6 +24,7 @@ class StockController(AccountsController):
self.validate_inspection()
self.validate_serialized_batch()
self.validate_customer_provided_item()
self.validate_internal_transfer()
self.validate_putaway_capacity()
def make_gl_entries(self, gl_entries=None, from_repost=False):
@ -74,6 +75,7 @@ class StockController(AccountsController):
warehouse_with_no_account = []
precision = frappe.get_precision("GL Entry", "debit_in_account_currency")
for item_row in voucher_details:
sle_list = sle_map.get(item_row.name)
if sle_list:
for sle in sle_list:
@ -218,7 +220,7 @@ class StockController(AccountsController):
""", (self.doctype, self.name), as_dict=True)
for sle in stock_ledger_entries:
stock_ledger.setdefault(sle.voucher_detail_no, []).append(sle)
stock_ledger.setdefault(sle.voucher_detail_no, []).append(sle)
return stock_ledger
def make_batches(self, warehouse_field):
@ -393,6 +395,32 @@ class StockController(AccountsController):
if frappe.db.get_value('Item', d.item_code, 'is_customer_provided_item'):
d.allow_zero_valuation_rate = 1
def validate_internal_transfer(self):
if self.doctype in ('Sales Invoice', 'Delivery Note', 'Purchase Invoice', 'Purchase Receipt') \
and self.is_internal_transfer():
self.validate_in_transit_warehouses()
self.validate_multi_currency()
self.validate_packed_items()
def validate_in_transit_warehouses(self):
if (self.doctype == 'Sales Invoice' and self.get('update_stock')) or self.doctype == 'Delivery Note':
for item in self.get('items'):
if not item.target_warehouse:
frappe.throw(_("Row {0}: Target Warehouse is mandatory for internal transfers").format(item.idx))
if (self.doctype == 'Purchase Invoice' and self.get('update_stock')) or self.doctype == 'Purchase Receipt':
for item in self.get('items'):
if not item.from_warehouse:
frappe.throw(_("Row {0}: From Warehouse is mandatory for internal transfers").format(item.idx))
def validate_multi_currency(self):
if self.currency != self.company_currency:
frappe.throw(_("Internal transfers can only be done in company's default currency"))
def validate_packed_items(self):
if self.doctype in ('Sales Invoice', 'Delivery Note Item') and self.get('packed_items'):
frappe.throw(_("Packed Items cannot be transferred internally"))
def validate_putaway_capacity(self):
# if over receipt is attempted while 'apply putaway rule' is disabled
# and if rule was applied on the transaction, validate it.

View File

@ -10,6 +10,7 @@ from erpnext.controllers.accounts_controller import validate_conversion_rate, \
validate_taxes_and_charges, validate_inclusive_tax
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.journal_entry.journal_entry import get_exchange_rate
class calculate_taxes_and_totals(object):
def __init__(self, doc):
@ -758,3 +759,35 @@ def get_rounded_tax_amount(itemised_tax, precision):
for taxes in itemised_tax.values():
for tax_account in taxes:
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

@ -105,10 +105,18 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
frappe.ui.form.on(this.frm.doctype + " Item", {
items_add: function(frm, cdt, cdn) {
var item = frappe.get_doc(cdt, cdn);
if(!item.warehouse && frm.doc.set_warehouse) {
if (!item.warehouse && frm.doc.set_warehouse) {
item.warehouse = frm.doc.set_warehouse;
}
if (!item.target_warehouse && frm.doc.set_target_warehouse) {
item.target_warehouse = frm.doc.set_target_warehouse;
}
if (!item.from_warehouse && frm.doc.set_from_warehouse) {
item.from_warehouse = frm.doc.set_from_warehouse;
}
erpnext.accounts.dimensions.copy_dimension_from_first_row(frm, cdt, cdn, 'items');
}
});
@ -227,6 +235,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
}
};
this.frm.trigger('set_default_internal_warehouse');
return frappe.run_serially([
() => set_value('currency', currency),
() => set_value('price_list_currency', currency),
@ -658,7 +668,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
args: item_args
},
callback: function(r) {
frappe.model.set_value(item.doctype, item.name, 'rate', r.message);
frappe.model.set_value(item.doctype, item.name, 'rate', r.message * item.conversion_factor);
}
});
},
@ -724,6 +734,31 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
this.calculate_taxes_and_totals(false);
},
update_stock: function() {
this.frm.trigger('set_default_internal_warehouse');
},
set_default_internal_warehouse: function() {
let me = this;
if ((this.frm.doc.doctype === 'Sales Invoice' && me.frm.doc.update_stock)
|| this.frm.doc.doctype == 'Delivery Note') {
if (this.frm.doc.is_internal_customer && this.frm.doc.company === this.frm.doc.represents_company) {
frappe.db.get_value('Company', this.frm.doc.company, 'default_in_transit_warehouse', function(value) {
me.frm.set_value('set_target_warehouse', value.default_in_transit_warehouse);
});
}
}
if ((this.frm.doc.doctype === 'Purchase Invoice' && me.frm.doc.update_stock)
|| this.frm.doc.doctype == 'Purchase Receipt') {
if (this.frm.doc.is_internal_supplier && this.frm.doc.company === this.frm.doc.represents_company) {
frappe.db.get_value('Company', this.frm.doc.company, 'default_in_transit_warehouse', function(value) {
me.frm.set_value('set_from_warehouse', value.default_in_transit_warehouse);
});
}
}
},
company: function() {
var me = this;
var set_pricing = function() {
@ -810,7 +845,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
in_list(['Purchase Order', 'Purchase Receipt', 'Purchase Invoice'], this.frm.doctype)) {
erpnext.utils.get_shipping_address(this.frm, function(){
set_party_account(set_pricing);
})
});
// Get default company billing address in Purchase Invoice, Order and Receipt
frappe.call({
@ -1977,6 +2012,14 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
this.autofill_warehouse(this.frm.doc.items, "warehouse", this.frm.doc.set_warehouse);
},
set_target_warehouse: function() {
this.autofill_warehouse(this.frm.doc.items, "target_warehouse", this.frm.doc.set_target_warehouse);
},
set_from_warehouse: function() {
this.autofill_warehouse(this.frm.doc.items, "from_warehouse", this.frm.doc.set_from_warehouse);
},
autofill_warehouse : function (child_table, warehouse_field, warehouse) {
if (warehouse && child_table && child_table.length) {
let doctype = child_table[0].doctype;

View File

@ -276,6 +276,12 @@ erpnext.utils.validate_mandatory = function(frm, label, value, trigger_on) {
erpnext.utils.get_shipping_address = function(frm, callback){
if (frm.doc.company) {
if (!(frm.doc.inter_com_order_reference || frm.doc.internal_invoice_reference ||
frm.doc.internal_order_reference)) {
if (callback) {
return callback();
}
}
frappe.call({
method: "erpnext.accounts.custom.address.get_shipping_address",
args: {

View File

@ -40,14 +40,12 @@ erpnext.setup_auto_gst_taxation = (doctype) => {
callback: function(r) {
if(r.message) {
frm.set_value('taxes_and_charges', r.message.taxes_and_charges);
frm.set_value('taxes', r.message.taxes);
frm.set_value('place_of_supply', r.message.place_of_supply);
} else if (frm.doc.is_internal_supplier || frm.doc.is_internal_customer) {
frm.set_value('taxes_and_charges', '');
frm.set_value('taxes', []);
}
}
});
}
});
};
}

View File

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

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)))
def validate_internal_customer(self):
if self.is_internal_customer and frappe.db.get_value('Customer', {"represents_company": self.represents_company}, "name"):
internal_customer = frappe.db.get_value("Customer",
{"is_internal_customer": 1, "represents_company": self.represents_company, "name": ("!=", self.name)}, "name")
if internal_customer:
frappe.throw(_("Internal Customer for company {0} already exists").format(
frappe.bold(self.represents_company)))

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

View File

@ -107,6 +107,8 @@
"tc_name",
"terms",
"more_info",
"is_internal_customer",
"represents_company",
"inter_company_order_reference",
"project",
"party_account_currency",
@ -1103,7 +1105,8 @@
"hide_days": 1,
"hide_seconds": 1,
"label": "Inter Company Order Reference",
"options": "Purchase Order"
"options": "Purchase Order",
"read_only": 1
},
{
"description": "Track this Sales Order against any Project",
@ -1455,13 +1458,29 @@
"hide_seconds": 1,
"label": "Skip Delivery Note",
"print_hide": 1
},
{
"default": "0",
"fetch_from": "customer.is_internal_customer",
"fieldname": "is_internal_customer",
"fieldtype": "Check",
"label": "Is Internal Customer",
"read_only": 1
},
{
"fetch_from": "customer.represents_company",
"fieldname": "represents_company",
"fieldtype": "Link",
"label": "Represents Company",
"options": "Company",
"read_only": 1
}
],
"icon": "fa fa-file-text",
"idx": 105,
"is_submittable": 1,
"links": [],
"modified": "2020-10-30 13:59:18.628077",
"modified": "2021-01-20 23:40:39.929296",
"modified_by": "Administrator",
"module": "Selling",
"name": "Sales Order",

View File

@ -95,13 +95,19 @@ frappe.ui.form.on("Delivery Note", {
frm.page.set_inner_btn_group_as_primary(__('Create'));
}
if (frm.doc.docstatus === 1 && frm.doc.is_internal_customer && !frm.doc.inter_company_reference) {
frm.add_custom_button(__('Purchase Receipt'), function() {
frappe.model.open_mapped_doc({
method: 'erpnext.stock.doctype.delivery_note.delivery_note.make_inter_company_purchase_receipt',
frm: frm,
})
}, __('Create'));
if (frm.doc.docstatus == 1 && !frm.doc.inter_company_reference) {
let internal = me.frm.doc.is_internal_customer;
if (internal) {
let button_label = (me.frm.doc.company === me.frm.doc.represents_company) ? "Internal Purchase Receipt" :
"Inter Company Purchase Receipt";
me.frm.add_custom_button(button_label, function() {
frappe.model.open_mapped_doc({
method: 'erpnext.stock.doctype.delivery_note.delivery_note.make_inter_company_purchase_receipt',
frm: frm,
});
}, __('Create'));
}
}
}
});
@ -297,15 +303,6 @@ 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}));

View File

@ -53,7 +53,7 @@
"sec_warehouse",
"set_warehouse",
"col_break_warehouse",
"to_warehouse",
"set_target_warehouse",
"items_section",
"scan_barcode",
"items",
@ -117,6 +117,7 @@
"source",
"column_break5",
"is_internal_customer",
"represents_company",
"inter_company_reference",
"per_billed",
"customer_group",
@ -502,18 +503,6 @@
"fieldname": "col_break_warehouse",
"fieldtype": "Column Break"
},
{
"description": "Required only for sample item.",
"fieldname": "to_warehouse",
"fieldtype": "Link",
"in_standard_filter": 1,
"label": "To Warehouse",
"no_copy": 1,
"oldfieldname": "to_warehouse",
"oldfieldtype": "Link",
"options": "Warehouse",
"print_hide": 1
},
{
"fieldname": "items_section",
"fieldtype": "Section Break",
@ -1261,13 +1250,34 @@
"no_copy": 1,
"print_hide": 1,
"read_only": 1
},
{
"depends_on": "eval: doc.is_internal_customer",
"fieldname": "set_target_warehouse",
"fieldtype": "Link",
"in_standard_filter": 1,
"label": "Set Target Warehouse",
"no_copy": 1,
"oldfieldname": "to_warehouse",
"oldfieldtype": "Link",
"options": "Warehouse",
"print_hide": 1
},
{
"description": "Company which internal customer represents.",
"fetch_from": "customer.represents_company",
"fieldname": "represents_company",
"fieldtype": "Link",
"label": "Represents Company",
"options": "Company",
"read_only": 1
}
],
"icon": "fa fa-truck",
"idx": 146,
"is_submittable": 1,
"links": [],
"modified": "2020-11-30 12:54:45.407289",
"modified": "2020-12-26 17:07:59.194403",
"modified_by": "Administrator",
"module": "Stock",
"name": "Delivery Note",

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)
def make_inter_company_transaction(doctype, source_name, target_doc=None):
from erpnext.accounts.doctype.sales_invoice.sales_invoice import validate_inter_company_transaction, get_inter_company_details
from erpnext.accounts.doctype.sales_invoice.sales_invoice import (validate_inter_company_transaction,
get_inter_company_details, update_address, update_taxes, set_purchase_references)
if doctype == 'Delivery Note':
source_doc = frappe.get_doc(doctype, source_name)
@ -682,6 +683,7 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
def set_missing_values(source, target):
target.run_method("set_missing_values")
set_purchase_references(target)
if target.doctype == 'Purchase Receipt':
master_doctype = 'Purchase Taxes and Charges Template'
@ -697,21 +699,35 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
if target_doc.doctype == 'Purchase Receipt':
target_doc.company = details.get("company")
target_doc.supplier = details.get("party")
target_doc.supplier_address = source_doc.company_address
target_doc.shipping_address = source_doc.shipping_address_name or source_doc.customer_address
target_doc.buying_price_list = source_doc.selling_price_list
target_doc.is_internal_supplier = 1
target_doc.inter_company_reference = source_doc.name
# Invert the address on target doc creation
update_address(target_doc, 'supplier_address', 'address_display', source_doc.company_address)
update_address(target_doc, 'shipping_address', 'shipping_address_display', source_doc.customer_address)
update_taxes(target_doc, party=target_doc.supplier, party_type='Supplier', company=target_doc.company,
doctype=target_doc.doctype, party_address=target_doc.supplier_address,
company_address=target_doc.shipping_address)
else:
target_doc.company = details.get("company")
target_doc.customer = details.get("party")
target_doc.company_address = source_doc.supplier_address
target_doc.shipping_address_name = source_doc.shipping_address
target_doc.selling_price_list = source_doc.buying_price_list
target_doc.is_internal_customer = 1
target_doc.inter_company_reference = source_doc.name
doclist = get_mapped_doc(doctype, source_name, {
# Invert the address on target doc creation
update_address(target_doc, 'company_address', 'company_address_display', source_doc.supplier_address)
update_address(target_doc, 'shipping_address_name', 'shipping_address', source_doc.shipping_address)
update_address(target_doc, 'customer_address', 'address_display', source_doc.shipping_address)
update_taxes(target_doc, party=target_doc.customer, party_type='Customer', company=target_doc.company,
doctype=target_doc.doctype, party_address=target_doc.customer_address,
company_address=target_doc.company_address, shipping_address_name=target_doc.shipping_address_name)
doclist = get_mapped_doc(doctype, source_name, {
doctype: {
"doctype": target_doctype,
"postprocess": update_details,
@ -722,7 +738,10 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
doctype +" Item": {
"doctype": target_doctype + " Item",
"field_map": {
source_document_warehouse_field: target_document_warehouse_field
source_document_warehouse_field: target_document_warehouse_field,
'name': 'delivery_note_item',
'batch_no': 'batch_no',
'serial_no': 'serial_no'
},
"field_no_map": [
"warehouse"

View File

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

View File

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

View File

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

View File

@ -1,6 +1,7 @@
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
{% include 'erpnext/stock/landed_taxes_and_charges_common.js' %};
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", "posting_date", "posting_date");
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 =
`<br><br>
<table class="table table-bordered" style="background-color: #f9f9f9;">
@ -72,6 +62,11 @@ erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({
</table>`;
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() {
@ -97,34 +92,36 @@ erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({
set_total_taxes_and_charges: function() {
var total_taxes_and_charges = 0.0;
$.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() {
var me = this;
if(this.frm.doc.taxes.length) {
var total_item_cost = 0.0;
var based_on = this.frm.doc.distribute_charges_based_on.toLowerCase();
$.each(this.frm.doc.items || [], function(i, d) {
total_item_cost += flt(d[based_on])
});
var total_charges = 0.0;
$.each(this.frm.doc.items || [], function(i, item) {
item.applicable_charges = flt(item[based_on]) * flt(me.frm.doc.total_taxes_and_charges) / flt(total_item_cost)
item.applicable_charges = flt(item.applicable_charges, precision("applicable_charges", item))
total_charges += item.applicable_charges
});
if (based_on != 'distribute manually') {
$.each(this.frm.doc.items || [], function(i, d) {
total_item_cost += flt(d[based_on])
});
if (total_charges != this.frm.doc.total_taxes_and_charges){
var diff = this.frm.doc.total_taxes_and_charges - flt(total_charges)
this.frm.doc.items.slice(-1)[0].applicable_charges += diff
var total_charges = 0.0;
$.each(this.frm.doc.items || [], function(i, item) {
item.applicable_charges = flt(item[based_on]) * flt(me.frm.doc.total_taxes_and_charges) / flt(total_item_cost)
item.applicable_charges = flt(item.applicable_charges, precision("applicable_charges", item))
total_charges += item.applicable_charges
});
if (total_charges != this.frm.doc.total_taxes_and_charges){
var diff = this.frm.doc.total_taxes_and_charges - flt(total_charges)
this.frm.doc.items.slice(-1)[0].applicable_charges += diff
}
refresh_field("items");
}
refresh_field("items");
}
},
distribute_charges_based_on: function (frm) {
@ -134,7 +131,16 @@ erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({
items_remove: () => {
this.trigger('set_applicable_charges_for_item');
}
});
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:",
"creation": "2014-07-11 11:33:42.547339",
"doctype": "DocType",
@ -7,6 +8,9 @@
"field_order": [
"naming_series",
"company",
"column_break_2",
"posting_date",
"section_break_5",
"purchase_receipts",
"purchase_receipt_items",
"get_items_from_purchase_receipts",
@ -30,7 +34,9 @@
"options": "MAT-LCV-.YYYY.-",
"print_hide": 1,
"reqd": 1,
"set_only_once": 1
"set_only_once": 1,
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "company",
@ -40,24 +46,32 @@
"label": "Company",
"options": "Company",
"remember_last_selected_value": 1,
"reqd": 1
"reqd": 1,
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "purchase_receipts",
"fieldtype": "Table",
"label": "Purchase Receipts",
"options": "Landed Cost Purchase Receipt",
"reqd": 1
"reqd": 1,
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "purchase_receipt_items",
"fieldtype": "Section Break",
"label": "Purchase Receipt Items"
"label": "Purchase Receipt Items",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "get_items_from_purchase_receipts",
"fieldtype": "Button",
"label": "Get Items From Purchase Receipts"
"label": "Get Items From Purchase Receipts",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "items",
@ -65,42 +79,56 @@
"label": "Purchase Receipt Items",
"no_copy": 1,
"options": "Landed Cost Item",
"reqd": 1
"reqd": 1,
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "sec_break1",
"fieldtype": "Section Break",
"label": "Applicable Charges"
"label": "Applicable Charges",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "taxes",
"fieldtype": "Table",
"label": "Taxes and Charges",
"options": "Landed Cost Taxes and Charges",
"reqd": 1
"reqd": 1,
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "section_break_9",
"fieldtype": "Section Break"
"fieldtype": "Section Break",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "total_taxes_and_charges",
"fieldtype": "Currency",
"label": "Total Taxes and Charges",
"label": "Total Taxes and Charges (Company Currency)",
"options": "Company:company:default_currency",
"read_only": 1,
"reqd": 1
"reqd": 1,
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "col_break1",
"fieldtype": "Column Break"
"fieldtype": "Column Break",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "distribute_charges_based_on",
"fieldtype": "Select",
"label": "Distribute Charges Based On",
"options": "Qty\nAmount",
"reqd": 1
"options": "Qty\nAmount\nDistribute Manually",
"reqd": 1,
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "amended_from",
@ -109,21 +137,51 @@
"no_copy": 1,
"options": "Landed Cost Voucher",
"print_hide": 1,
"read_only": 1
"read_only": 1,
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "sec_break2",
"fieldtype": "Section Break"
"fieldtype": "Section Break",
"show_days": 1,
"show_seconds": 1
},
{
"fieldname": "landed_cost_help",
"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",
"index_web_pages_for_search": 1,
"is_submittable": 1,
"modified": "2019-11-21 15:34:10.846093",
"links": [],
"modified": "2021-01-25 23:07:30.468423",
"modified_by": "Administrator",
"module": "Stock",
"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 erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
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):
def get_items_from_purchase_receipts(self):
@ -39,13 +40,15 @@ class LandedCostVoucher(Document):
def validate(self):
self.check_mandatory()
self.validate_purchase_receipts()
init_landed_taxes_and_totals(self)
self.set_total_taxes_and_charges()
if not self.get("items"):
self.get_items_from_purchase_receipts()
else:
self.validate_applicable_charges_for_item()
self.validate_purchase_receipts()
self.validate_expense_accounts()
self.set_total_taxes_and_charges()
self.set_applicable_charges_on_item()
self.validate_applicable_charges_for_item()
def check_mandatory(self):
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}")
.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):
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):
based_on = self.distribute_charges_based_on.lower()
total = sum([flt(d.get(based_on)) for d in self.get("items")])
if based_on != 'distribute manually':
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:
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,
'item_code': item.item_code }, fields=['name', 'docstatus'])
if not docs or len(docs) != item.qty:
frappe.throw(_('There are not enough asset created or linked to {0}.').format(item.receipt_document)
+ _('Please create or link {0} Assets with respective document.').format(item.qty))
frappe.throw(_('There are not enough asset created or linked to {0}. Please create or link {1} Assets with respective document.').format(
item.receipt_document, item.qty))
if docs:
for d in docs:
if d.docstatus == 1:
frappe.throw(_('{0} {1} has submitted Assets. Remove Item {2} from table to continue.')
.format(item.receipt_document_type, frappe.bold(item.receipt_document), frappe.bold(item.item_code)))
frappe.throw(_('{2} <b>{0}</b> has submitted Assets. Remove Item <b>{1}</b> from table to continue.').format(
item.receipt_document, item.item_code, item.receipt_document_type))
def update_rate_in_serial_no_for_non_asset_items(self, receipt_document):
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
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 create_account
class TestLandedCostVoucher(unittest.TestCase):
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)
self.assertEqual(lcv.items[0].applicable_charges, 41.07)
self.assertEqual(lcv.items[2].applicable_charges, 41.08)
self.assertEqual(flt(lcv.items[0].applicable_charges, 2), 41.07)
self.assertEqual(flt(lcv.items[2].applicable_charges, 2), 41.08)
def test_multiple_landed_cost_voucher_against_pr(self):
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[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):
args = frappe._dict(args)
ref_doc = frappe.get_doc(args.receipt_document_type, args.receipt_document)

View File

@ -48,6 +48,7 @@
"set_warehouse",
"rejected_warehouse",
"col_break_warehouse",
"set_from_warehouse",
"is_subcontracted",
"supplier_warehouse",
"items_section",
@ -115,6 +116,7 @@
"per_returned",
"is_internal_supplier",
"inter_company_reference",
"represents_company",
"subscription_detail",
"auto_repeat",
"printing_settings",
@ -1087,7 +1089,9 @@
"fieldname": "inter_company_reference",
"fieldtype": "Link",
"label": "Inter Company Reference",
"no_copy": 1,
"options": "Delivery Note",
"print_hide": 1,
"read_only": 1
},
{
@ -1121,13 +1125,29 @@
"no_copy": 1,
"print_hide": 1,
"read_only": 1
},
{
"depends_on": "eval: doc.is_internal_supplier",
"description": "Sets 'From Warehouse' in each row of the items table.",
"fieldname": "set_from_warehouse",
"fieldtype": "Link",
"label": "Set From Warehouse",
"options": "Warehouse"
},
{
"fetch_from": "supplier.represents_company",
"fieldname": "represents_company",
"fieldtype": "Link",
"label": "Represents Company",
"options": "Company",
"read_only": 1
}
],
"icon": "fa fa-truck",
"idx": 261,
"is_submittable": 1,
"links": [],
"modified": "2020-12-08 18:31:32.234503",
"modified": "2020-12-26 20:49:39.106049",
"modified_by": "Administrator",
"module": "Stock",
"name": "Purchase Receipt",

View File

@ -288,12 +288,15 @@ class PurchaseReceipt(BuyingController):
# Amount added through landed-cost-voucher
if d.landed_cost_voucher_amount and landed_cost_entries:
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({
"account": account,
"account_currency": account_currency,
"against": warehouse_account[d.warehouse]["account"],
"cost_center": d.cost_center,
"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
}, item=d))
@ -728,7 +731,13 @@ def get_item_account_wise_additional_cost(purchase_document):
for lcv in landed_cost_vouchers:
landed_cost_voucher_doc = frappe.get_doc("Landed Cost Voucher", lcv.parent)
based_on_field = frappe.scrub(landed_cost_voucher_doc.distribute_charges_based_on)
#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)
total_item_cost = 0
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:
for account in landed_cost_voucher_doc.taxes:
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)][account.expense_account] += \
item_account_wise_cost[(item.item_code, item.purchase_receipt_item)].setdefault(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
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

View File

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

View File

@ -3,6 +3,8 @@
frappe.provide("erpnext.stock");
frappe.provide("erpnext.accounts.dimensions");
{% include 'erpnext/stock/landed_taxes_and_charges_common.js' %};
frappe.ui.form.on('Stock Entry', {
setup: function(frm) {
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");
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) {
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",
@ -738,8 +731,18 @@ var validate_sample_quantity = function(frm, cdt, cdn) {
};
frappe.ui.form.on('Landed Cost Taxes and Charges', {
amount: function(frm) {
frm.events.calculate_amount(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);
}
},
expense_account: function(frm, cdt, cdn) {
frm.events.set_account_currency(frm, cdt, cdn);
}
});

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.stock_reconciliation.stock_reconciliation import OpeningEntryAccountError
from erpnext.accounts.general_ledger import process_gl_map
from erpnext.controllers.taxes_and_totals import init_landed_taxes_and_totals
import json
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)
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
`tabStock Entry` se, `tabLanded Cost Taxes and Charges` sed
where
@ -445,6 +446,7 @@ class StockEntry(StockController):
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)
init_landed_taxes_and_totals(self)
self.distribute_additional_costs()
self.update_valuation_rate()
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]):
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"):
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"):
if d.t_warehouse:
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
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
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:
for d in self.get("items"):
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,
"cost_center": d.cost_center,
"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))
gl_entries.append(self.get_gl_dict({
@ -798,7 +807,7 @@ class StockEntry(StockController):
"against": account,
"cost_center": d.cost_center,
"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))
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
else:
self.wh_data.valuation_rate = sle.outgoing_rate
else:
if flt(self.wh_data.qty_after_transaction) >= 0 and sle.outgoing_rate:
self.wh_data.valuation_rate = sle.outgoing_rate