Merge branch 'develop' into lcv_multicurrency

This commit is contained in:
Marica 2021-01-28 13:29:36 +05:30 committed by GitHub
commit 7aa16bafff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
48 changed files with 835 additions and 361 deletions

View File

@ -21,6 +21,7 @@
"book_asset_depreciation_entry_automatically", "book_asset_depreciation_entry_automatically",
"add_taxes_from_item_tax_template", "add_taxes_from_item_tax_template",
"automatically_fetch_payment_terms", "automatically_fetch_payment_terms",
"delete_linked_ledger_entries",
"deferred_accounting_settings_section", "deferred_accounting_settings_section",
"automatically_process_deferred_accounting_entry", "automatically_process_deferred_accounting_entry",
"book_deferred_entries_based_on", "book_deferred_entries_based_on",
@ -219,6 +220,12 @@
"fieldtype": "Select", "fieldtype": "Select",
"label": "Book Deferred Entries Based On", "label": "Book Deferred Entries Based On",
"options": "Days\nMonths" "options": "Days\nMonths"
},
{
"default": "0",
"fieldname": "delete_linked_ledger_entries",
"fieldtype": "Check",
"label": "Delete Accounting and Stock Ledger Entries on deletion of Transaction"
} }
], ],
"icon": "icon-cog", "icon": "icon-cog",
@ -226,7 +233,7 @@
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"issingle": 1, "issingle": 1,
"links": [], "links": [],
"modified": "2020-10-13 11:32:52.268826", "modified": "2021-01-05 13:04:00.118892",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Accounts Settings", "name": "Accounts Settings",

View File

@ -275,8 +275,11 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
supplier: function() { supplier: function() {
var me = this; var me = this;
if(this.frm.updating_party_details)
// Do not update if inter company reference is there as the details will already be updated
if(this.frm.updating_party_details || this.frm.doc.inter_company_invoice_reference)
return; return;
erpnext.utils.get_party_details(this.frm, "erpnext.accounts.party.get_party_details", erpnext.utils.get_party_details(this.frm, "erpnext.accounts.party.get_party_details",
{ {
posting_date: this.frm.doc.posting_date, posting_date: this.frm.doc.posting_date,

View File

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

View File

@ -511,8 +511,8 @@ class PurchaseInvoice(BuyingController):
voucher_wise_stock_value = {} voucher_wise_stock_value = {}
if self.update_stock: if self.update_stock:
for d in frappe.get_all('Stock Ledger Entry', for d in frappe.get_all('Stock Ledger Entry',
fields = ["voucher_detail_no", "stock_value_difference"], filters={'voucher_no': self.name}): fields = ["voucher_detail_no", "stock_value_difference", "warehouse"], filters={'voucher_no': self.name}):
voucher_wise_stock_value.setdefault(d.voucher_detail_no, d.stock_value_difference) voucher_wise_stock_value.setdefault((d.voucher_detail_no, d.warehouse), d.stock_value_difference)
valuation_tax_accounts = [d.account_head for d in self.get("taxes") valuation_tax_accounts = [d.account_head for d in self.get("taxes")
if d.category in ('Valuation', 'Total and Valuation') if d.category in ('Valuation', 'Total and Valuation')
@ -563,6 +563,7 @@ class PurchaseInvoice(BuyingController):
) )
else: else:
if not self.is_internal_transfer():
gl_entries.append( gl_entries.append(
self.get_gl_dict({ self.get_gl_dict({
"account": item.expense_account, "account": item.expense_account,
@ -625,6 +626,7 @@ class PurchaseInvoice(BuyingController):
if expense_booked_in_pr: if expense_booked_in_pr:
expense_account = service_received_but_not_billed_account expense_account = service_received_but_not_billed_account
if not self.is_internal_transfer():
gl_entries.append(self.get_gl_dict({ gl_entries.append(self.get_gl_dict({
"account": expense_account, "account": expense_account,
"against": self.supplier, "against": self.supplier,
@ -796,10 +798,10 @@ class PurchaseInvoice(BuyingController):
# Stock ledger value is not matching with the warehouse amount # Stock ledger value is not matching with the warehouse amount
if (self.update_stock and voucher_wise_stock_value.get(item.name) and if (self.update_stock and voucher_wise_stock_value.get(item.name) and
warehouse_debit_amount != flt(voucher_wise_stock_value.get(item.name), net_amt_precision)): warehouse_debit_amount != flt(voucher_wise_stock_value.get((item.name, item.warehouse)), net_amt_precision)):
cost_of_goods_sold_account = self.get_company_default("default_expense_account") cost_of_goods_sold_account = self.get_company_default("default_expense_account")
stock_amount = flt(voucher_wise_stock_value.get(item.name), net_amt_precision) stock_amount = flt(voucher_wise_stock_value.get((item.name, item.warehouse)), net_amt_precision)
stock_adjustment_amt = warehouse_debit_amount - stock_amount stock_adjustment_amt = warehouse_debit_amount - stock_amount
gl_entries.append( gl_entries.append(

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -34,6 +34,9 @@ def valdiate_taxes_and_charges_template(doc):
validate_disabled(doc) validate_disabled(doc)
# Validate with existing taxes and charges template for unique tax category
validate_for_tax_category(doc)
for tax in doc.get("taxes"): for tax in doc.get("taxes"):
validate_taxes_and_charges(tax) validate_taxes_and_charges(tax)
validate_inclusive_tax(tax, doc) validate_inclusive_tax(tax, doc)
@ -41,3 +44,7 @@ def valdiate_taxes_and_charges_template(doc):
def validate_disabled(doc): def validate_disabled(doc):
if doc.is_default and doc.disabled: if doc.is_default and doc.disabled:
frappe.throw(_("Disabled template must not be default template")) frappe.throw(_("Disabled template must not be default template"))
def validate_for_tax_category(doc):
if frappe.db.exists(doc.doctype, {"company": doc.company, "tax_category": doc.tax_category, "disabled": 0}):
frappe.throw(_("A template with tax category {0} already exists. Only one template is allowed with each tax category").format(frappe.bold(doc.tax_category)))

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -224,7 +224,7 @@ erpnext.buying.RequestforQuotationController = erpnext.buying.BuyingController.e
material_request_type: "Purchase", material_request_type: "Purchase",
docstatus: 1, docstatus: 1,
status: ["!=", "Stopped"], status: ["!=", "Stopped"],
per_ordered: ["<", 99.99], per_ordered: ["<", 100],
company: me.frm.doc.company company: me.frm.doc.company
} }
}) })
@ -280,7 +280,7 @@ erpnext.buying.RequestforQuotationController = erpnext.buying.BuyingController.e
material_request_type: "Purchase", material_request_type: "Purchase",
docstatus: 1, docstatus: 1,
status: ["!=", "Stopped"], status: ["!=", "Stopped"],
per_ordered: ["<", 99.99] per_ordered: ["<", 100]
} }
}); });
dialog.hide(); dialog.hide();

View File

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

View File

@ -44,7 +44,7 @@ erpnext.buying.SupplierQuotationController = erpnext.buying.BuyingController.ext
material_request_type: "Purchase", material_request_type: "Purchase",
docstatus: 1, docstatus: 1,
status: ["!=", "Stopped"], status: ["!=", "Stopped"],
per_ordered: ["<", 99.99], per_ordered: ["<", 100],
company: me.frm.doc.company company: me.frm.doc.company
} }
}) })

View File

@ -75,6 +75,9 @@ class AccountsController(TransactionBase):
self.ensure_supplier_is_not_blocked() self.ensure_supplier_is_not_blocked()
self.validate_date_with_fiscal_year() self.validate_date_with_fiscal_year()
self.validate_inter_company_reference()
self.set_incoming_rate()
if self.meta.get_field("currency"): if self.meta.get_field("currency"):
self.calculate_taxes_and_totals() self.calculate_taxes_and_totals()
@ -119,6 +122,12 @@ class AccountsController(TransactionBase):
def before_cancel(self): def before_cancel(self):
validate_einvoice_fields(self) validate_einvoice_fields(self)
def on_trash(self):
# delete sl and gl entries on deletion of transaction
if frappe.db.get_single_value('Accounts Settings', 'delete_linked_ledger_entries'):
frappe.db.sql("delete from `tabGL Entry` where voucher_type=%s and voucher_no=%s", (self.doctype, self.name))
frappe.db.sql("delete from `tabStock Ledger Entry` where voucher_type=%s and voucher_no=%s", (self.doctype, self.name))
def validate_deferred_start_and_end_date(self): def validate_deferred_start_and_end_date(self):
for d in self.items: for d in self.items:
if d.get("enable_deferred_revenue") or d.get("enable_deferred_expense"): if d.get("enable_deferred_revenue") or d.get("enable_deferred_expense"):
@ -206,6 +215,17 @@ class AccountsController(TransactionBase):
validate_fiscal_year(self.get(date_field), self.fiscal_year, self.company, validate_fiscal_year(self.get(date_field), self.fiscal_year, self.company,
self.meta.get_label(date_field), self) self.meta.get_label(date_field), self)
def validate_inter_company_reference(self):
if self.doctype not in ('Purchase Invoice', 'Purchase Receipt', 'Purchase Order'):
return
if self.is_internal_transfer():
if not (self.get('inter_company_reference') or self.get('inter_company_invoice_reference')
or self.get('inter_company_order_reference')):
msg = _("Internal Sale or Delivery Reference missing. ")
msg += _("Please create purchase from internal sale or delivery document itself")
frappe.throw(msg, title=_("Internal Sales Reference Missing"))
def validate_due_date(self): def validate_due_date(self):
if self.get('is_pos'): return if self.get('is_pos'): return
@ -964,9 +984,9 @@ class AccountsController(TransactionBase):
It will an internal transfer if its an internal customer and representation It will an internal transfer if its an internal customer and representation
company is same as billing company company is same as billing company
""" """
if self.doctype == 'Sales Invoice': if self.doctype in ('Sales Invoice', 'Delivery Note', 'Sales Order'):
internal_party_field = 'is_internal_customer' internal_party_field = 'is_internal_customer'
else: elif self.doctype in ('Purchase Invoice', 'Purchase Receipt', 'Purchase Order'):
internal_party_field = 'is_internal_supplier' internal_party_field = 'is_internal_supplier'
if self.get(internal_party_field) and (self.represents_company == self.company): if self.get(internal_party_field) and (self.represents_company == self.company):

View File

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

View File

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

View File

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

View File

@ -416,9 +416,6 @@ regional_overrides = {
'Italy': { 'Italy': {
'erpnext.controllers.taxes_and_totals.update_itemised_tax_data': 'erpnext.regional.italy.utils.update_itemised_tax_data', 'erpnext.controllers.taxes_and_totals.update_itemised_tax_data': 'erpnext.regional.italy.utils.update_itemised_tax_data',
'erpnext.controllers.accounts_controller.validate_regional': 'erpnext.regional.italy.utils.sales_invoice_validate', 'erpnext.controllers.accounts_controller.validate_regional': 'erpnext.regional.italy.utils.sales_invoice_validate',
},
'Germany': {
'erpnext.controllers.accounts_controller.validate_regional': 'erpnext.regional.germany.accounts_controller.validate_regional',
} }
} }
user_privacy_documents = [ user_privacy_documents = [

View File

@ -88,7 +88,7 @@ def get_events(start, end, filters=None):
def add_assignments(events, start, end, conditions=None): def add_assignments(events, start, end, conditions=None):
query = """select name, start_date, end_date, employee_name, query = """select name, start_date, end_date, employee_name,
employee, docstatus employee, docstatus, shift_type
from `tabShift Assignment` where from `tabShift Assignment` where
start_date >= %(start_date)s start_date >= %(start_date)s
or end_date <= %(end_date)s or end_date <= %(end_date)s
@ -97,19 +97,41 @@ def add_assignments(events, start, end, conditions=None):
if conditions: if conditions:
query += conditions query += conditions
for d in frappe.db.sql(query, {"start_date":start, "end_date":end}, as_dict=True): records = frappe.db.sql(query, {"start_date":start, "end_date":end}, as_dict=True)
shift_timing_map = get_shift_type_timing([d.shift_type for d in records])
for d in records:
daily_event_start = d.start_date
daily_event_end = d.end_date if d.end_date else getdate()
delta = timedelta(days=1)
while daily_event_start <= daily_event_end:
start_timing = frappe.utils.get_datetime(daily_event_start)+ shift_timing_map[d.shift_type]['start_time']
end_timing = frappe.utils.get_datetime(daily_event_start)+ shift_timing_map[d.shift_type]['end_time']
daily_event_start += delta
e = { e = {
"name": d.name, "name": d.name,
"doctype": "Shift Assignment", "doctype": "Shift Assignment",
"start_date": d.start_date, "start_date": start_timing,
"end_date": d.end_date if d.end_date else nowdate(), "end_date": end_timing,
"title": cstr(d.employee_name) + ": "+ \ "title": cstr(d.employee_name) + ": "+ \
cstr(d.shift_type), cstr(d.shift_type),
"docstatus": d.docstatus "docstatus": d.docstatus,
"allDay": 0
} }
if e not in events: if e not in events:
events.append(e) events.append(e)
return events
def get_shift_type_timing(shift_types):
shift_timing_map = {}
data = frappe.get_all("Shift Type", filters = {"name": ("IN", shift_types)}, fields = ['name', 'start_time', 'end_time'])
for d in data:
shift_timing_map[d.name] = d
return shift_timing_map
def get_employee_shift(employee, for_date=nowdate(), consider_default_shift=False, next_shift_direction=None): def get_employee_shift(employee, for_date=nowdate(), consider_default_shift=False, next_shift_direction=None):
"""Returns a Shift Type for the given employee on the given date. (excluding the holidays) """Returns a Shift Type for the given employee on the given date. (excluding the holidays)

View File

@ -6,14 +6,8 @@ frappe.views.calendar["Shift Assignment"] = {
"start": "start_date", "start": "start_date",
"end": "end_date", "end": "end_date",
"id": "name", "id": "name",
"docstatus": 1 "docstatus": 1,
}, "allDay": "allDay",
options: {
header: {
left: 'prev,next today',
center: 'title',
right: 'month'
}
}, },
get_events_method: "erpnext.hr.doctype.shift_assignment.shift_assignment.get_events" get_events_method: "erpnext.hr.doctype.shift_assignment.shift_assignment.get_events"
} }

View File

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

View File

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

View File

@ -1,53 +0,0 @@
import frappe
from frappe import _
from frappe import msgprint
REQUIRED_FIELDS = {
"Sales Invoice": [
{
"field_name": "company_address",
"regulation": "§ 14 Abs. 4 Nr. 1 UStG"
},
{
"field_name": "company_tax_id",
"regulation": "§ 14 Abs. 4 Nr. 2 UStG"
},
{
"field_name": "taxes",
"regulation": "§ 14 Abs. 4 Nr. 8 UStG"
},
{
"field_name": "customer_address",
"regulation": "§ 14 Abs. 4 Nr. 1 UStG",
"condition": "base_grand_total > 250"
}
]
}
def validate_regional(doc):
"""Check if required fields for this document are present."""
required_fields = REQUIRED_FIELDS.get(doc.doctype)
if not required_fields:
return
meta = frappe.get_meta(doc.doctype)
field_map = {field.fieldname: field.label for field in meta.fields}
for field in required_fields:
condition = field.get("condition")
if condition and not frappe.safe_eval(condition, doc.as_dict()):
continue
field_name = field.get("field_name")
regulation = field.get("regulation")
if field_name and not doc.get(field_name):
missing(field_map.get(field_name), regulation)
def missing(field_label, regulation):
"""Notify the user that a required field is missing."""
translated_msg = _('Remember to set {field_label}. It is required by {regulation}.', context='Specific for Germany. Example: Remember to set Company Tax ID. It is required by § 14 Abs. 4 Nr. 2 UStG.') # noqa: E501
formatted_msg = translated_msg.format(field_label=frappe.bold(_(field_label)), regulation=regulation)
msgprint(formatted_msg)

View File

@ -1,12 +0,0 @@
import frappe
import unittest
from erpnext.regional.germany.accounts_controller import validate_regional
class TestAccountsController(unittest.TestCase):
def setUp(self):
self.sales_invoice = frappe.get_last_doc('Sales Invoice')
def test_validate_regional(self):
validate_regional(self.sales_invoice)

View File

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

View File

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

View File

@ -255,6 +255,7 @@ class Gstr1Report(object):
for item_code, tax_amounts in item_wise_tax_detail.items(): for item_code, tax_amounts in item_wise_tax_detail.items():
tax_rate = tax_amounts[0] tax_rate = tax_amounts[0]
if tax_rate:
if cgst_or_sgst: if cgst_or_sgst:
tax_rate *= 2 tax_rate *= 2
if parent not in self.cgst_sgst_invoices: if parent not in self.cgst_sgst_invoices:

View File

@ -84,7 +84,10 @@ class Customer(TransactionBase):
frappe.throw(_("{0} is not a company bank account").format(frappe.bold(self.default_bank_account))) frappe.throw(_("{0} is not a company bank account").format(frappe.bold(self.default_bank_account)))
def validate_internal_customer(self): def validate_internal_customer(self):
if self.is_internal_customer and frappe.db.get_value('Customer', {"represents_company": self.represents_company}, "name"): internal_customer = frappe.db.get_value("Customer",
{"is_internal_customer": 1, "represents_company": self.represents_company, "name": ("!=", self.name)}, "name")
if internal_customer:
frappe.throw(_("Internal Customer for company {0} already exists").format( frappe.throw(_("Internal Customer for company {0} already exists").format(
frappe.bold(self.represents_company))) frappe.bold(self.represents_company)))

View File

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

View File

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

View File

@ -95,15 +95,21 @@ frappe.ui.form.on("Delivery Note", {
frm.page.set_inner_btn_group_as_primary(__('Create')); frm.page.set_inner_btn_group_as_primary(__('Create'));
} }
if (frm.doc.docstatus === 1 && frm.doc.is_internal_customer && !frm.doc.inter_company_reference) { if (frm.doc.docstatus == 1 && !frm.doc.inter_company_reference) {
frm.add_custom_button(__('Purchase Receipt'), function() { let internal = me.frm.doc.is_internal_customer;
if (internal) {
let button_label = (me.frm.doc.company === me.frm.doc.represents_company) ? "Internal Purchase Receipt" :
"Inter Company Purchase Receipt";
me.frm.add_custom_button(button_label, function() {
frappe.model.open_mapped_doc({ frappe.model.open_mapped_doc({
method: 'erpnext.stock.doctype.delivery_note.delivery_note.make_inter_company_purchase_receipt', method: 'erpnext.stock.doctype.delivery_note.delivery_note.make_inter_company_purchase_receipt',
frm: frm, frm: frm,
}) });
}, __('Create')); }, __('Create'));
} }
} }
}
}); });
frappe.ui.form.on("Delivery Note Item", { frappe.ui.form.on("Delivery Note Item", {
@ -297,15 +303,6 @@ erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend(
} }
}) })
}, },
to_warehouse: function() {
let packed_items_table = this.frm.doc["packed_items"];
this.autofill_warehouse(this.frm.doc["items"], "target_warehouse", this.frm.doc.to_warehouse);
if (packed_items_table && packed_items_table.length) {
this.autofill_warehouse(packed_items_table, "target_warehouse", this.frm.doc.to_warehouse);
}
}
}); });
$.extend(cur_frm.cscript, new erpnext.stock.DeliveryNoteController({frm: cur_frm})); $.extend(cur_frm.cscript, new erpnext.stock.DeliveryNoteController({frm: cur_frm}));

View File

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

View File

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

View File

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

View File

@ -106,9 +106,9 @@
"item_tax_section_break", "item_tax_section_break",
"taxes", "taxes",
"inspection_criteria", "inspection_criteria",
"quality_inspection_template",
"inspection_required_before_purchase", "inspection_required_before_purchase",
"inspection_required_before_delivery", "inspection_required_before_delivery",
"quality_inspection_template",
"manufacturing", "manufacturing",
"default_bom", "default_bom",
"is_sub_contracted_item", "is_sub_contracted_item",
@ -814,7 +814,6 @@
"label": "Inspection Required before Delivery" "label": "Inspection Required before Delivery"
}, },
{ {
"depends_on": "eval:(doc.inspection_required_before_purchase || doc.inspection_required_before_delivery)",
"fieldname": "quality_inspection_template", "fieldname": "quality_inspection_template",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Quality Inspection Template", "label": "Quality Inspection Template",
@ -1069,7 +1068,7 @@
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"links": [], "links": [],
"max_attachments": 1, "max_attachments": 1,
"modified": "2020-08-07 14:24:58.384992", "modified": "2021-01-25 20:49:50.222976",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Item", "name": "Item",

View File

@ -1,6 +1,7 @@
frappe.listview_settings['Material Request'] = { frappe.listview_settings['Material Request'] = {
add_fields: ["material_request_type", "status", "per_ordered", "per_received", "transfer_status"], add_fields: ["material_request_type", "status", "per_ordered", "per_received", "transfer_status"],
get_indicator: function(doc) { get_indicator: function(doc) {
var precision = frappe.defaults.get_default("float_precision");
if (doc.status=="Stopped") { if (doc.status=="Stopped") {
return [__("Stopped"), "red", "status,=,Stopped"]; return [__("Stopped"), "red", "status,=,Stopped"];
} else if (doc.transfer_status && doc.docstatus != 2) { } else if (doc.transfer_status && doc.docstatus != 2) {
@ -11,14 +12,14 @@ frappe.listview_settings['Material Request'] = {
} else if (doc.transfer_status == "Completed") { } else if (doc.transfer_status == "Completed") {
return [__("Completed"), "green"]; return [__("Completed"), "green"];
} }
} else if(doc.docstatus==1 && flt(doc.per_ordered, 2) == 0) { } else if (doc.docstatus==1 && flt(doc.per_ordered, precision) == 0) {
return [__("Pending"), "orange", "per_ordered,=,0"]; return [__("Pending"), "orange", "per_ordered,=,0"];
} else if(doc.docstatus==1 && flt(doc.per_ordered, 2) < 100) { } else if (doc.docstatus==1 && flt(doc.per_ordered, precision) < 100) {
return [__("Partially ordered"), "yellow", "per_ordered,<,100"]; return [__("Partially ordered"), "yellow", "per_ordered,<,100"];
} else if(doc.docstatus==1 && flt(doc.per_ordered, 2) == 100) { } else if (doc.docstatus==1 && flt(doc.per_ordered, precision) == 100) {
if (doc.material_request_type == "Purchase" && flt(doc.per_received, 2) < 100 && flt(doc.per_received, 2) > 0) { if (doc.material_request_type == "Purchase" && flt(doc.per_received, precision) < 100 && flt(doc.per_received, precision) > 0) {
return [__("Partially Received"), "yellow", "per_received,<,100"]; return [__("Partially Received"), "yellow", "per_received,<,100"];
} else if (doc.material_request_type == "Purchase" && flt(doc.per_received, 2) == 100) { } else if (doc.material_request_type == "Purchase" && flt(doc.per_received, precision) == 100) {
return [__("Received"), "green", "per_received,=,100"]; return [__("Received"), "green", "per_received,=,100"];
} else if (doc.material_request_type == "Purchase") { } else if (doc.material_request_type == "Purchase") {
return [__("Ordered"), "green", "per_ordered,=,100"]; return [__("Ordered"), "green", "per_ordered,=,100"];

View File

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

View File

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

View File

@ -7,6 +7,14 @@ frappe.provide("erpnext.accounts.dimensions");
frappe.ui.form.on('Stock Entry', { frappe.ui.form.on('Stock Entry', {
setup: function(frm) { setup: function(frm) {
frm.set_indicator_formatter('item_code', function(doc) {
if (!doc.s_warehouse) {
return 'blue';
} else {
return (doc.qty<=doc.actual_qty) ? 'green' : 'orange';
}
});
frm.set_query('work_order', function() { frm.set_query('work_order', function() {
return { return {
filters: [ filters: [
@ -782,15 +790,6 @@ erpnext.stock.StockEntry = erpnext.stock.StockController.extend({
} }
} }
this.frm.set_indicator_formatter('item_code',
function(doc) {
if (!doc.s_warehouse) {
return 'blue';
} else {
return (doc.qty<=doc.actual_qty) ? "green" : "orange"
}
})
this.frm.add_fetch("purchase_order", "supplier", "supplier"); this.frm.add_fetch("purchase_order", "supplier", "supplier");
frappe.dynamic_link = { doc: this.frm.doc, fieldname: 'supplier', doctype: 'Supplier' } frappe.dynamic_link = { doc: this.frm.doc, fieldname: 'supplier', doctype: 'Supplier' }

View File

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

View File

@ -40,7 +40,7 @@
<div class="col-md-{{ section.column_value }} mb-4"> <div class="col-md-{{ section.column_value }} mb-4">
<div class="card h-100 justify-content-between"> <div class="card h-100 justify-content-between">
{% if card.image %} {% if card.image %}
<div class="website-image-lazy" data-class="card-img-top h-100" data-src="{{ card.image }}" data-alt="{{ card.title }}"></div> <div class="website-image-lazy" data-class="card-img-top h-75" data-src="{{ card.image }}" data-alt="{{ card.title }}"></div>
{% endif %} {% endif %}
<div class="card-body"> <div class="card-body">
<h5 class="card-title">{{ card.title }}</h5> <h5 class="card-title">{{ card.title }}</h5>