Merge branch 'develop' into tcs_calculation
This commit is contained in:
commit
62dfff5dde
@ -254,7 +254,8 @@ def create_account(**kwargs):
|
||||
account_name = kwargs.get('account_name'),
|
||||
account_type = kwargs.get('account_type'),
|
||||
parent_account = kwargs.get('parent_account'),
|
||||
company = kwargs.get('company')
|
||||
company = kwargs.get('company'),
|
||||
account_currency = kwargs.get('account_currency')
|
||||
))
|
||||
|
||||
account.save()
|
||||
|
@ -165,9 +165,9 @@ def toggle_disabling(doc):
|
||||
frappe.clear_cache(doctype=doctype)
|
||||
|
||||
def get_doctypes_with_dimensions():
|
||||
doclist = ["GL Entry", "Sales Invoice", "Purchase Invoice", "Payment Entry", "Asset",
|
||||
doclist = ["GL Entry", "Sales Invoice", "POS Invoice", "Purchase Invoice", "Payment Entry", "Asset",
|
||||
"Expense Claim", "Expense Claim Detail", "Expense Taxes and Charges", "Stock Entry", "Budget", "Payroll Entry", "Delivery Note",
|
||||
"Sales Invoice Item", "Purchase Invoice Item", "Purchase Order Item", "Journal Entry Account", "Material Request Item", "Delivery Note Item",
|
||||
"Sales Invoice Item", "POS Invoice Item", "Purchase Invoice Item", "Purchase Order Item", "Journal Entry Account", "Material Request Item", "Delivery Note Item",
|
||||
"Purchase Receipt Item", "Stock Entry Detail", "Payment Entry Deduction", "Sales Taxes and Charges", "Purchase Taxes and Charges", "Shipping Rule",
|
||||
"Landed Cost Item", "Asset Value Adjustment", "Loyalty Program", "Fee Schedule", "Fee Structure", "Stock Reconciliation",
|
||||
"Travel Request", "Fees", "POS Profile", "Opening Invoice Creation Tool", "Opening Invoice Creation Tool Item", "Subscription",
|
||||
|
@ -21,6 +21,7 @@
|
||||
"book_asset_depreciation_entry_automatically",
|
||||
"add_taxes_from_item_tax_template",
|
||||
"automatically_fetch_payment_terms",
|
||||
"delete_linked_ledger_entries",
|
||||
"deferred_accounting_settings_section",
|
||||
"automatically_process_deferred_accounting_entry",
|
||||
"book_deferred_entries_based_on",
|
||||
@ -219,6 +220,12 @@
|
||||
"fieldtype": "Select",
|
||||
"label": "Book Deferred Entries Based On",
|
||||
"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",
|
||||
@ -226,7 +233,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2020-10-13 11:32:52.268826",
|
||||
"modified": "2021-01-05 13:04:00.118892",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Accounts Settings",
|
||||
@ -254,4 +261,4 @@
|
||||
"sort_field": "modified",
|
||||
"sort_order": "ASC",
|
||||
"track_changes": 1
|
||||
}
|
||||
}
|
@ -159,8 +159,8 @@ class GLEntry(Document):
|
||||
|
||||
if not self.flags.from_repost and not self.voucher_type == 'Period Closing Voucher' \
|
||||
and self.cost_center and _check_is_group():
|
||||
frappe.throw(_("""{0} {1}: Cost Center {2} is a group cost center and group cost centers cannot
|
||||
be used in transactions""").format(self.voucher_type, self.voucher_no, frappe.bold(self.cost_center)))
|
||||
frappe.throw(_("""{0} {1}: Cost Center {2} is a group cost center and group cost centers cannot be used in transactions""").format(
|
||||
self.voucher_type, self.voucher_no, frappe.bold(self.cost_center)))
|
||||
|
||||
def validate_party(self):
|
||||
validate_party_frozen_disabled(self.party_type, self.party)
|
||||
@ -170,7 +170,7 @@ class GLEntry(Document):
|
||||
account_currency = get_account_currency(self.account)
|
||||
|
||||
if not self.account_currency:
|
||||
self.account_currency = company_currency
|
||||
self.account_currency = account_currency or company_currency
|
||||
|
||||
if account_currency != self.account_currency:
|
||||
frappe.throw(_("{0} {1}: Accounting Entry for {2} can only be made in currency: {3}")
|
||||
|
@ -57,7 +57,11 @@ class POSClosingEntry(Document):
|
||||
if not invalid_rows:
|
||||
return
|
||||
|
||||
error_list = [_("Row #{}: {}").format(row.get('idx'), row.get('msg')) for row in invalid_rows]
|
||||
error_list = []
|
||||
for row in invalid_rows:
|
||||
for msg in row.get('msg'):
|
||||
error_list.append(_("Row #{}: {}").format(row.get('idx'), msg))
|
||||
|
||||
frappe.throw(error_list, title=_("Invalid POS Invoices"), as_list=True)
|
||||
|
||||
def on_submit(self):
|
||||
|
@ -2,6 +2,7 @@
|
||||
// For license information, please see license.txt
|
||||
|
||||
{% include 'erpnext/selling/sales_common.js' %};
|
||||
frappe.provide("erpnext.accounts");
|
||||
|
||||
erpnext.selling.POSInvoiceController = erpnext.selling.SellingController.extend({
|
||||
setup(doc) {
|
||||
@ -9,12 +10,18 @@ erpnext.selling.POSInvoiceController = erpnext.selling.SellingController.extend(
|
||||
this._super(doc);
|
||||
},
|
||||
|
||||
company: function() {
|
||||
erpnext.accounts.dimensions.update_dimension(this.frm, this.frm.doctype);
|
||||
},
|
||||
|
||||
onload(doc) {
|
||||
this._super();
|
||||
if(doc.__islocal && doc.is_pos && frappe.get_route_str() !== 'point-of-sale') {
|
||||
this.frm.script_manager.trigger("is_pos");
|
||||
this.frm.refresh_fields();
|
||||
}
|
||||
|
||||
erpnext.accounts.dimensions.setup_dimension_filters(this.frm, this.frm.doctype);
|
||||
},
|
||||
|
||||
refresh(doc) {
|
||||
|
@ -78,7 +78,7 @@ class POSInvoice(SalesInvoice):
|
||||
mode_of_payment=pay.mode_of_payment, status="Paid"),
|
||||
fieldname="grand_total")
|
||||
|
||||
if pay.amount != paid_amt:
|
||||
if paid_amt and pay.amount != paid_amt:
|
||||
return frappe.throw(_("Payment related to {0} is not completed").format(pay.mode_of_payment))
|
||||
|
||||
def validate_stock_availablility(self):
|
||||
@ -297,7 +297,9 @@ class POSInvoice(SalesInvoice):
|
||||
self.set(fieldname, profile.get(fieldname))
|
||||
|
||||
if self.customer:
|
||||
customer_price_list, customer_group = frappe.db.get_value("Customer", self.customer, ['default_price_list', 'customer_group'])
|
||||
customer_price_list, customer_group, customer_currency = frappe.db.get_value(
|
||||
"Customer", self.customer, ['default_price_list', 'customer_group', 'default_currency']
|
||||
)
|
||||
customer_group_price_list = frappe.db.get_value("Customer Group", customer_group, 'default_price_list')
|
||||
selling_price_list = customer_price_list or customer_group_price_list or profile.get('selling_price_list')
|
||||
else:
|
||||
@ -305,6 +307,8 @@ class POSInvoice(SalesInvoice):
|
||||
|
||||
if selling_price_list:
|
||||
self.set('selling_price_list', selling_price_list)
|
||||
if customer_currency != profile.get('currency'):
|
||||
self.set('currency', customer_currency)
|
||||
|
||||
# set pos values in items
|
||||
for item in self.get("items"):
|
||||
|
@ -5,10 +5,10 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.utils import cint, flt, add_months, today, date_diff, getdate, add_days, cstr, nowdate
|
||||
from frappe.model.document import Document
|
||||
from frappe.model.mapper import map_doc
|
||||
from frappe.model import default_fields
|
||||
from frappe.model.document import Document
|
||||
from frappe.model.mapper import map_doc, map_child_doc
|
||||
from frappe.utils import flt, getdate, nowdate
|
||||
|
||||
from six import iteritems
|
||||
|
||||
@ -83,7 +83,7 @@ class POSInvoiceMergeLog(Document):
|
||||
|
||||
credit_note.is_consolidated = 1
|
||||
# TODO: return could be against multiple sales invoice which could also have been consolidated?
|
||||
credit_note.return_against = self.consolidated_invoice
|
||||
# credit_note.return_against = self.consolidated_invoice
|
||||
credit_note.save()
|
||||
credit_note.submit()
|
||||
self.consolidated_credit_note = credit_note.name
|
||||
@ -111,7 +111,9 @@ class POSInvoiceMergeLog(Document):
|
||||
i.qty = i.qty + item.qty
|
||||
if not found:
|
||||
item.rate = item.net_rate
|
||||
items.append(item)
|
||||
item.price_list_rate = 0
|
||||
si_item = map_child_doc(item, invoice, {"doctype": "Sales Invoice Item"})
|
||||
items.append(si_item)
|
||||
|
||||
for tax in doc.get('taxes'):
|
||||
found = False
|
||||
@ -147,6 +149,8 @@ class POSInvoiceMergeLog(Document):
|
||||
invoice.set('taxes', taxes)
|
||||
invoice.additional_discount_percentage = 0
|
||||
invoice.discount_amount = 0.0
|
||||
invoice.taxes_and_charges = None
|
||||
invoice.ignore_pricing_rule = 1
|
||||
|
||||
return invoice
|
||||
|
||||
|
@ -12,8 +12,6 @@
|
||||
"company",
|
||||
"country",
|
||||
"column_break_9",
|
||||
"update_stock",
|
||||
"ignore_pricing_rule",
|
||||
"warehouse",
|
||||
"campaign",
|
||||
"company_address",
|
||||
@ -25,8 +23,14 @@
|
||||
"hide_images",
|
||||
"hide_unavailable_items",
|
||||
"auto_add_item_to_cart",
|
||||
"item_groups",
|
||||
"column_break_16",
|
||||
"update_stock",
|
||||
"ignore_pricing_rule",
|
||||
"allow_rate_change",
|
||||
"allow_discount_change",
|
||||
"section_break_23",
|
||||
"item_groups",
|
||||
"column_break_25",
|
||||
"customer_groups",
|
||||
"section_break_16",
|
||||
"print_format",
|
||||
@ -309,6 +313,7 @@
|
||||
"default": "1",
|
||||
"fieldname": "update_stock",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"label": "Update Stock",
|
||||
"read_only": 1
|
||||
},
|
||||
@ -329,13 +334,34 @@
|
||||
"fieldname": "auto_add_item_to_cart",
|
||||
"fieldtype": "Check",
|
||||
"label": "Automatically Add Filtered Item To Cart"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "allow_rate_change",
|
||||
"fieldtype": "Check",
|
||||
"label": "Allow User to Edit Rate"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "allow_discount_change",
|
||||
"fieldtype": "Check",
|
||||
"label": "Allow User to Edit Discount"
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"fieldname": "section_break_23",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_25",
|
||||
"fieldtype": "Column Break"
|
||||
}
|
||||
],
|
||||
"icon": "icon-cog",
|
||||
"idx": 1,
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2020-12-10 13:59:28.877572",
|
||||
"modified": "2021-01-06 14:42:41.713864",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "POS Profile",
|
||||
|
@ -275,8 +275,11 @@ erpnext.accounts.PurchaseInvoice = erpnext.buying.BuyingController.extend({
|
||||
|
||||
supplier: function() {
|
||||
var me = this;
|
||||
if(this.frm.updating_party_details)
|
||||
|
||||
// Do not update if inter company reference is there as the details will already be updated
|
||||
if(this.frm.updating_party_details || this.frm.doc.inter_company_invoice_reference)
|
||||
return;
|
||||
|
||||
erpnext.utils.get_party_details(this.frm, "erpnext.accounts.party.get_party_details",
|
||||
{
|
||||
posting_date: this.frm.doc.posting_date,
|
||||
|
@ -57,8 +57,8 @@
|
||||
"set_warehouse",
|
||||
"rejected_warehouse",
|
||||
"col_break_warehouse",
|
||||
"set_from_warehouse",
|
||||
"is_subcontracted",
|
||||
"supplier_warehouse",
|
||||
"items_section",
|
||||
"update_stock",
|
||||
"scan_barcode",
|
||||
@ -515,6 +515,7 @@
|
||||
},
|
||||
{
|
||||
"depends_on": "update_stock",
|
||||
"description": "Sets 'Accepted Warehouse' in each row of the items table.",
|
||||
"fieldname": "set_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"label": "Set Accepted Warehouse",
|
||||
@ -543,17 +544,6 @@
|
||||
"options": "No\nYes",
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.is_subcontracted==\"Yes\"",
|
||||
"fieldname": "supplier_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"label": "Supplier Warehouse",
|
||||
"no_copy": 1,
|
||||
"options": "Warehouse",
|
||||
"print_hide": 1,
|
||||
"print_width": "50px",
|
||||
"width": "50px"
|
||||
},
|
||||
{
|
||||
"fieldname": "items_section",
|
||||
"fieldtype": "Section Break",
|
||||
@ -1232,7 +1222,9 @@
|
||||
"fieldname": "inter_company_invoice_reference",
|
||||
"fieldtype": "Link",
|
||||
"label": "Inter Company Invoice Reference",
|
||||
"no_copy": 1,
|
||||
"options": "Sales Invoice",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
@ -1356,13 +1348,25 @@
|
||||
"fieldtype": "Link",
|
||||
"label": "Represents Company",
|
||||
"options": "Company"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.update_stock && (doc.is_subcontracted==\"Yes\" || doc.is_internal_supplier)",
|
||||
"description": "Sets 'From Warehouse' in each row of the items table.",
|
||||
"fieldname": "set_from_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"label": "Set From Warehouse",
|
||||
"no_copy": 1,
|
||||
"options": "Warehouse",
|
||||
"print_hide": 1,
|
||||
"print_width": "50px",
|
||||
"width": "50px"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-file-text",
|
||||
"idx": 204,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-12-11 12:46:12.796378",
|
||||
"modified": "2020-12-26 20:49:03.305063",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Invoice",
|
||||
|
@ -443,7 +443,7 @@ class PurchaseInvoice(BuyingController):
|
||||
else:
|
||||
self.stock_received_but_not_billed = None
|
||||
self.expenses_included_in_valuation = None
|
||||
|
||||
|
||||
self.negative_expense_to_be_booked = 0.0
|
||||
gl_entries = []
|
||||
|
||||
@ -457,7 +457,7 @@ class PurchaseInvoice(BuyingController):
|
||||
self.make_internal_transfer_gl_entries(gl_entries)
|
||||
|
||||
gl_entries = make_regional_gl_entries(gl_entries, self)
|
||||
|
||||
|
||||
gl_entries = merge_similar_entries(gl_entries)
|
||||
|
||||
self.make_payment_gl_entries(gl_entries)
|
||||
@ -480,7 +480,7 @@ class PurchaseInvoice(BuyingController):
|
||||
grand_total = self.rounded_total if (self.rounding_adjustment and self.rounded_total) else self.grand_total
|
||||
|
||||
if grand_total and not self.is_internal_transfer():
|
||||
# Didnot use base_grand_total to book rounding loss gle
|
||||
# Did not use base_grand_total to book rounding loss gle
|
||||
grand_total_in_company_currency = flt(grand_total * self.conversion_rate,
|
||||
self.precision("grand_total"))
|
||||
gl_entries.append(
|
||||
@ -511,8 +511,8 @@ class PurchaseInvoice(BuyingController):
|
||||
voucher_wise_stock_value = {}
|
||||
if self.update_stock:
|
||||
for d in frappe.get_all('Stock Ledger Entry',
|
||||
fields = ["voucher_detail_no", "stock_value_difference"], filters={'voucher_no': self.name}):
|
||||
voucher_wise_stock_value.setdefault(d.voucher_detail_no, d.stock_value_difference)
|
||||
fields = ["voucher_detail_no", "stock_value_difference", "warehouse"], filters={'voucher_no': self.name}):
|
||||
voucher_wise_stock_value.setdefault((d.voucher_detail_no, d.warehouse), d.stock_value_difference)
|
||||
|
||||
valuation_tax_accounts = [d.account_head for d in self.get("taxes")
|
||||
if d.category in ('Valuation', 'Total and Valuation')
|
||||
@ -563,16 +563,17 @@ class PurchaseInvoice(BuyingController):
|
||||
)
|
||||
|
||||
else:
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
"account": item.expense_account,
|
||||
"against": self.supplier,
|
||||
"debit": warehouse_debit_amount,
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||
"cost_center": item.cost_center,
|
||||
"project": item.project or self.project
|
||||
}, account_currency, item=item)
|
||||
)
|
||||
if not self.is_internal_transfer():
|
||||
gl_entries.append(
|
||||
self.get_gl_dict({
|
||||
"account": item.expense_account,
|
||||
"against": self.supplier,
|
||||
"debit": warehouse_debit_amount,
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||
"cost_center": item.cost_center,
|
||||
"project": item.project or self.project
|
||||
}, account_currency, item=item)
|
||||
)
|
||||
|
||||
# Amount added through landed-cost-voucher
|
||||
if landed_cost_entries:
|
||||
@ -582,7 +583,8 @@ class PurchaseInvoice(BuyingController):
|
||||
"against": item.expense_account,
|
||||
"cost_center": item.cost_center,
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||
"credit": flt(amount),
|
||||
"credit": flt(amount["base_amount"]),
|
||||
"credit_in_account_currency": flt(amount["amount"]),
|
||||
"project": item.project or self.project
|
||||
}, item=item))
|
||||
|
||||
@ -624,13 +626,14 @@ class PurchaseInvoice(BuyingController):
|
||||
if expense_booked_in_pr:
|
||||
expense_account = service_received_but_not_billed_account
|
||||
|
||||
gl_entries.append(self.get_gl_dict({
|
||||
"account": expense_account,
|
||||
"against": self.supplier,
|
||||
"debit": amount,
|
||||
"cost_center": item.cost_center,
|
||||
"project": item.project or self.project
|
||||
}, account_currency, item=item))
|
||||
if not self.is_internal_transfer():
|
||||
gl_entries.append(self.get_gl_dict({
|
||||
"account": expense_account,
|
||||
"against": self.supplier,
|
||||
"debit": amount,
|
||||
"cost_center": item.cost_center,
|
||||
"project": item.project or self.project
|
||||
}, account_currency, item=item))
|
||||
|
||||
# If asset is bought through this document and not linked to PR
|
||||
if self.update_stock and item.landed_cost_voucher_amount:
|
||||
@ -795,10 +798,10 @@ class PurchaseInvoice(BuyingController):
|
||||
|
||||
# Stock ledger value is not matching with the warehouse amount
|
||||
if (self.update_stock and voucher_wise_stock_value.get(item.name) and
|
||||
warehouse_debit_amount != flt(voucher_wise_stock_value.get(item.name), net_amt_precision)):
|
||||
warehouse_debit_amount != flt(voucher_wise_stock_value.get((item.name, item.warehouse)), net_amt_precision)):
|
||||
|
||||
cost_of_goods_sold_account = self.get_company_default("default_expense_account")
|
||||
stock_amount = flt(voucher_wise_stock_value.get(item.name), net_amt_precision)
|
||||
stock_amount = flt(voucher_wise_stock_value.get((item.name, item.warehouse)), net_amt_precision)
|
||||
stock_adjustment_amt = warehouse_debit_amount - stock_amount
|
||||
|
||||
gl_entries.append(
|
||||
@ -999,10 +1002,10 @@ class PurchaseInvoice(BuyingController):
|
||||
self.delete_auto_created_batches()
|
||||
|
||||
self.make_gl_entries_on_cancel()
|
||||
|
||||
|
||||
if self.update_stock == 1:
|
||||
self.repost_future_sle_and_gle()
|
||||
|
||||
|
||||
self.update_project()
|
||||
frappe.db.set(self, 'status', 'Cancelled')
|
||||
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"actions": [],
|
||||
"autoname": "hash",
|
||||
"creation": "2013-05-22 12:43:10",
|
||||
"doctype": "DocType",
|
||||
@ -87,6 +88,7 @@
|
||||
"po_detail",
|
||||
"purchase_receipt",
|
||||
"pr_detail",
|
||||
"sales_invoice_item",
|
||||
"item_weight_details",
|
||||
"weight_per_unit",
|
||||
"total_weight",
|
||||
@ -553,8 +555,8 @@
|
||||
"fieldtype": "Link",
|
||||
"hidden": 1,
|
||||
"label": "Brand",
|
||||
"print_hide": 1,
|
||||
"options": "Brand"
|
||||
"options": "Brand",
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"fetch_from": "item_code.item_group",
|
||||
@ -562,9 +564,9 @@
|
||||
"fieldname": "item_group",
|
||||
"fieldtype": "Link",
|
||||
"label": "Item Group",
|
||||
"options": "Item Group",
|
||||
"print_hide": 1,
|
||||
"read_only": 1,
|
||||
"options": "Item Group"
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"description": "Tax detail table fetched from item master as a string and stored in this field.\nUsed for Taxes and Charges",
|
||||
@ -759,10 +761,11 @@
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:parent.is_internal_supplier && parent.update_stock",
|
||||
"fieldname": "from_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"ignore_user_permissions": 1,
|
||||
"label": "Supplier Warehouse",
|
||||
"label": "From Warehouse",
|
||||
"options": "Warehouse"
|
||||
},
|
||||
{
|
||||
@ -779,11 +782,20 @@
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "sales_invoice_item",
|
||||
"fieldtype": "Data",
|
||||
"label": "Sales Invoice Item",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"modified": "2020-08-20 11:48:01.398356",
|
||||
"links": [],
|
||||
"modified": "2020-12-26 17:20:36.415791",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Purchase Invoice Item",
|
||||
@ -791,4 +803,4 @@
|
||||
"permissions": [],
|
||||
"sort_field": "modified",
|
||||
"sort_order": "DESC"
|
||||
}
|
||||
}
|
@ -130,16 +130,15 @@ erpnext.accounts.SalesInvoiceController = erpnext.selling.SellingController.exte
|
||||
|
||||
this.set_default_print_format();
|
||||
if (doc.docstatus == 1 && !doc.inter_company_invoice_reference) {
|
||||
frappe.model.with_doc("Customer", me.frm.doc.customer, function() {
|
||||
var customer = frappe.model.get_doc("Customer", me.frm.doc.customer);
|
||||
var internal = customer.is_internal_customer;
|
||||
var disabled = customer.disabled;
|
||||
if (internal == 1 && disabled == 0) {
|
||||
me.frm.add_custom_button("Inter Company Invoice", function() {
|
||||
me.make_inter_company_invoice();
|
||||
}, __('Create'));
|
||||
}
|
||||
});
|
||||
let internal = me.frm.doc.is_internal_customer;
|
||||
if (internal) {
|
||||
let button_label = (me.frm.doc.company === me.frm.doc.represents_company) ? "Internal Purchase Invoice" :
|
||||
"Inter Company Purchase Invoice";
|
||||
|
||||
me.frm.add_custom_button(button_label, function() {
|
||||
me.make_inter_company_invoice();
|
||||
}, __('Create'));
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -60,6 +60,8 @@
|
||||
"ignore_pricing_rule",
|
||||
"sec_warehouse",
|
||||
"set_warehouse",
|
||||
"column_break_55",
|
||||
"set_target_warehouse",
|
||||
"items_section",
|
||||
"update_stock",
|
||||
"scan_barcode",
|
||||
@ -1969,13 +1971,24 @@
|
||||
"label": "Represents Company",
|
||||
"options": "Company",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_55",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.is_internal_customer && doc.update_stock",
|
||||
"fieldname": "set_target_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"label": "Set Target Warehouse",
|
||||
"options": "Warehouse"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-file-text",
|
||||
"idx": 181,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-12-11 12:48:31.769958",
|
||||
"modified": "2020-12-25 22:57:32.555067",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice",
|
||||
|
@ -6,7 +6,7 @@ import frappe, erpnext
|
||||
import frappe.defaults
|
||||
from frappe.utils import cint, flt, getdate, add_days, cstr, nowdate, get_link_to_form, formatdate
|
||||
from frappe import _, msgprint, throw
|
||||
from erpnext.accounts.party import get_party_account, get_due_date
|
||||
from erpnext.accounts.party import get_party_account, get_due_date, get_party_details
|
||||
from frappe.model.mapper import get_mapped_doc
|
||||
from erpnext.controllers.selling_controller import SellingController
|
||||
from erpnext.accounts.utils import get_account_currency
|
||||
@ -22,6 +22,8 @@ from erpnext.accounts.doctype.loyalty_program.loyalty_program import \
|
||||
get_loyalty_program_details_with_points, get_loyalty_details, validate_loyalty_points
|
||||
from erpnext.accounts.deferred_revenue import validate_service_stop_date
|
||||
from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import get_party_tax_withholding_details
|
||||
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
|
||||
|
||||
@ -462,7 +464,9 @@ class SalesInvoice(SellingController):
|
||||
if not for_validate and not self.customer:
|
||||
self.customer = pos.customer
|
||||
|
||||
self.ignore_pricing_rule = pos.ignore_pricing_rule
|
||||
if not for_validate:
|
||||
self.ignore_pricing_rule = pos.ignore_pricing_rule
|
||||
|
||||
if pos.get('account_for_change_amount'):
|
||||
self.account_for_change_amount = pos.get('account_for_change_amount')
|
||||
|
||||
@ -1563,7 +1567,7 @@ def validate_inter_company_transaction(doc, doctype):
|
||||
details = get_inter_company_details(doc, doctype)
|
||||
price_list = doc.selling_price_list if doctype in ["Sales Invoice", "Sales Order", "Delivery Note"] else doc.buying_price_list
|
||||
valid_price_list = frappe.db.get_value("Price List", {"name": price_list, "buying": 1, "selling": 1})
|
||||
if not valid_price_list:
|
||||
if not valid_price_list and not doc.is_internal_transfer():
|
||||
frappe.throw(_("Selected Price List should have buying and selling fields checked."))
|
||||
|
||||
party = details.get("party")
|
||||
@ -1586,6 +1590,7 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
|
||||
if doctype in ["Sales Invoice", "Sales Order"]:
|
||||
source_doc = frappe.get_doc(doctype, source_name)
|
||||
target_doctype = "Purchase Invoice" if doctype == "Sales Invoice" else "Purchase Order"
|
||||
target_detail_field = "sales_invoice_item" if doctype == "Sales Invoice" else "sales_order_item"
|
||||
source_document_warehouse_field = 'target_warehouse'
|
||||
target_document_warehouse_field = 'from_warehouse'
|
||||
else:
|
||||
@ -1599,6 +1604,7 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
|
||||
|
||||
def set_missing_values(source, target):
|
||||
target.run_method("set_missing_values")
|
||||
set_purchase_references(target)
|
||||
|
||||
def update_details(source_doc, target_doc, source_parent):
|
||||
target_doc.inter_company_invoice_reference = source_doc.name
|
||||
@ -1606,19 +1612,38 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
|
||||
currency = frappe.db.get_value('Supplier', details.get('party'), 'default_currency')
|
||||
target_doc.company = details.get("company")
|
||||
target_doc.supplier = details.get("party")
|
||||
target_doc.is_internal_supplier = 1
|
||||
target_doc.ignore_pricing_rule = 1
|
||||
target_doc.buying_price_list = source_doc.selling_price_list
|
||||
|
||||
# Invert Addresses
|
||||
update_address(target_doc, 'supplier_address', 'address_display', source_doc.company_address)
|
||||
update_address(target_doc, 'shipping_address', 'shipping_address_display', source_doc.customer_address)
|
||||
|
||||
if currency:
|
||||
target_doc.currency = currency
|
||||
|
||||
update_taxes(target_doc, party=target_doc.supplier, party_type='Supplier', company=target_doc.company,
|
||||
doctype=target_doc.doctype, party_address=target_doc.supplier_address,
|
||||
company_address=target_doc.shipping_address)
|
||||
|
||||
else:
|
||||
currency = frappe.db.get_value('Customer', details.get('party'), 'default_currency')
|
||||
target_doc.company = details.get("company")
|
||||
target_doc.customer = details.get("party")
|
||||
target_doc.selling_price_list = source_doc.buying_price_list
|
||||
|
||||
update_address(target_doc, 'company_address', 'company_address_display', source_doc.supplier_address)
|
||||
update_address(target_doc, 'shipping_address_name', 'shipping_address', source_doc.shipping_address)
|
||||
update_address(target_doc, 'customer_address', 'address_display', source_doc.shipping_address)
|
||||
|
||||
if currency:
|
||||
target_doc.currency = currency
|
||||
|
||||
update_taxes(target_doc, party=target_doc.customer, party_type='Customer', company=target_doc.company,
|
||||
doctype=target_doc.doctype, party_address=target_doc.customer_address,
|
||||
company_address=target_doc.company_address, shipping_address_name=target_doc.shipping_address_name)
|
||||
|
||||
item_field_map = {
|
||||
"doctype": target_doctype + " Item",
|
||||
"field_no_map": [
|
||||
@ -1626,25 +1651,33 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
|
||||
"expense_account",
|
||||
"cost_center",
|
||||
"warehouse"
|
||||
]
|
||||
],
|
||||
"field_map": {
|
||||
'rate': 'rate',
|
||||
}
|
||||
}
|
||||
|
||||
if source_doc.get('update_stock'):
|
||||
item_field_map.update({
|
||||
'field_map': {
|
||||
source_document_warehouse_field: target_document_warehouse_field,
|
||||
'batch_no': 'batch_no',
|
||||
'serial_no': 'serial_no'
|
||||
}
|
||||
if doctype in ["Sales Invoice", "Sales Order"]:
|
||||
item_field_map["field_map"].update({
|
||||
"name": target_detail_field,
|
||||
})
|
||||
|
||||
if source_doc.get('update_stock'):
|
||||
item_field_map["field_map"].update({
|
||||
source_document_warehouse_field: target_document_warehouse_field,
|
||||
'batch_no': 'batch_no',
|
||||
'serial_no': 'serial_no'
|
||||
})
|
||||
|
||||
doclist = get_mapped_doc(doctype, source_name, {
|
||||
doctype: {
|
||||
"doctype": target_doctype,
|
||||
"postprocess": update_details,
|
||||
"set_target_warehouse": "set_from_warehouse",
|
||||
"field_no_map": [
|
||||
"taxes_and_charges"
|
||||
"taxes_and_charges",
|
||||
"set_warehouse",
|
||||
"shipping_address"
|
||||
]
|
||||
},
|
||||
doctype +" Item": item_field_map
|
||||
@ -1653,6 +1686,110 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
|
||||
|
||||
return doclist
|
||||
|
||||
def set_purchase_references(doc):
|
||||
# add internal PO or PR links if any
|
||||
if doc.is_internal_transfer():
|
||||
if doc.doctype == 'Purchase Receipt':
|
||||
so_item_map = get_delivery_note_details(doc.inter_company_invoice_reference)
|
||||
|
||||
if so_item_map:
|
||||
pd_item_map, parent_child_map, warehouse_map = \
|
||||
get_pd_details('Purchase Order Item', so_item_map, 'sales_order_item')
|
||||
|
||||
update_pr_items(doc, so_item_map, pd_item_map, parent_child_map, warehouse_map)
|
||||
|
||||
elif doc.doctype == 'Purchase Invoice':
|
||||
dn_item_map, so_item_map = get_sales_invoice_details(doc.inter_company_invoice_reference)
|
||||
# First check for Purchase receipt
|
||||
if list(dn_item_map.values()):
|
||||
pd_item_map, parent_child_map, warehouse_map = \
|
||||
get_pd_details('Purchase Receipt Item', dn_item_map, 'delivery_note_item')
|
||||
|
||||
update_pi_items(doc, 'pr_detail', 'purchase_receipt',
|
||||
dn_item_map, pd_item_map, parent_child_map, warehouse_map)
|
||||
|
||||
if list(so_item_map.values()):
|
||||
pd_item_map, parent_child_map, warehouse_map = \
|
||||
get_pd_details('Purchase Order Item', so_item_map, 'sales_order_item')
|
||||
|
||||
update_pi_items(doc, 'po_detail', 'purchase_order',
|
||||
so_item_map, pd_item_map, parent_child_map, warehouse_map)
|
||||
|
||||
def update_pi_items(doc, detail_field, parent_field, sales_item_map,
|
||||
purchase_item_map, parent_child_map, warehouse_map):
|
||||
for item in doc.get('items'):
|
||||
item.set(detail_field, purchase_item_map.get(sales_item_map.get(item.sales_invoice_item)))
|
||||
item.set(parent_field, parent_child_map.get(sales_item_map.get(item.sales_invoice_item)))
|
||||
if doc.update_stock:
|
||||
item.warehouse = warehouse_map.get(sales_item_map.get(item.sales_invoice_item))
|
||||
|
||||
def update_pr_items(doc, sales_item_map, purchase_item_map, parent_child_map, warehouse_map):
|
||||
for item in doc.get('items'):
|
||||
item.purchase_order_item = purchase_item_map.get(sales_item_map.get(item.delivery_note_item))
|
||||
item.warehouse = warehouse_map.get(sales_item_map.get(item.delivery_note_item))
|
||||
item.purchase_order = parent_child_map.get(sales_item_map.get(item.delivery_note_item))
|
||||
|
||||
def get_delivery_note_details(internal_reference):
|
||||
so_item_map = {}
|
||||
|
||||
si_item_details = frappe.get_all('Delivery Note Item', fields=['name', 'so_detail'],
|
||||
filters={'parent': internal_reference})
|
||||
|
||||
for d in si_item_details:
|
||||
so_item_map.setdefault(d.name, d.so_detail)
|
||||
|
||||
return so_item_map
|
||||
|
||||
def get_sales_invoice_details(internal_reference):
|
||||
dn_item_map = {}
|
||||
so_item_map = {}
|
||||
|
||||
si_item_details = frappe.get_all('Sales Invoice Item', fields=['name', 'so_detail',
|
||||
'dn_detail'], filters={'parent': internal_reference})
|
||||
|
||||
for d in si_item_details:
|
||||
if d.dn_detail:
|
||||
dn_item_map.setdefault(d.name, d.dn_detail)
|
||||
if d.so_detail:
|
||||
so_item_map.setdefault(d.name, d.so_detail)
|
||||
|
||||
return dn_item_map, so_item_map
|
||||
|
||||
def get_pd_details(doctype, sd_detail_map, sd_detail_field):
|
||||
pd_item_map = {}
|
||||
accepted_warehouse_map = {}
|
||||
parent_child_map = {}
|
||||
|
||||
pd_item_details = frappe.get_all(doctype,
|
||||
fields=[sd_detail_field, 'name', 'warehouse', 'parent'], filters={sd_detail_field: ('in', list(sd_detail_map.values()))})
|
||||
|
||||
for d in pd_item_details:
|
||||
pd_item_map.setdefault(d.get(sd_detail_field), d.name)
|
||||
parent_child_map.setdefault(d.get(sd_detail_field), d.parent)
|
||||
accepted_warehouse_map.setdefault(d.get(sd_detail_field), d.warehouse)
|
||||
|
||||
return pd_item_map, parent_child_map, accepted_warehouse_map
|
||||
|
||||
def update_taxes(doc, party=None, party_type=None, company=None, doctype=None, party_address=None,
|
||||
company_address=None, shipping_address_name=None, master_doctype=None):
|
||||
# Update Party Details
|
||||
party_details = get_party_details(party=party, party_type=party_type, company=company,
|
||||
doctype=doctype, party_address=party_address, company_address=company_address,
|
||||
shipping_address=shipping_address_name)
|
||||
|
||||
# Update taxes and charges if any
|
||||
doc.taxes_and_charges = party_details.get('taxes_and_charges')
|
||||
doc.set('taxes', party_details.get('taxes'))
|
||||
|
||||
def update_address(doc, address_field, address_display_field, address_name):
|
||||
doc.set(address_field, address_name)
|
||||
fetch_values = get_fetch_values(doc.doctype, address_field, address_name)
|
||||
|
||||
for key, value in fetch_values.items():
|
||||
doc.set(key, value)
|
||||
|
||||
doc.set(address_display_field, get_address_display(doc.get(address_field)))
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_loyalty_programs(customer):
|
||||
''' sets applicable loyalty program to the customer or returns a list of applicable programs '''
|
||||
|
@ -22,6 +22,7 @@ from erpnext.regional.india.utils import get_ewb_data
|
||||
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
|
||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
|
||||
from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice
|
||||
from erpnext.stock.utils import get_incoming_rate
|
||||
|
||||
class TestSalesInvoice(unittest.TestCase):
|
||||
def make(self):
|
||||
@ -688,7 +689,7 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
self.assertTrue(gle)
|
||||
|
||||
def test_pos_gl_entry_with_perpetual_inventory(self):
|
||||
make_pos_profile(company="_Test Company with perpetual inventory", income_account = "Sales - TCP1",
|
||||
make_pos_profile(company="_Test Company with perpetual inventory", income_account = "Sales - TCP1",
|
||||
expense_account = "Cost of Goods Sold - TCP1", warehouse="Stores - TCP1", cost_center = "Main - TCP1", write_off_account="_Test Write Off - TCP1")
|
||||
|
||||
pr = make_purchase_receipt(company= "_Test Company with perpetual inventory", item_code= "_Test FG Item",warehouse= "Stores - TCP1",cost_center= "Main - TCP1")
|
||||
@ -745,7 +746,7 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
self.assertEqual(pos_return.get('payments')[0].amount, -1000)
|
||||
|
||||
def test_pos_change_amount(self):
|
||||
make_pos_profile(company="_Test Company with perpetual inventory", income_account = "Sales - TCP1",
|
||||
make_pos_profile(company="_Test Company with perpetual inventory", income_account = "Sales - TCP1",
|
||||
expense_account = "Cost of Goods Sold - TCP1", warehouse="Stores - TCP1", cost_center = "Main - TCP1", write_off_account="_Test Write Off - TCP1")
|
||||
|
||||
pr = make_purchase_receipt(company= "_Test Company with perpetual inventory",
|
||||
@ -1770,59 +1771,82 @@ class TestSalesInvoice(unittest.TestCase):
|
||||
self.assertEqual(target_doc.company, "_Test Company 1")
|
||||
self.assertEqual(target_doc.supplier, "_Test Internal Supplier")
|
||||
|
||||
# def test_internal_transfer_gl_entry(self):
|
||||
# ## Create internal transfer account
|
||||
# account = create_account(account_name="Unrealized Profit",
|
||||
# parent_account="Current Liabilities - TCP1", company="_Test Company with perpetual inventory")
|
||||
def test_internal_transfer_gl_entry(self):
|
||||
## Create internal transfer account
|
||||
account = create_account(account_name="Unrealized Profit",
|
||||
parent_account="Current Liabilities - TCP1", company="_Test Company with perpetual inventory")
|
||||
|
||||
# frappe.db.set_value('Company', '_Test Company with perpetual inventory',
|
||||
# 'unrealized_profit_loss_account', account)
|
||||
frappe.db.set_value('Company', '_Test Company with perpetual inventory',
|
||||
'unrealized_profit_loss_account', account)
|
||||
|
||||
# customer = create_internal_customer("_Test Internal Customer 2", "_Test Company with perpetual inventory",
|
||||
# "_Test Company with perpetual inventory")
|
||||
customer = create_internal_customer("_Test Internal Customer 2", "_Test Company with perpetual inventory",
|
||||
"_Test Company with perpetual inventory")
|
||||
|
||||
# create_internal_supplier("_Test Internal Supplier 2", "_Test Company with perpetual inventory",
|
||||
# "_Test Company with perpetual inventory")
|
||||
create_internal_supplier("_Test Internal Supplier 2", "_Test Company with perpetual inventory",
|
||||
"_Test Company with perpetual inventory")
|
||||
|
||||
# si = create_sales_invoice(
|
||||
# company = "_Test Company with perpetual inventory",
|
||||
# customer = customer,
|
||||
# debit_to = "Debtors - TCP1",
|
||||
# warehouse = "Stores - TCP1",
|
||||
# income_account = "Sales - TCP1",
|
||||
# expense_account = "Cost of Goods Sold - TCP1",
|
||||
# cost_center = "Main - TCP1",
|
||||
# currency = "INR",
|
||||
# do_not_save = 1
|
||||
# )
|
||||
si = create_sales_invoice(
|
||||
company = "_Test Company with perpetual inventory",
|
||||
customer = customer,
|
||||
debit_to = "Debtors - TCP1",
|
||||
warehouse = "Stores - TCP1",
|
||||
income_account = "Sales - TCP1",
|
||||
expense_account = "Cost of Goods Sold - TCP1",
|
||||
cost_center = "Main - TCP1",
|
||||
currency = "INR",
|
||||
do_not_save = 1
|
||||
)
|
||||
|
||||
# si.selling_price_list = "_Test Price List Rest of the World"
|
||||
# si.update_stock = 1
|
||||
# si.items[0].target_warehouse = 'Work In Progress - TCP1'
|
||||
# add_taxes(si)
|
||||
# si.save()
|
||||
# si.submit()
|
||||
si.selling_price_list = "_Test Price List Rest of the World"
|
||||
si.update_stock = 1
|
||||
si.items[0].target_warehouse = 'Work In Progress - TCP1'
|
||||
add_taxes(si)
|
||||
si.save()
|
||||
|
||||
# target_doc = make_inter_company_transaction("Sales Invoice", si.name)
|
||||
# target_doc.company = '_Test Company with perpetual inventory'
|
||||
# target_doc.items[0].warehouse = 'Finished Goods - TCP1'
|
||||
# add_taxes(target_doc)
|
||||
# target_doc.save()
|
||||
# target_doc.submit()
|
||||
rate = 0.0
|
||||
for d in si.get('items'):
|
||||
rate = get_incoming_rate({
|
||||
"item_code": d.item_code,
|
||||
"warehouse": d.warehouse,
|
||||
"posting_date": si.posting_date,
|
||||
"posting_time": si.posting_time,
|
||||
"qty": -1 * flt(d.get('stock_qty')),
|
||||
"serial_no": d.serial_no,
|
||||
"company": si.company,
|
||||
"voucher_type": 'Sales Invoice',
|
||||
"voucher_no": si.name,
|
||||
"allow_zero_valuation": d.get("allow_zero_valuation")
|
||||
}, raise_error_if_no_rate=False)
|
||||
|
||||
# si_gl_entries = [
|
||||
# ["_Test Account Excise Duty - TCP1", 0.0, 12.0, nowdate()],
|
||||
# ["Unrealized Profit - TCP1", 12.0, 0.0, nowdate()]
|
||||
# ]
|
||||
rate = flt(rate, 2)
|
||||
|
||||
# check_gl_entries(self, si.name, si_gl_entries, add_days(nowdate(), -1))
|
||||
si.submit()
|
||||
|
||||
# pi_gl_entries = [
|
||||
# ["_Test Account Excise Duty - TCP1", 12.0 , 0.0, nowdate()],
|
||||
# ["Unrealized Profit - TCP1", 0.0, 12.0, nowdate()]
|
||||
# ]
|
||||
target_doc = make_inter_company_transaction("Sales Invoice", si.name)
|
||||
target_doc.company = '_Test Company with perpetual inventory'
|
||||
target_doc.items[0].warehouse = 'Finished Goods - TCP1'
|
||||
add_taxes(target_doc)
|
||||
target_doc.save()
|
||||
target_doc.submit()
|
||||
|
||||
# check_gl_entries(self, target_doc.name, pi_gl_entries, add_days(nowdate(), -1))
|
||||
tax_amount = flt(rate * (12/100), 2)
|
||||
si_gl_entries = [
|
||||
["_Test Account Excise Duty - TCP1", 0.0, tax_amount, nowdate()],
|
||||
["Unrealized Profit - TCP1", tax_amount, 0.0, nowdate()]
|
||||
]
|
||||
|
||||
check_gl_entries(self, si.name, si_gl_entries, add_days(nowdate(), -1))
|
||||
|
||||
pi_gl_entries = [
|
||||
["_Test Account Excise Duty - TCP1", tax_amount , 0.0, nowdate()],
|
||||
["Unrealized Profit - TCP1", 0.0, tax_amount, nowdate()]
|
||||
]
|
||||
|
||||
# Sale and Purchase both should be at valuation rate
|
||||
self.assertEqual(si.items[0].rate, rate)
|
||||
self.assertEqual(target_doc.items[0].rate, rate)
|
||||
|
||||
check_gl_entries(self, target_doc.name, pi_gl_entries, add_days(nowdate(), -1))
|
||||
|
||||
def test_eway_bill_json(self):
|
||||
si = make_sales_invoice_for_ewaybill()
|
||||
|
@ -565,11 +565,12 @@
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: parent.is_internal_customer && parent.update_stock",
|
||||
"fieldname": "target_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 1,
|
||||
"label": "Customer Warehouse (Optional)",
|
||||
"label": "Target Warehouse",
|
||||
"no_copy": 1,
|
||||
"options": "Warehouse",
|
||||
"print_hide": 1
|
||||
@ -815,7 +816,7 @@
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-09-23 19:59:04.879322",
|
||||
"modified": "2020-12-26 17:25:04.090630",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Accounts",
|
||||
"name": "Sales Invoice Item",
|
||||
|
@ -34,6 +34,9 @@ def valdiate_taxes_and_charges_template(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"):
|
||||
validate_taxes_and_charges(tax)
|
||||
validate_inclusive_tax(tax, doc)
|
||||
@ -41,3 +44,7 @@ def valdiate_taxes_and_charges_template(doc):
|
||||
def validate_disabled(doc):
|
||||
if doc.is_default and doc.disabled:
|
||||
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)))
|
||||
|
@ -49,7 +49,8 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
||||
elif d.po_detail:
|
||||
purchase_receipt = ", ".join(po_pr_map.get(d.po_detail, []))
|
||||
|
||||
expense_account = d.expense_account or aii_account_map.get(d.company)
|
||||
expense_account = d.unrealized_profit_loss_account or d.expense_account \
|
||||
or aii_account_map.get(d.company)
|
||||
|
||||
row = {
|
||||
'item_code': d.item_code,
|
||||
@ -315,6 +316,7 @@ def get_items(filters, additional_query_columns):
|
||||
`tabPurchase Invoice Item`.`name`, `tabPurchase Invoice Item`.`parent`,
|
||||
`tabPurchase Invoice`.posting_date, `tabPurchase Invoice`.credit_to, `tabPurchase Invoice`.company,
|
||||
`tabPurchase Invoice`.supplier, `tabPurchase Invoice`.remarks, `tabPurchase Invoice`.base_net_total,
|
||||
`tabPurchase Invoice`.unrealized_profit_loss_account,
|
||||
`tabPurchase Invoice Item`.`item_code`, `tabPurchase Invoice Item`.description,
|
||||
`tabPurchase Invoice Item`.`item_name`, `tabPurchase Invoice Item`.`item_group`,
|
||||
`tabPurchase Invoice Item`.`project`, `tabPurchase Invoice Item`.`purchase_order`,
|
||||
|
@ -76,7 +76,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
||||
'company': d.company,
|
||||
'sales_order': d.sales_order,
|
||||
'delivery_note': d.delivery_note,
|
||||
'income_account': d.income_account,
|
||||
'income_account': d.unrealized_profit_loss_account or d.income_account,
|
||||
'cost_center': d.cost_center,
|
||||
'stock_qty': d.stock_qty,
|
||||
'stock_uom': d.stock_uom
|
||||
@ -379,6 +379,7 @@ def get_items(filters, additional_query_columns):
|
||||
select
|
||||
`tabSales Invoice Item`.name, `tabSales Invoice Item`.parent,
|
||||
`tabSales Invoice`.posting_date, `tabSales Invoice`.debit_to,
|
||||
`tabSales Invoice`.unrealized_profit_loss_account,
|
||||
`tabSales Invoice`.project, `tabSales Invoice`.customer, `tabSales Invoice`.remarks,
|
||||
`tabSales Invoice`.territory, `tabSales Invoice`.company, `tabSales Invoice`.base_net_total,
|
||||
`tabSales Invoice Item`.item_code, `tabSales Invoice Item`.description,
|
||||
|
@ -14,13 +14,15 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
||||
if not filters: filters = {}
|
||||
|
||||
invoice_list = get_invoices(filters, additional_query_columns)
|
||||
columns, expense_accounts, tax_accounts = get_columns(invoice_list, additional_table_columns)
|
||||
columns, expense_accounts, tax_accounts, unrealized_profit_loss_accounts \
|
||||
= get_columns(invoice_list, additional_table_columns)
|
||||
|
||||
if not invoice_list:
|
||||
msgprint(_("No record found"))
|
||||
return columns, invoice_list
|
||||
|
||||
invoice_expense_map = get_invoice_expense_map(invoice_list)
|
||||
internal_invoice_map = get_internal_invoice_map(invoice_list)
|
||||
invoice_expense_map, invoice_tax_map = get_invoice_tax_map(invoice_list,
|
||||
invoice_expense_map, expense_accounts)
|
||||
invoice_po_pr_map = get_invoice_po_pr_map(invoice_list)
|
||||
@ -52,10 +54,17 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
|
||||
# map expense values
|
||||
base_net_total = 0
|
||||
for expense_acc in expense_accounts:
|
||||
expense_amount = flt(invoice_expense_map.get(inv.name, {}).get(expense_acc))
|
||||
if inv.is_internal_supplier and inv.company == inv.represents_company:
|
||||
expense_amount = 0
|
||||
else:
|
||||
expense_amount = flt(invoice_expense_map.get(inv.name, {}).get(expense_acc))
|
||||
base_net_total += expense_amount
|
||||
row.append(expense_amount)
|
||||
|
||||
# Add amount in unrealized account
|
||||
for account in unrealized_profit_loss_accounts:
|
||||
row.append(flt(internal_invoice_map.get((inv.name, account))))
|
||||
|
||||
# net total
|
||||
row.append(base_net_total or inv.base_net_total)
|
||||
|
||||
@ -96,7 +105,8 @@ def get_columns(invoice_list, additional_table_columns):
|
||||
"width": 80
|
||||
}
|
||||
]
|
||||
expense_accounts = tax_accounts = expense_columns = tax_columns = []
|
||||
expense_accounts = tax_accounts = expense_columns = tax_columns = unrealized_profit_loss_accounts = \
|
||||
unrealized_profit_loss_account_columns = []
|
||||
|
||||
if invoice_list:
|
||||
expense_accounts = frappe.db.sql_list("""select distinct expense_account
|
||||
@ -112,17 +122,25 @@ def get_columns(invoice_list, additional_table_columns):
|
||||
and parent in (%s) order by account_head""" %
|
||||
', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]))
|
||||
|
||||
unrealized_profit_loss_accounts = frappe.db.sql_list("""SELECT distinct unrealized_profit_loss_account
|
||||
from `tabPurchase Invoice` where docstatus = 1 and name in (%s)
|
||||
and ifnull(unrealized_profit_loss_account, '') != ''
|
||||
order by unrealized_profit_loss_account""" %
|
||||
', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]))
|
||||
|
||||
expense_columns = [(account + ":Currency/currency:120") for account in expense_accounts]
|
||||
unrealized_profit_loss_account_columns = [(account + ":Currency/currency:120") for account in unrealized_profit_loss_accounts]
|
||||
|
||||
for account in tax_accounts:
|
||||
if account not in expense_accounts:
|
||||
tax_columns.append(account + ":Currency/currency:120")
|
||||
|
||||
columns = columns + expense_columns + [_("Net Total") + ":Currency/currency:120"] + tax_columns + \
|
||||
columns = columns + expense_columns + unrealized_profit_loss_account_columns + \
|
||||
[_("Net Total") + ":Currency/currency:120"] + tax_columns + \
|
||||
[_("Total Tax") + ":Currency/currency:120", _("Grand Total") + ":Currency/currency:120",
|
||||
_("Rounded Total") + ":Currency/currency:120", _("Outstanding Amount") + ":Currency/currency:120"]
|
||||
|
||||
return columns, expense_accounts, tax_accounts
|
||||
return columns, expense_accounts, tax_accounts, unrealized_profit_loss_accounts
|
||||
|
||||
def get_conditions(filters):
|
||||
conditions = ""
|
||||
@ -199,6 +217,19 @@ def get_invoice_expense_map(invoice_list):
|
||||
|
||||
return invoice_expense_map
|
||||
|
||||
def get_internal_invoice_map(invoice_list):
|
||||
unrealized_amount_details = frappe.db.sql("""SELECT name, unrealized_profit_loss_account,
|
||||
base_net_total as amount from `tabPurchase Invoice` where name in (%s)
|
||||
and is_internal_supplier = 1 and company = represents_company""" %
|
||||
', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1)
|
||||
|
||||
internal_invoice_map = {}
|
||||
for d in unrealized_amount_details:
|
||||
if d.unrealized_profit_loss_account:
|
||||
internal_invoice_map.setdefault((d.name, d.unrealized_profit_loss_account), d.amount)
|
||||
|
||||
return internal_invoice_map
|
||||
|
||||
def get_invoice_tax_map(invoice_list, invoice_expense_map, expense_accounts):
|
||||
tax_details = frappe.db.sql("""
|
||||
select parent, account_head, case add_deduct_tax when "Add" then sum(base_tax_amount_after_discount_amount)
|
||||
|
@ -15,13 +15,14 @@ def _execute(filters, additional_table_columns=None, additional_query_columns=No
|
||||
if not filters: filters = frappe._dict({})
|
||||
|
||||
invoice_list = get_invoices(filters, additional_query_columns)
|
||||
columns, income_accounts, tax_accounts = get_columns(invoice_list, additional_table_columns)
|
||||
columns, income_accounts, tax_accounts, unrealized_profit_loss_accounts = get_columns(invoice_list, additional_table_columns)
|
||||
|
||||
if not invoice_list:
|
||||
msgprint(_("No record found"))
|
||||
return columns, invoice_list
|
||||
|
||||
invoice_income_map = get_invoice_income_map(invoice_list)
|
||||
internal_invoice_map = get_internal_invoice_map(invoice_list)
|
||||
invoice_income_map, invoice_tax_map = get_invoice_tax_map(invoice_list,
|
||||
invoice_income_map, income_accounts)
|
||||
#Cost Center & Warehouse Map
|
||||
@ -70,12 +71,22 @@ def _execute(filters, additional_table_columns=None, additional_query_columns=No
|
||||
# map income values
|
||||
base_net_total = 0
|
||||
for income_acc in income_accounts:
|
||||
income_amount = flt(invoice_income_map.get(inv.name, {}).get(income_acc))
|
||||
if inv.is_internal_customer and inv.company == inv.represents_company:
|
||||
income_amount = 0
|
||||
else:
|
||||
income_amount = flt(invoice_income_map.get(inv.name, {}).get(income_acc))
|
||||
|
||||
base_net_total += income_amount
|
||||
row.update({
|
||||
frappe.scrub(income_acc): income_amount
|
||||
})
|
||||
|
||||
# Add amount in unrealized account
|
||||
for account in unrealized_profit_loss_accounts:
|
||||
row.update({
|
||||
frappe.scrub(account): flt(internal_invoice_map.get((inv.name, account)))
|
||||
})
|
||||
|
||||
# net total
|
||||
row.update({'net_total': base_net_total or inv.base_net_total})
|
||||
|
||||
@ -230,6 +241,8 @@ def get_columns(invoice_list, additional_table_columns):
|
||||
tax_accounts = []
|
||||
income_columns = []
|
||||
tax_columns = []
|
||||
unrealized_profit_loss_accounts = []
|
||||
unrealized_profit_loss_account_columns = []
|
||||
|
||||
if invoice_list:
|
||||
income_accounts = frappe.db.sql_list("""select distinct income_account
|
||||
@ -243,12 +256,18 @@ def get_columns(invoice_list, additional_table_columns):
|
||||
and parent in (%s) order by account_head""" %
|
||||
', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]))
|
||||
|
||||
unrealized_profit_loss_accounts = frappe.db.sql_list("""SELECT distinct unrealized_profit_loss_account
|
||||
from `tabSales Invoice` where docstatus = 1 and name in (%s)
|
||||
and ifnull(unrealized_profit_loss_account, '') != ''
|
||||
order by unrealized_profit_loss_account""" %
|
||||
', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]))
|
||||
|
||||
for account in income_accounts:
|
||||
income_columns.append({
|
||||
"label": account,
|
||||
"fieldname": frappe.scrub(account),
|
||||
"fieldtype": "Currency",
|
||||
"options": 'currency',
|
||||
"options": "currency",
|
||||
"width": 120
|
||||
})
|
||||
|
||||
@ -258,15 +277,24 @@ def get_columns(invoice_list, additional_table_columns):
|
||||
"label": account,
|
||||
"fieldname": frappe.scrub(account),
|
||||
"fieldtype": "Currency",
|
||||
"options": 'currency',
|
||||
"options": "currency",
|
||||
"width": 120
|
||||
})
|
||||
|
||||
for account in unrealized_profit_loss_accounts:
|
||||
unrealized_profit_loss_account_columns.append({
|
||||
"label": account,
|
||||
"fieldname": frappe.scrub(account),
|
||||
"fieldtype": "Currency",
|
||||
"options": "currency",
|
||||
"width": 120
|
||||
})
|
||||
|
||||
net_total_column = [{
|
||||
"label": _("Net Total"),
|
||||
"fieldname": "net_total",
|
||||
"fieldtype": "Currency",
|
||||
"options": 'currency',
|
||||
"options": "currency",
|
||||
"width": 120
|
||||
}]
|
||||
|
||||
@ -301,9 +329,10 @@ def get_columns(invoice_list, additional_table_columns):
|
||||
}
|
||||
]
|
||||
|
||||
columns = columns + income_columns + net_total_column + tax_columns + total_columns
|
||||
columns = columns + income_columns + unrealized_profit_loss_account_columns + \
|
||||
net_total_column + tax_columns + total_columns
|
||||
|
||||
return columns, income_accounts, tax_accounts
|
||||
return columns, income_accounts, tax_accounts, unrealized_profit_loss_accounts
|
||||
|
||||
def get_conditions(filters):
|
||||
conditions = ""
|
||||
@ -368,7 +397,8 @@ def get_invoices(filters, additional_query_columns):
|
||||
return frappe.db.sql("""
|
||||
select name, posting_date, debit_to, project, customer,
|
||||
customer_name, owner, remarks, territory, tax_id, customer_group,
|
||||
base_net_total, base_grand_total, base_rounded_total, outstanding_amount {0}
|
||||
base_net_total, base_grand_total, base_rounded_total, outstanding_amount,
|
||||
is_internal_customer, represents_company, company {0}
|
||||
from `tabSales Invoice`
|
||||
where docstatus = 1 %s order by posting_date desc, name desc""".format(additional_query_columns or '') %
|
||||
conditions, filters, as_dict=1)
|
||||
@ -385,6 +415,19 @@ def get_invoice_income_map(invoice_list):
|
||||
|
||||
return invoice_income_map
|
||||
|
||||
def get_internal_invoice_map(invoice_list):
|
||||
unrealized_amount_details = frappe.db.sql("""SELECT name, unrealized_profit_loss_account,
|
||||
base_net_total as amount from `tabSales Invoice` where name in (%s)
|
||||
and is_internal_customer = 1 and company = represents_company""" %
|
||||
', '.join(['%s']*len(invoice_list)), tuple([inv.name for inv in invoice_list]), as_dict=1)
|
||||
|
||||
internal_invoice_map = {}
|
||||
for d in unrealized_amount_details:
|
||||
if d.unrealized_profit_loss_account:
|
||||
internal_invoice_map.setdefault((d.name, d.unrealized_profit_loss_account), d.amount)
|
||||
|
||||
return internal_invoice_map
|
||||
|
||||
def get_invoice_tax_map(invoice_list, invoice_income_map, income_accounts):
|
||||
tax_details = frappe.db.sql("""select parent, account_head,
|
||||
sum(base_tax_amount_after_discount_amount) as tax_amount
|
||||
|
@ -164,16 +164,16 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
|
||||
|
||||
if (doc.docstatus === 1 && !doc.inter_company_order_reference) {
|
||||
let me = this;
|
||||
frappe.model.with_doc("Supplier", me.frm.doc.supplier, () => {
|
||||
let supplier = frappe.model.get_doc("Supplier", me.frm.doc.supplier);
|
||||
let internal = supplier.is_internal_supplier;
|
||||
let disabled = supplier.disabled;
|
||||
if (internal === 1 && disabled === 0) {
|
||||
me.frm.add_custom_button("Inter Company Order", function() {
|
||||
me.make_inter_company_order(me.frm);
|
||||
}, __('Create'));
|
||||
}
|
||||
});
|
||||
let internal = me.frm.doc.is_internal_supplier;
|
||||
if (internal) {
|
||||
let button_label = (me.frm.doc.company === me.frm.doc.represents_company) ? "Internal Sales Order" :
|
||||
"Inter Company Sales Order";
|
||||
|
||||
me.frm.add_custom_button(button_label, function() {
|
||||
me.make_inter_company_order(me.frm);
|
||||
}, __('Create'));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -381,7 +381,7 @@ erpnext.buying.PurchaseOrderController = erpnext.buying.BuyingController.extend(
|
||||
material_request_type: "Purchase",
|
||||
docstatus: 1,
|
||||
status: ["!=", "Stopped"],
|
||||
per_ordered: ["<", 99.99],
|
||||
per_ordered: ["<", 100],
|
||||
company: me.frm.doc.company
|
||||
}
|
||||
})
|
||||
|
@ -134,6 +134,8 @@
|
||||
"ref_sq",
|
||||
"column_break_74",
|
||||
"party_account_currency",
|
||||
"is_internal_supplier",
|
||||
"represents_company",
|
||||
"inter_company_order_reference"
|
||||
],
|
||||
"fields": [
|
||||
@ -1101,13 +1103,28 @@
|
||||
{
|
||||
"fieldname": "items_col_break",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fetch_from": "supplier.is_internal_supplier",
|
||||
"fieldname": "is_internal_supplier",
|
||||
"fieldtype": "Check",
|
||||
"label": "Is Internal Supplier"
|
||||
},
|
||||
{
|
||||
"fetch_from": "supplier.represents_company",
|
||||
"fieldname": "represents_company",
|
||||
"fieldtype": "Link",
|
||||
"label": "Represents Company",
|
||||
"options": "Company",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-file-text",
|
||||
"idx": 105,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-12-03 16:46:44.229351",
|
||||
"modified": "2021-01-20 22:07:23.487138",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Purchase Order",
|
||||
|
@ -224,7 +224,7 @@ erpnext.buying.RequestforQuotationController = erpnext.buying.BuyingController.e
|
||||
material_request_type: "Purchase",
|
||||
docstatus: 1,
|
||||
status: ["!=", "Stopped"],
|
||||
per_ordered: ["<", 99.99],
|
||||
per_ordered: ["<", 100],
|
||||
company: me.frm.doc.company
|
||||
}
|
||||
})
|
||||
@ -280,7 +280,7 @@ erpnext.buying.RequestforQuotationController = erpnext.buying.BuyingController.e
|
||||
material_request_type: "Purchase",
|
||||
docstatus: 1,
|
||||
status: ["!=", "Stopped"],
|
||||
per_ordered: ["<", 99.99]
|
||||
per_ordered: ["<", 100]
|
||||
}
|
||||
});
|
||||
dialog.hide();
|
||||
|
@ -52,7 +52,10 @@ class Supplier(TransactionBase):
|
||||
self.validate_internal_supplier()
|
||||
|
||||
def validate_internal_supplier(self):
|
||||
if self.is_internal_supplier and frappe.db.get_value("Supplier", {"represents_company": self.represents_company}, "name"):
|
||||
internal_supplier = frappe.db.get_value("Supplier",
|
||||
{"is_internal_supplier": 1, "represents_company": self.represents_company, "name": ("!=", self.name)}, "name")
|
||||
|
||||
if internal_supplier:
|
||||
frappe.throw(_("Internal Supplier for company {0} already exists").format(
|
||||
frappe.bold(self.represents_company)))
|
||||
|
||||
|
@ -44,7 +44,7 @@ erpnext.buying.SupplierQuotationController = erpnext.buying.BuyingController.ext
|
||||
material_request_type: "Purchase",
|
||||
docstatus: 1,
|
||||
status: ["!=", "Stopped"],
|
||||
per_ordered: ["<", 99.99],
|
||||
per_ordered: ["<", 100],
|
||||
company: me.frm.doc.company
|
||||
}
|
||||
})
|
||||
|
@ -75,6 +75,9 @@ class AccountsController(TransactionBase):
|
||||
self.ensure_supplier_is_not_blocked()
|
||||
|
||||
self.validate_date_with_fiscal_year()
|
||||
self.validate_inter_company_reference()
|
||||
|
||||
self.set_incoming_rate()
|
||||
|
||||
if self.meta.get_field("currency"):
|
||||
self.calculate_taxes_and_totals()
|
||||
@ -110,14 +113,20 @@ class AccountsController(TransactionBase):
|
||||
self.set_inter_company_account()
|
||||
|
||||
validate_regional(self)
|
||||
|
||||
|
||||
validate_einvoice_fields(self)
|
||||
|
||||
if self.doctype != 'Material Request':
|
||||
apply_pricing_rule_on_transaction(self)
|
||||
|
||||
|
||||
def before_cancel(self):
|
||||
validate_einvoice_fields(self)
|
||||
|
||||
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):
|
||||
for d in self.items:
|
||||
@ -206,6 +215,17 @@ class AccountsController(TransactionBase):
|
||||
validate_fiscal_year(self.get(date_field), self.fiscal_year, self.company,
|
||||
self.meta.get_label(date_field), self)
|
||||
|
||||
def validate_inter_company_reference(self):
|
||||
if self.doctype not in ('Purchase Invoice', 'Purchase Receipt', 'Purchase Order'):
|
||||
return
|
||||
|
||||
if self.is_internal_transfer():
|
||||
if not (self.get('inter_company_reference') or self.get('inter_company_invoice_reference')
|
||||
or self.get('inter_company_order_reference')):
|
||||
msg = _("Internal Sale or Delivery Reference missing. ")
|
||||
msg += _("Please create purchase from internal sale or delivery document itself")
|
||||
frappe.throw(msg, title=_("Internal Sales Reference Missing"))
|
||||
|
||||
def validate_due_date(self):
|
||||
if self.get('is_pos'): return
|
||||
|
||||
@ -282,6 +302,7 @@ class AccountsController(TransactionBase):
|
||||
args["doctype"] = self.doctype
|
||||
args["name"] = self.name
|
||||
args["child_docname"] = item.name
|
||||
args["ignore_pricing_rule"] = self.ignore_pricing_rule if hasattr(self, 'ignore_pricing_rule') else 0
|
||||
|
||||
if not args.get("transaction_date"):
|
||||
args["transaction_date"] = args.get("posting_date")
|
||||
@ -448,8 +469,10 @@ class AccountsController(TransactionBase):
|
||||
account_currency = get_account_currency(gl_dict.account)
|
||||
|
||||
if gl_dict.account and self.doctype not in ["Journal Entry",
|
||||
"Period Closing Voucher", "Payment Entry"]:
|
||||
"Period Closing Voucher", "Payment Entry", "Purchase Receipt", "Purchase Invoice", "Stock Entry"]:
|
||||
self.validate_account_currency(gl_dict.account, account_currency)
|
||||
|
||||
if gl_dict.account and self.doctype not in ["Journal Entry", "Period Closing Voucher", "Payment Entry"]:
|
||||
set_balance_in_account_currency(gl_dict, account_currency, self.get("conversion_rate"),
|
||||
self.company_currency)
|
||||
|
||||
@ -962,9 +985,9 @@ class AccountsController(TransactionBase):
|
||||
It will an internal transfer if its an internal customer and representation
|
||||
company is same as billing company
|
||||
"""
|
||||
if self.doctype == 'Sales Invoice':
|
||||
if self.doctype in ('Sales Invoice', 'Delivery Note', 'Sales Order'):
|
||||
internal_party_field = 'is_internal_customer'
|
||||
else:
|
||||
elif self.doctype in ('Purchase Invoice', 'Purchase Receipt', 'Purchase Order'):
|
||||
internal_party_field = 'is_internal_supplier'
|
||||
|
||||
if self.get(internal_party_field) and (self.represents_company == self.company):
|
||||
|
@ -44,7 +44,6 @@ class BuyingController(StockController):
|
||||
self.validate_items()
|
||||
self.set_qty_as_per_stock_uom()
|
||||
self.validate_stock_or_nonstock_items()
|
||||
self.update_tax_category_for_internal_transfer()
|
||||
self.validate_warehouse()
|
||||
self.validate_from_warehouse()
|
||||
self.set_supplier_address()
|
||||
@ -100,11 +99,6 @@ class BuyingController(StockController):
|
||||
msg = _('Tax Category has been changed to "Total" because all the Items are non-stock items')
|
||||
self.update_tax_category(msg)
|
||||
|
||||
def update_tax_category_for_internal_transfer(self):
|
||||
if self.doctype == 'Purchase Invoice' and self.is_internal_transfer():
|
||||
msg = _('Tax Category has been changed to "Total" as its an internal purchase.')
|
||||
self.update_tax_category(msg)
|
||||
|
||||
def update_tax_category(self, msg):
|
||||
tax_for_valuation = [d for d in self.get("taxes")
|
||||
if d.category in ["Valuation", "Valuation and Total"]]
|
||||
@ -224,6 +218,48 @@ class BuyingController(StockController):
|
||||
else:
|
||||
item.valuation_rate = 0.0
|
||||
|
||||
def set_incoming_rate(self):
|
||||
if self.doctype not in ("Purchase Receipt", "Purchase Invoice", "Purchase Order"):
|
||||
return
|
||||
|
||||
ref_doctype_map = {
|
||||
"Purchase Order": "Sales Order Item",
|
||||
"Purchase Receipt": "Delivery Note Item",
|
||||
"Purchase Invoice": "Sales Invoice Item",
|
||||
}
|
||||
|
||||
ref_doctype = ref_doctype_map.get(self.doctype)
|
||||
items = self.get("items")
|
||||
for d in items:
|
||||
if not cint(self.get("is_return")):
|
||||
# Get outgoing rate based on original item cost based on valuation method
|
||||
|
||||
if not d.get(frappe.scrub(ref_doctype)):
|
||||
outgoing_rate = get_incoming_rate({
|
||||
"item_code": d.item_code,
|
||||
"warehouse": d.get('from_warehouse'),
|
||||
"posting_date": self.get('posting_date') or self.get('transation_date'),
|
||||
"posting_time": self.get('posting_time'),
|
||||
"qty": -1 * flt(d.get('stock_qty')),
|
||||
"serial_no": d.get('serial_no'),
|
||||
"company": self.company,
|
||||
"voucher_type": self.doctype,
|
||||
"voucher_no": self.name,
|
||||
"allow_zero_valuation": d.get("allow_zero_valuation")
|
||||
}, raise_error_if_no_rate=False)
|
||||
|
||||
rate = flt(outgoing_rate * d.conversion_factor, d.precision('rate'))
|
||||
else:
|
||||
rate = frappe.db.get_value(ref_doctype, d.get(frappe.scrub(ref_doctype)), 'rate')
|
||||
|
||||
if self.is_internal_transfer():
|
||||
if rate != d.rate:
|
||||
d.rate = rate
|
||||
d.discount_percentage = 0
|
||||
d.discount_amount = 0
|
||||
frappe.msgprint(_("Row {0}: Item rate has been updated as per valuation rate since its an internal stock transfer")
|
||||
.format(d.idx), alert=1)
|
||||
|
||||
def get_supplied_items_cost(self, item_row_id, reset_outgoing_rate=True):
|
||||
supplied_items_cost = 0.0
|
||||
for d in self.get("supplied_items"):
|
||||
@ -243,7 +279,7 @@ class BuyingController(StockController):
|
||||
|
||||
d.amount = flt(flt(d.consumed_qty) * flt(d.rate), d.precision("amount"))
|
||||
supplied_items_cost += flt(d.amount)
|
||||
|
||||
|
||||
return supplied_items_cost
|
||||
|
||||
def validate_for_subcontracting(self):
|
||||
@ -559,6 +595,8 @@ class BuyingController(StockController):
|
||||
from_warehouse_sle = self.get_sl_entries(d, {
|
||||
"actual_qty": -1 * pr_qty,
|
||||
"warehouse": d.from_warehouse,
|
||||
"outgoing_rate": d.rate,
|
||||
"recalculate_rate": 1,
|
||||
"dependant_sle_voucher_detail_no": d.name
|
||||
})
|
||||
|
||||
|
@ -262,6 +262,7 @@ def make_return_doc(doctype, source_name, target_doc=None):
|
||||
|
||||
if doc.get("is_return"):
|
||||
if doc.doctype == 'Sales Invoice' or doc.doctype == 'POS Invoice':
|
||||
doc.consolidated_invoice = ""
|
||||
doc.set('payments', [])
|
||||
for data in source.payments:
|
||||
paid_amount = 0.00
|
||||
|
@ -3,7 +3,7 @@
|
||||
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.utils import cint, flt, cstr, comma_or, get_link_to_form
|
||||
from frappe.utils import cint, flt, cstr, get_link_to_form, nowtime
|
||||
from frappe import _, throw
|
||||
from erpnext.stock.get_item_details import get_bin_details
|
||||
from erpnext.stock.utils import get_incoming_rate
|
||||
@ -49,7 +49,6 @@ class SellingController(StockController):
|
||||
self.set_customer_address()
|
||||
self.validate_for_duplicate_items()
|
||||
self.validate_target_warehouse()
|
||||
self.set_incoming_rate()
|
||||
|
||||
def set_missing_values(self, for_validate=False):
|
||||
|
||||
@ -191,7 +190,7 @@ class SellingController(StockController):
|
||||
for it in self.get("items"):
|
||||
if not it.item_code:
|
||||
continue
|
||||
|
||||
|
||||
last_purchase_rate, is_stock_item = frappe.get_cached_value("Item", it.item_code, ["last_purchase_rate", "is_stock_item"])
|
||||
last_purchase_rate_in_sales_uom = last_purchase_rate * (it.conversion_factor or 1)
|
||||
if flt(it.base_net_rate) < flt(last_purchase_rate_in_sales_uom):
|
||||
@ -312,7 +311,7 @@ class SellingController(StockController):
|
||||
sales_order.update_reserved_qty(so_item_rows)
|
||||
|
||||
def set_incoming_rate(self):
|
||||
if self.doctype not in ("Delivery Note", "Sales Invoice"):
|
||||
if self.doctype not in ("Delivery Note", "Sales Invoice", "Sales Order"):
|
||||
return
|
||||
|
||||
items = self.get("items") + (self.get("packed_items") or [])
|
||||
@ -322,15 +321,26 @@ class SellingController(StockController):
|
||||
d.incoming_rate = get_incoming_rate({
|
||||
"item_code": d.item_code,
|
||||
"warehouse": d.warehouse,
|
||||
"posting_date": self.posting_date,
|
||||
"posting_time": self.posting_time,
|
||||
"qty": -1*flt(d.qty),
|
||||
"serial_no": d.serial_no,
|
||||
"posting_date": self.get('posting_date') or self.get('transaction_date'),
|
||||
"posting_time": self.get('posting_time') or nowtime(),
|
||||
"qty": -1 * flt(d.get('stock_qty') or d.get('actual_qty')),
|
||||
"serial_no": d.get('serial_no'),
|
||||
"company": self.company,
|
||||
"voucher_type": self.doctype,
|
||||
"voucher_no": self.name,
|
||||
"allow_zero_valuation": d.get("allow_zero_valuation")
|
||||
}, raise_error_if_no_rate=False)
|
||||
|
||||
# For internal transfers use incoming rate as the valuation rate
|
||||
if self.is_internal_transfer():
|
||||
rate = flt(d.incoming_rate * d.conversion_factor, d.precision('rate'))
|
||||
if d.rate != rate:
|
||||
d.rate = rate
|
||||
d.discount_percentage = 0
|
||||
d.discount_amount = 0
|
||||
frappe.msgprint(_("Row {0}: Item rate has been updated as per valuation rate since its an internal stock transfer")
|
||||
.format(d.idx), alert=1)
|
||||
|
||||
elif self.get("return_against"):
|
||||
# Get incoming rate of return entry from reference document
|
||||
# based on original item cost as per valuation method
|
||||
@ -391,7 +401,7 @@ class SellingController(StockController):
|
||||
})
|
||||
if item_row.warehouse:
|
||||
sle.dependant_sle_voucher_detail_no = item_row.name
|
||||
|
||||
|
||||
return sle
|
||||
|
||||
def set_po_nos(self, for_validate=False):
|
||||
@ -459,13 +469,19 @@ class SellingController(StockController):
|
||||
non_stock_items = [d.item_code, d.description]
|
||||
|
||||
if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1:
|
||||
duplicate_items_msg = _("Item {0} entered multiple times.").format(frappe.bold(d.item_code))
|
||||
duplicate_items_msg += "<br><br>"
|
||||
duplicate_items_msg += _("Please enable {} in {} to allow same item in multiple rows").format(
|
||||
frappe.bold("Allow Item to Be Added Multiple Times in a Transaction"),
|
||||
get_link_to_form("Selling Settings", "Selling Settings")
|
||||
)
|
||||
if stock_items in check_list:
|
||||
frappe.throw(_("Note: Item {0} entered multiple times").format(d.item_code))
|
||||
frappe.throw(duplicate_items_msg)
|
||||
else:
|
||||
check_list.append(stock_items)
|
||||
else:
|
||||
if non_stock_items in chk_dupl_itm:
|
||||
frappe.throw(_("Note: Item {0} entered multiple times").format(d.item_code))
|
||||
frappe.throw(duplicate_items_msg)
|
||||
else:
|
||||
chk_dupl_itm.append(non_stock_items)
|
||||
|
||||
|
@ -24,6 +24,7 @@ class StockController(AccountsController):
|
||||
self.validate_inspection()
|
||||
self.validate_serialized_batch()
|
||||
self.validate_customer_provided_item()
|
||||
self.validate_internal_transfer()
|
||||
self.validate_putaway_capacity()
|
||||
|
||||
def make_gl_entries(self, gl_entries=None, from_repost=False):
|
||||
@ -74,6 +75,7 @@ class StockController(AccountsController):
|
||||
warehouse_with_no_account = []
|
||||
precision = frappe.get_precision("GL Entry", "debit_in_account_currency")
|
||||
for item_row in voucher_details:
|
||||
|
||||
sle_list = sle_map.get(item_row.name)
|
||||
if sle_list:
|
||||
for sle in sle_list:
|
||||
@ -218,7 +220,7 @@ class StockController(AccountsController):
|
||||
""", (self.doctype, self.name), as_dict=True)
|
||||
|
||||
for sle in stock_ledger_entries:
|
||||
stock_ledger.setdefault(sle.voucher_detail_no, []).append(sle)
|
||||
stock_ledger.setdefault(sle.voucher_detail_no, []).append(sle)
|
||||
return stock_ledger
|
||||
|
||||
def make_batches(self, warehouse_field):
|
||||
@ -393,6 +395,32 @@ class StockController(AccountsController):
|
||||
if frappe.db.get_value('Item', d.item_code, 'is_customer_provided_item'):
|
||||
d.allow_zero_valuation_rate = 1
|
||||
|
||||
def validate_internal_transfer(self):
|
||||
if self.doctype in ('Sales Invoice', 'Delivery Note', 'Purchase Invoice', 'Purchase Receipt') \
|
||||
and self.is_internal_transfer():
|
||||
self.validate_in_transit_warehouses()
|
||||
self.validate_multi_currency()
|
||||
self.validate_packed_items()
|
||||
|
||||
def validate_in_transit_warehouses(self):
|
||||
if (self.doctype == 'Sales Invoice' and self.get('update_stock')) or self.doctype == 'Delivery Note':
|
||||
for item in self.get('items'):
|
||||
if not item.target_warehouse:
|
||||
frappe.throw(_("Row {0}: Target Warehouse is mandatory for internal transfers").format(item.idx))
|
||||
|
||||
if (self.doctype == 'Purchase Invoice' and self.get('update_stock')) or self.doctype == 'Purchase Receipt':
|
||||
for item in self.get('items'):
|
||||
if not item.from_warehouse:
|
||||
frappe.throw(_("Row {0}: From Warehouse is mandatory for internal transfers").format(item.idx))
|
||||
|
||||
def validate_multi_currency(self):
|
||||
if self.currency != self.company_currency:
|
||||
frappe.throw(_("Internal transfers can only be done in company's default currency"))
|
||||
|
||||
def validate_packed_items(self):
|
||||
if self.doctype in ('Sales Invoice', 'Delivery Note Item') and self.get('packed_items'):
|
||||
frappe.throw(_("Packed Items cannot be transferred internally"))
|
||||
|
||||
def validate_putaway_capacity(self):
|
||||
# if over receipt is attempted while 'apply putaway rule' is disabled
|
||||
# and if rule was applied on the transaction, validate it.
|
||||
|
@ -10,6 +10,7 @@ from erpnext.controllers.accounts_controller import validate_conversion_rate, \
|
||||
validate_taxes_and_charges, validate_inclusive_tax
|
||||
from erpnext.stock.get_item_details import _get_item_tax_template
|
||||
from erpnext.accounts.doctype.pricing_rule.utils import get_applied_pricing_rules
|
||||
from erpnext.accounts.doctype.journal_entry.journal_entry import get_exchange_rate
|
||||
|
||||
class calculate_taxes_and_totals(object):
|
||||
def __init__(self, doc):
|
||||
@ -106,7 +107,7 @@ class calculate_taxes_and_totals(object):
|
||||
elif item.discount_amount and item.pricing_rules:
|
||||
item.rate = item.price_list_rate - item.discount_amount
|
||||
|
||||
if item.doctype in ['Quotation Item', 'Sales Order Item', 'Delivery Note Item', 'Sales Invoice Item']:
|
||||
if item.doctype in ['Quotation Item', 'Sales Order Item', 'Delivery Note Item', 'Sales Invoice Item', 'POS Invoice Item']:
|
||||
item.rate_with_margin, item.base_rate_with_margin = self.calculate_margin(item)
|
||||
if flt(item.rate_with_margin) > 0:
|
||||
item.rate = flt(item.rate_with_margin * (1.0 - (item.discount_percentage / 100.0)), item.precision("rate"))
|
||||
@ -758,3 +759,35 @@ def get_rounded_tax_amount(itemised_tax, precision):
|
||||
for taxes in itemised_tax.values():
|
||||
for tax_account in taxes:
|
||||
taxes[tax_account]["tax_amount"] = flt(taxes[tax_account]["tax_amount"], precision)
|
||||
|
||||
class init_landed_taxes_and_totals(object):
|
||||
def __init__(self, doc):
|
||||
self.doc = doc
|
||||
self.tax_field = 'taxes' if self.doc.doctype == 'Landed Cost Voucher' else 'additional_costs'
|
||||
self.set_account_currency()
|
||||
self.set_exchange_rate()
|
||||
self.set_amounts_in_company_currency()
|
||||
|
||||
def set_account_currency(self):
|
||||
company_currency = erpnext.get_company_currency(self.doc.company)
|
||||
for d in self.doc.get(self.tax_field):
|
||||
if not d.account_currency:
|
||||
account_currency = frappe.db.get_value('Account', d.expense_account, 'account_currency')
|
||||
d.account_currency = account_currency or company_currency
|
||||
|
||||
def set_exchange_rate(self):
|
||||
company_currency = erpnext.get_company_currency(self.doc.company)
|
||||
for d in self.doc.get(self.tax_field):
|
||||
if d.account_currency == company_currency:
|
||||
d.exchange_rate = 1
|
||||
elif not d.exchange_rate or d.exchange_rate == 1 or self.doc.posting_date:
|
||||
d.exchange_rate = get_exchange_rate(self.doc.posting_date, account=d.expense_account,
|
||||
account_currency=d.account_currency, company=self.doc.company)
|
||||
|
||||
if not d.exchange_rate:
|
||||
frappe.throw(_("Row {0}: Exchange Rate is mandatory").format(d.idx))
|
||||
|
||||
def set_amounts_in_company_currency(self):
|
||||
for d in self.doc.get(self.tax_field):
|
||||
d.amount = flt(d.amount, d.precision("amount"))
|
||||
d.base_amount = flt(d.amount * flt(d.exchange_rate), d.precision("base_amount"))
|
@ -44,6 +44,7 @@ class TestMpesaSettings(unittest.TestCase):
|
||||
create_mpesa_settings(payment_gateway_name="Payment")
|
||||
mpesa_account = frappe.db.get_value("Payment Gateway Account", {"payment_gateway": 'Mpesa-Payment'}, "payment_account")
|
||||
frappe.db.set_value("Account", mpesa_account, "account_currency", "KES")
|
||||
frappe.db.set_value("Customer", "_Test Customer", "default_currency", "KES")
|
||||
|
||||
pos_invoice = create_pos_invoice(do_not_submit=1)
|
||||
pos_invoice.append("payments", {'mode_of_payment': 'Mpesa-Payment', 'account': mpesa_account, 'amount': 500})
|
||||
@ -69,6 +70,8 @@ class TestMpesaSettings(unittest.TestCase):
|
||||
self.assertEquals(pos_invoice.mpesa_receipt_number, "LGR7OWQX0R")
|
||||
self.assertEquals(integration_request.status, "Completed")
|
||||
|
||||
frappe.db.set_value("Customer", "_Test Customer", "default_currency", "")
|
||||
|
||||
def create_mpesa_settings(payment_gateway_name="Express"):
|
||||
if frappe.db.exists("Mpesa Settings", payment_gateway_name):
|
||||
return frappe.get_doc("Mpesa Settings", payment_gateway_name)
|
||||
|
@ -416,9 +416,6 @@ regional_overrides = {
|
||||
'Italy': {
|
||||
'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',
|
||||
},
|
||||
'Germany': {
|
||||
'erpnext.controllers.accounts_controller.validate_regional': 'erpnext.regional.germany.accounts_controller.validate_regional',
|
||||
}
|
||||
}
|
||||
user_privacy_documents = [
|
||||
|
@ -88,7 +88,7 @@ def get_events(start, end, filters=None):
|
||||
|
||||
def add_assignments(events, start, end, conditions=None):
|
||||
query = """select name, start_date, end_date, employee_name,
|
||||
employee, docstatus
|
||||
employee, docstatus, shift_type
|
||||
from `tabShift Assignment` where
|
||||
start_date >= %(start_date)s
|
||||
or end_date <= %(end_date)s
|
||||
@ -97,18 +97,40 @@ def add_assignments(events, start, end, conditions=None):
|
||||
if conditions:
|
||||
query += conditions
|
||||
|
||||
for d in frappe.db.sql(query, {"start_date":start, "end_date":end}, as_dict=True):
|
||||
e = {
|
||||
"name": d.name,
|
||||
"doctype": "Shift Assignment",
|
||||
"start_date": d.start_date,
|
||||
"end_date": d.end_date if d.end_date else nowdate(),
|
||||
"title": cstr(d.employee_name) + ": "+ \
|
||||
cstr(d.shift_type),
|
||||
"docstatus": d.docstatus
|
||||
}
|
||||
if e not in events:
|
||||
events.append(e)
|
||||
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 = {
|
||||
"name": d.name,
|
||||
"doctype": "Shift Assignment",
|
||||
"start_date": start_timing,
|
||||
"end_date": end_timing,
|
||||
"title": cstr(d.employee_name) + ": "+ \
|
||||
cstr(d.shift_type),
|
||||
"docstatus": d.docstatus,
|
||||
"allDay": 0
|
||||
}
|
||||
if e not in events:
|
||||
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):
|
||||
|
@ -6,14 +6,8 @@ frappe.views.calendar["Shift Assignment"] = {
|
||||
"start": "start_date",
|
||||
"end": "end_date",
|
||||
"id": "name",
|
||||
"docstatus": 1
|
||||
},
|
||||
options: {
|
||||
header: {
|
||||
left: 'prev,next today',
|
||||
center: 'title',
|
||||
right: 'month'
|
||||
}
|
||||
"docstatus": 1,
|
||||
"allDay": "allDay",
|
||||
},
|
||||
get_events_method: "erpnext.hr.doctype.shift_assignment.shift_assignment.get_events"
|
||||
}
|
@ -677,7 +677,7 @@ erpnext.patches.v13_0.move_tax_slabs_from_payroll_period_to_income_tax_slab #123
|
||||
erpnext.patches.v12_0.fix_quotation_expired_status
|
||||
erpnext.patches.v12_0.update_appointment_reminder_scheduler_entry
|
||||
erpnext.patches.v12_0.rename_pos_closing_doctype
|
||||
erpnext.patches.v13_0.replace_pos_payment_mode_table
|
||||
erpnext.patches.v13_0.replace_pos_payment_mode_table #2020-12-29
|
||||
erpnext.patches.v12_0.remove_duplicate_leave_ledger_entries #2020-05-22
|
||||
erpnext.patches.v13_0.patch_to_fix_reverse_linking_in_additional_salary_encashment_and_incentive
|
||||
execute:frappe.reload_doc("HR", "doctype", "Employee Advance")
|
||||
@ -743,7 +743,9 @@ erpnext.patches.v13_0.updates_for_multi_currency_payroll
|
||||
erpnext.patches.v13_0.create_leave_policy_assignment_based_on_employee_current_leave_policy
|
||||
erpnext.patches.v13_0.add_po_to_global_search
|
||||
erpnext.patches.v13_0.update_returned_qty_in_pr_dn
|
||||
erpnext.patches.v13_0.create_uae_pos_invoice_fields
|
||||
erpnext.patches.v13_0.update_project_template_tasks
|
||||
erpnext.patches.v13_0.set_company_in_leave_ledger_entry
|
||||
erpnext.patches.v13_0.convert_qi_parameter_to_link_field
|
||||
erpnext.patches.v13_0.setup_patient_history_settings_for_standard_doctypes
|
||||
erpnext.patches.v13_0.add_naming_series_to_old_projects
|
||||
|
26
erpnext/patches/v13_0/add_naming_series_to_old_projects.py
Normal file
26
erpnext/patches/v13_0/add_naming_series_to_old_projects.py
Normal file
@ -0,0 +1,26 @@
|
||||
from __future__ import unicode_literals
|
||||
import frappe
|
||||
from frappe.custom.doctype.property_setter.property_setter import make_property_setter, delete_property_setter
|
||||
|
||||
def execute():
|
||||
frappe.reload_doc("projects", "doctype", "project")
|
||||
projects = frappe.db.get_all("Project",
|
||||
fields=["name", "naming_series", "modified"],
|
||||
filters={
|
||||
"naming_series": ["is", "not set"]
|
||||
},
|
||||
order_by="timestamp(modified) asc")
|
||||
|
||||
# disable set only once as the old docs must be saved
|
||||
# (to bypass 'Cant change naming series' validation on save)
|
||||
make_property_setter("Project", "naming_series", "set_only_once", 0, "Check")
|
||||
|
||||
for entry in projects:
|
||||
# need to save the doc so that users can edit old projects
|
||||
doc = frappe.get_doc("Project", entry.name)
|
||||
if not doc.naming_series:
|
||||
doc.naming_series = "PROJ-.####"
|
||||
doc.save()
|
||||
|
||||
delete_property_setter("Project", "set_only_once", "naming_series")
|
||||
frappe.db.commit()
|
14
erpnext/patches/v13_0/create_uae_pos_invoice_fields.py
Normal file
14
erpnext/patches/v13_0/create_uae_pos_invoice_fields.py
Normal file
@ -0,0 +1,14 @@
|
||||
# Copyright (c) 2019, Frappe and Contributors
|
||||
# License: GNU General Public License v3. See license.txt
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import frappe
|
||||
from erpnext.regional.united_arab_emirates.setup import make_custom_fields
|
||||
|
||||
def execute():
|
||||
company = frappe.get_all('Company', filters = {'country': ['in', ['Saudi Arabia', 'United Arab Emirates']]})
|
||||
if not company:
|
||||
return
|
||||
|
||||
make_custom_fields()
|
@ -6,12 +6,10 @@ from __future__ import unicode_literals
|
||||
import frappe
|
||||
|
||||
def execute():
|
||||
frappe.reload_doc("accounts", "doctype", "POS Payment Method")
|
||||
frappe.reload_doc("accounts", "doctype", "pos_payment_method")
|
||||
pos_profiles = frappe.get_all("POS Profile")
|
||||
|
||||
for pos_profile in pos_profiles:
|
||||
if not pos_profile.get("payments"): return
|
||||
|
||||
payments = frappe.db.sql("""
|
||||
select idx, parentfield, parenttype, parent, mode_of_payment, `default` from `tabSales Invoice Payment` where parent=%s
|
||||
""", pos_profile.name, as_dict=1)
|
||||
|
@ -195,6 +195,10 @@ erpnext.buying.BuyingController = erpnext.TransactionController.extend({
|
||||
this._super(doc, cdt, cdn);
|
||||
},
|
||||
|
||||
batch_no: function(doc, cdt, cdn) {
|
||||
this._super(doc, cdt, cdn);
|
||||
},
|
||||
|
||||
received_qty: function(doc, cdt, cdn) {
|
||||
this.calculate_accepted_qty(doc, cdt, cdn)
|
||||
},
|
||||
|
@ -105,10 +105,18 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
frappe.ui.form.on(this.frm.doctype + " Item", {
|
||||
items_add: function(frm, cdt, cdn) {
|
||||
var item = frappe.get_doc(cdt, cdn);
|
||||
if(!item.warehouse && frm.doc.set_warehouse) {
|
||||
if (!item.warehouse && frm.doc.set_warehouse) {
|
||||
item.warehouse = frm.doc.set_warehouse;
|
||||
}
|
||||
|
||||
if (!item.target_warehouse && frm.doc.set_target_warehouse) {
|
||||
item.target_warehouse = frm.doc.set_target_warehouse;
|
||||
}
|
||||
|
||||
if (!item.from_warehouse && frm.doc.set_from_warehouse) {
|
||||
item.from_warehouse = frm.doc.set_from_warehouse;
|
||||
}
|
||||
|
||||
erpnext.accounts.dimensions.copy_dimension_from_first_row(frm, cdt, cdn, 'items');
|
||||
}
|
||||
});
|
||||
@ -227,6 +235,8 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
}
|
||||
};
|
||||
|
||||
this.frm.trigger('set_default_internal_warehouse');
|
||||
|
||||
return frappe.run_serially([
|
||||
() => set_value('currency', currency),
|
||||
() => set_value('price_list_currency', currency),
|
||||
@ -589,11 +599,21 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
return frappe.db.get_value("Item", item.item_code, ["has_batch_no", "has_serial_no"])
|
||||
.then((r) => {
|
||||
if (r.message &&
|
||||
(r.message.has_batch_no || r.message.has_serial_no)) {
|
||||
(r.message.has_batch_no || r.message.has_serial_no)) {
|
||||
frappe.flags.hide_serial_batch_dialog = false;
|
||||
}
|
||||
});
|
||||
},
|
||||
() => {
|
||||
// check if batch serial selector is disabled or not
|
||||
if (show_batch_dialog && !frappe.flags.hide_serial_batch_dialog)
|
||||
return frappe.db.get_single_value('Stock Settings', 'disable_serial_no_and_batch_selector')
|
||||
.then((value) => {
|
||||
if (value) {
|
||||
frappe.flags.hide_serial_batch_dialog = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
() => {
|
||||
if(show_batch_dialog && !frappe.flags.hide_serial_batch_dialog) {
|
||||
var d = locals[cdt][cdn];
|
||||
@ -648,7 +668,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
args: item_args
|
||||
},
|
||||
callback: function(r) {
|
||||
frappe.model.set_value(item.doctype, item.name, 'rate', r.message);
|
||||
frappe.model.set_value(item.doctype, item.name, 'rate', r.message * item.conversion_factor);
|
||||
}
|
||||
});
|
||||
},
|
||||
@ -714,6 +734,31 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
this.calculate_taxes_and_totals(false);
|
||||
},
|
||||
|
||||
update_stock: function() {
|
||||
this.frm.trigger('set_default_internal_warehouse');
|
||||
},
|
||||
|
||||
set_default_internal_warehouse: function() {
|
||||
let me = this;
|
||||
if ((this.frm.doc.doctype === 'Sales Invoice' && me.frm.doc.update_stock)
|
||||
|| this.frm.doc.doctype == 'Delivery Note') {
|
||||
if (this.frm.doc.is_internal_customer && this.frm.doc.company === this.frm.doc.represents_company) {
|
||||
frappe.db.get_value('Company', this.frm.doc.company, 'default_in_transit_warehouse', function(value) {
|
||||
me.frm.set_value('set_target_warehouse', value.default_in_transit_warehouse);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
if ((this.frm.doc.doctype === 'Purchase Invoice' && me.frm.doc.update_stock)
|
||||
|| this.frm.doc.doctype == 'Purchase Receipt') {
|
||||
if (this.frm.doc.is_internal_supplier && this.frm.doc.company === this.frm.doc.represents_company) {
|
||||
frappe.db.get_value('Company', this.frm.doc.company, 'default_in_transit_warehouse', function(value) {
|
||||
me.frm.set_value('set_from_warehouse', value.default_in_transit_warehouse);
|
||||
});
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
company: function() {
|
||||
var me = this;
|
||||
var set_pricing = function() {
|
||||
@ -800,7 +845,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
in_list(['Purchase Order', 'Purchase Receipt', 'Purchase Invoice'], this.frm.doctype)) {
|
||||
erpnext.utils.get_shipping_address(this.frm, function(){
|
||||
set_party_account(set_pricing);
|
||||
})
|
||||
});
|
||||
|
||||
// Get default company billing address in Purchase Invoice, Order and Receipt
|
||||
frappe.call({
|
||||
@ -1099,6 +1144,11 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
}
|
||||
},
|
||||
|
||||
batch_no: function(doc, cdt, cdn) {
|
||||
let item = frappe.get_doc(cdt, cdn);
|
||||
this.apply_price_list(item, true);
|
||||
},
|
||||
|
||||
toggle_conversion_factor: function(item) {
|
||||
// toggle read only property for conversion factor field if the uom and stock uom are same
|
||||
if(this.frm.get_field('items').grid.fields_map.conversion_factor) {
|
||||
@ -1403,6 +1453,7 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
"pricing_rules": d.pricing_rules,
|
||||
"warehouse": d.warehouse,
|
||||
"serial_no": d.serial_no,
|
||||
"batch_no": d.batch_no,
|
||||
"price_list_rate": d.price_list_rate,
|
||||
"conversion_factor": d.conversion_factor || 1.0
|
||||
});
|
||||
@ -1961,6 +2012,14 @@ erpnext.TransactionController = erpnext.taxes_and_totals.extend({
|
||||
this.autofill_warehouse(this.frm.doc.items, "warehouse", this.frm.doc.set_warehouse);
|
||||
},
|
||||
|
||||
set_target_warehouse: function() {
|
||||
this.autofill_warehouse(this.frm.doc.items, "target_warehouse", this.frm.doc.set_target_warehouse);
|
||||
},
|
||||
|
||||
set_from_warehouse: function() {
|
||||
this.autofill_warehouse(this.frm.doc.items, "from_warehouse", this.frm.doc.set_from_warehouse);
|
||||
},
|
||||
|
||||
autofill_warehouse : function (child_table, warehouse_field, warehouse) {
|
||||
if (warehouse && child_table && child_table.length) {
|
||||
let doctype = child_table[0].doctype;
|
||||
|
@ -276,6 +276,12 @@ erpnext.utils.validate_mandatory = function(frm, label, value, trigger_on) {
|
||||
|
||||
erpnext.utils.get_shipping_address = function(frm, callback){
|
||||
if (frm.doc.company) {
|
||||
if (!(frm.doc.inter_com_order_reference || frm.doc.internal_invoice_reference ||
|
||||
frm.doc.internal_order_reference)) {
|
||||
if (callback) {
|
||||
return callback();
|
||||
}
|
||||
}
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.custom.address.get_shipping_address",
|
||||
args: {
|
||||
|
@ -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)
|
@ -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)
|
@ -40,14 +40,12 @@ erpnext.setup_auto_gst_taxation = (doctype) => {
|
||||
callback: function(r) {
|
||||
if(r.message) {
|
||||
frm.set_value('taxes_and_charges', r.message.taxes_and_charges);
|
||||
frm.set_value('taxes', r.message.taxes);
|
||||
frm.set_value('place_of_supply', r.message.place_of_supply);
|
||||
} else if (frm.doc.is_internal_supplier || frm.doc.is_internal_customer) {
|
||||
frm.set_value('taxes_and_charges', '');
|
||||
frm.set_value('taxes', []);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -171,7 +171,7 @@ def get_regional_address_details(party_details, doctype, company):
|
||||
|
||||
if is_internal_transfer(party_details, doctype):
|
||||
party_details.taxes_and_charges = ''
|
||||
party_details.taxes = ''
|
||||
party_details.taxes = []
|
||||
return party_details
|
||||
|
||||
if doctype in ("Sales Invoice", "Delivery Note", "Sales Order"):
|
||||
|
@ -255,15 +255,16 @@ class Gstr1Report(object):
|
||||
|
||||
for item_code, tax_amounts in item_wise_tax_detail.items():
|
||||
tax_rate = tax_amounts[0]
|
||||
if cgst_or_sgst:
|
||||
tax_rate *= 2
|
||||
if parent not in self.cgst_sgst_invoices:
|
||||
self.cgst_sgst_invoices.append(parent)
|
||||
if tax_rate:
|
||||
if cgst_or_sgst:
|
||||
tax_rate *= 2
|
||||
if parent not in self.cgst_sgst_invoices:
|
||||
self.cgst_sgst_invoices.append(parent)
|
||||
|
||||
rate_based_dict = self.items_based_on_tax_rate\
|
||||
.setdefault(parent, {}).setdefault(tax_rate, [])
|
||||
if item_code not in rate_based_dict:
|
||||
rate_based_dict.append(item_code)
|
||||
rate_based_dict = self.items_based_on_tax_rate\
|
||||
.setdefault(parent, {}).setdefault(tax_rate, [])
|
||||
if item_code not in rate_based_dict:
|
||||
rate_based_dict.append(item_code)
|
||||
except ValueError:
|
||||
continue
|
||||
if unidentified_gst_accounts:
|
||||
|
@ -110,9 +110,11 @@ def make_custom_fields():
|
||||
'Purchase Order': purchase_invoice_fields + invoice_fields,
|
||||
'Purchase Receipt': purchase_invoice_fields + invoice_fields,
|
||||
'Sales Invoice': sales_invoice_fields + invoice_fields,
|
||||
'POS Invoice': sales_invoice_fields + invoice_fields,
|
||||
'Sales Order': sales_invoice_fields + invoice_fields,
|
||||
'Delivery Note': sales_invoice_fields + invoice_fields,
|
||||
'Sales Invoice Item': invoice_item_fields + delivery_date_field + [is_zero_rated, is_exempt],
|
||||
'POS Invoice Item': invoice_item_fields + delivery_date_field + [is_zero_rated, is_exempt],
|
||||
'Purchase Invoice Item': invoice_item_fields,
|
||||
'Sales Order Item': invoice_item_fields,
|
||||
'Delivery Note Item': invoice_item_fields,
|
||||
|
@ -84,7 +84,10 @@ class Customer(TransactionBase):
|
||||
frappe.throw(_("{0} is not a company bank account").format(frappe.bold(self.default_bank_account)))
|
||||
|
||||
def validate_internal_customer(self):
|
||||
if self.is_internal_customer and frappe.db.get_value('Customer', {"represents_company": self.represents_company}, "name"):
|
||||
internal_customer = frappe.db.get_value("Customer",
|
||||
{"is_internal_customer": 1, "represents_company": self.represents_company, "name": ("!=", self.name)}, "name")
|
||||
|
||||
if internal_customer:
|
||||
frappe.throw(_("Internal Customer for company {0} already exists").format(
|
||||
frappe.bold(self.represents_company)))
|
||||
|
||||
|
@ -171,8 +171,10 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
|
||||
this.frm.add_custom_button(__('Request for Raw Materials'), () => this.make_raw_material_request(), __('Create'));
|
||||
}
|
||||
|
||||
// make purchase order
|
||||
// Make Purchase Order
|
||||
if (!this.frm.doc.is_internal_customer) {
|
||||
this.frm.add_custom_button(__('Purchase Order'), () => this.make_purchase_order(), __('Create'));
|
||||
}
|
||||
|
||||
// maintenance
|
||||
if(flt(doc.per_delivered, 2) < 100 && (order_is_maintenance || order_is_a_custom_sale)) {
|
||||
@ -193,16 +195,15 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend(
|
||||
|
||||
if (doc.docstatus === 1 && !doc.inter_company_order_reference) {
|
||||
let me = this;
|
||||
frappe.model.with_doc("Customer", me.frm.doc.customer, () => {
|
||||
let customer = frappe.model.get_doc("Customer", me.frm.doc.customer);
|
||||
let internal = customer.is_internal_customer;
|
||||
let disabled = customer.disabled;
|
||||
if (internal === 1 && disabled === 0) {
|
||||
me.frm.add_custom_button("Inter Company Order", function() {
|
||||
me.make_inter_company_order();
|
||||
}, __('Create'));
|
||||
}
|
||||
});
|
||||
let internal = me.frm.doc.is_internal_customer;
|
||||
if (internal) {
|
||||
let button_label = (me.frm.doc.company === me.frm.doc.represents_company) ? "Internal Purchase Order" :
|
||||
"Inter Company Purchase Order";
|
||||
|
||||
me.frm.add_custom_button(button_label, function() {
|
||||
me.make_inter_company_order();
|
||||
}, __('Create'));
|
||||
}
|
||||
}
|
||||
}
|
||||
// payment request
|
||||
|
@ -107,6 +107,8 @@
|
||||
"tc_name",
|
||||
"terms",
|
||||
"more_info",
|
||||
"is_internal_customer",
|
||||
"represents_company",
|
||||
"inter_company_order_reference",
|
||||
"project",
|
||||
"party_account_currency",
|
||||
@ -1103,7 +1105,8 @@
|
||||
"hide_days": 1,
|
||||
"hide_seconds": 1,
|
||||
"label": "Inter Company Order Reference",
|
||||
"options": "Purchase Order"
|
||||
"options": "Purchase Order",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"description": "Track this Sales Order against any Project",
|
||||
@ -1455,13 +1458,29 @@
|
||||
"hide_seconds": 1,
|
||||
"label": "Skip Delivery Note",
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fetch_from": "customer.is_internal_customer",
|
||||
"fieldname": "is_internal_customer",
|
||||
"fieldtype": "Check",
|
||||
"label": "Is Internal Customer",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fetch_from": "customer.represents_company",
|
||||
"fieldname": "represents_company",
|
||||
"fieldtype": "Link",
|
||||
"label": "Represents Company",
|
||||
"options": "Company",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-file-text",
|
||||
"idx": 105,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-10-30 13:59:18.628077",
|
||||
"modified": "2021-01-20 23:40:39.929296",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Sales Order",
|
||||
|
@ -69,6 +69,10 @@ erpnext.PointOfSale.Controller = class {
|
||||
dialog.fields_dict.balance_details.grid.refresh();
|
||||
});
|
||||
}
|
||||
const pos_profile_query = {
|
||||
query: 'erpnext.accounts.doctype.pos_profile.pos_profile.pos_profile_query',
|
||||
filters: { company: frappe.defaults.get_default('company') }
|
||||
}
|
||||
const dialog = new frappe.ui.Dialog({
|
||||
title: __('Create POS Opening Entry'),
|
||||
static: true,
|
||||
@ -80,6 +84,7 @@ erpnext.PointOfSale.Controller = class {
|
||||
{
|
||||
fieldtype: 'Link', label: __('POS Profile'),
|
||||
options: 'POS Profile', fieldname: 'pos_profile', reqd: 1,
|
||||
get_query: () => pos_profile_query,
|
||||
onchange: () => fetch_pos_payment_methods()
|
||||
},
|
||||
{
|
||||
@ -124,9 +129,8 @@ erpnext.PointOfSale.Controller = class {
|
||||
});
|
||||
|
||||
frappe.db.get_doc("POS Profile", this.pos_profile).then((profile) => {
|
||||
Object.assign(this.settings, profile);
|
||||
this.settings.customer_groups = profile.customer_groups.map(group => group.customer_group);
|
||||
this.settings.hide_images = profile.hide_images;
|
||||
this.settings.auto_add_item_to_cart = profile.auto_add_item_to_cart;
|
||||
this.make_app();
|
||||
});
|
||||
}
|
||||
@ -255,11 +259,9 @@ erpnext.PointOfSale.Controller = class {
|
||||
get_frm: () => this.frm,
|
||||
|
||||
cart_item_clicked: (item_code, batch_no, uom) => {
|
||||
const item_row = this.frm.doc.items.find(
|
||||
i => i.item_code === item_code
|
||||
&& i.uom === uom
|
||||
&& (!batch_no || (batch_no && i.batch_no === batch_no))
|
||||
);
|
||||
const search_field = batch_no ? 'batch_no' : 'item_code';
|
||||
const search_value = batch_no || item_code;
|
||||
const item_row = this.frm.doc.items.find(i => i[search_field] === search_value && i.uom === uom);
|
||||
this.item_details.toggle_item_details_section(item_row);
|
||||
},
|
||||
|
||||
@ -281,6 +283,7 @@ erpnext.PointOfSale.Controller = class {
|
||||
init_item_details() {
|
||||
this.item_details = new erpnext.PointOfSale.ItemDetails({
|
||||
wrapper: this.$components_wrapper,
|
||||
settings: this.settings,
|
||||
events: {
|
||||
get_frm: () => this.frm,
|
||||
|
||||
@ -415,6 +418,11 @@ erpnext.PointOfSale.Controller = class {
|
||||
() => this.item_selector.toggle_component(true)
|
||||
]);
|
||||
},
|
||||
delete_order: (name) => {
|
||||
frappe.model.delete_doc(this.frm.doc.doctype, name, () => {
|
||||
this.recent_order_list.refresh_list();
|
||||
});
|
||||
},
|
||||
new_order: () => {
|
||||
frappe.run_serially([
|
||||
() => frappe.dom.freeze(),
|
||||
@ -696,14 +704,14 @@ erpnext.PointOfSale.Controller = class {
|
||||
frappe.dom.freeze();
|
||||
const { doctype, name, current_item } = this.item_details;
|
||||
|
||||
frappe.model.set_value(doctype, name, 'qty', 0);
|
||||
|
||||
this.frm.script_manager.trigger('qty', doctype, name).then(() => {
|
||||
frappe.model.clear_doc(doctype, name);
|
||||
this.update_cart_html(current_item, true);
|
||||
this.item_details.toggle_item_details_section(undefined);
|
||||
frappe.dom.unfreeze();
|
||||
})
|
||||
frappe.model.set_value(doctype, name, 'qty', 0)
|
||||
.then(() => {
|
||||
frappe.model.clear_doc(doctype, name);
|
||||
this.update_cart_html(current_item, true);
|
||||
this.item_details.toggle_item_details_section(undefined);
|
||||
frappe.dom.unfreeze();
|
||||
})
|
||||
.catch(e => console.log(e));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -5,6 +5,8 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
this.customer_info = undefined;
|
||||
this.hide_images = settings.hide_images;
|
||||
this.allowed_customer_groups = settings.customer_groups;
|
||||
this.allow_rate_change = settings.allow_rate_change;
|
||||
this.allow_discount_change = settings.allow_discount_change;
|
||||
|
||||
this.init_component();
|
||||
}
|
||||
@ -201,7 +203,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
me.events.checkout();
|
||||
me.toggle_checkout_btn(false);
|
||||
|
||||
me.$add_discount_elem.removeClass("d-none");
|
||||
me.allow_discount_change && me.$add_discount_elem.removeClass("d-none");
|
||||
});
|
||||
|
||||
this.$totals_section.on('click', '.edit-cart-btn', () => {
|
||||
@ -479,11 +481,15 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
update_totals_section(frm) {
|
||||
if (!frm) frm = this.events.get_frm();
|
||||
|
||||
this.render_net_total(frm.doc.base_net_total);
|
||||
this.render_grand_total(frm.doc.base_grand_total);
|
||||
this.render_net_total(frm.doc.net_total);
|
||||
this.render_grand_total(frm.doc.grand_total);
|
||||
|
||||
const taxes = frm.doc.taxes.map(t => { return { description: t.description, rate: t.rate }})
|
||||
this.render_taxes(frm.doc.base_total_taxes_and_charges, taxes);
|
||||
const taxes = frm.doc.taxes.map(t => {
|
||||
return {
|
||||
description: t.description, rate: t.rate
|
||||
}
|
||||
});
|
||||
this.render_taxes(frm.doc.total_taxes_and_charges, taxes);
|
||||
}
|
||||
|
||||
render_net_total(value) {
|
||||
@ -545,7 +551,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
get_cart_item({ item_code, batch_no, uom }) {
|
||||
const batch_attr = `[data-batch-no="${escape(batch_no)}"]`;
|
||||
const item_code_attr = `[data-item-code="${escape(item_code)}"]`;
|
||||
const uom_attr = `[data-uom=${escape(uom)}]`;
|
||||
const uom_attr = `[data-uom="${escape(uom)}"]`;
|
||||
|
||||
const item_selector = batch_no ?
|
||||
`.cart-item-wrapper${batch_attr}${uom_attr}` : `.cart-item-wrapper${item_code_attr}${uom_attr}`;
|
||||
@ -667,7 +673,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
|
||||
update_selector_value_in_cart_item(selector, value, item) {
|
||||
const $item_to_update = this.get_cart_item(item);
|
||||
$item_to_update.attr(`data-${selector}`, value);
|
||||
$item_to_update.attr(`data-${selector}`, escape(value));
|
||||
}
|
||||
|
||||
toggle_checkout_btn(show_checkout) {
|
||||
@ -702,14 +708,26 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
on_numpad_event($btn) {
|
||||
const current_action = $btn.attr('data-button-value');
|
||||
const action_is_field_edit = ['qty', 'discount_percentage', 'rate'].includes(current_action);
|
||||
|
||||
this.highlight_numpad_btn($btn, current_action);
|
||||
const action_is_allowed = action_is_field_edit ? (
|
||||
(current_action == 'rate' && this.allow_rate_change) ||
|
||||
(current_action == 'discount_percentage' && this.allow_discount_change) ||
|
||||
(current_action == 'qty')) : true;
|
||||
|
||||
const action_is_pressed_twice = this.prev_action === current_action;
|
||||
const first_click_event = !this.prev_action;
|
||||
const field_to_edit_changed = this.prev_action && this.prev_action != current_action;
|
||||
|
||||
if (action_is_field_edit) {
|
||||
if (!action_is_allowed) {
|
||||
const label = current_action == 'rate' ? 'Rate'.bold() : 'Discount'.bold();
|
||||
const message = __('Editing {0} is not allowed as per POS Profile settings', [label]);
|
||||
frappe.show_alert({
|
||||
indicator: 'red',
|
||||
message: message
|
||||
});
|
||||
frappe.utils.play_sound("error");
|
||||
return;
|
||||
}
|
||||
|
||||
if (first_click_event || field_to_edit_changed) {
|
||||
this.prev_action = current_action;
|
||||
@ -753,6 +771,7 @@ erpnext.PointOfSale.ItemCart = class {
|
||||
this.numpad_value = current_action;
|
||||
}
|
||||
|
||||
this.highlight_numpad_btn($btn, current_action);
|
||||
this.events.numpad_event(this.numpad_value, this.prev_action);
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,9 @@
|
||||
erpnext.PointOfSale.ItemDetails = class {
|
||||
constructor({ wrapper, events }) {
|
||||
constructor({ wrapper, events, settings }) {
|
||||
this.wrapper = wrapper;
|
||||
this.events = events;
|
||||
this.allow_rate_change = settings.allow_rate_change;
|
||||
this.allow_discount_change = settings.allow_discount_change;
|
||||
this.current_item = {};
|
||||
|
||||
this.init_component();
|
||||
@ -207,17 +209,27 @@ erpnext.PointOfSale.ItemDetails = class {
|
||||
bind_custom_control_change_event() {
|
||||
const me = this;
|
||||
if (this.rate_control) {
|
||||
this.rate_control.df.onchange = function() {
|
||||
if (this.value || flt(this.value) === 0) {
|
||||
me.events.form_updated(me.doctype, me.name, 'rate', this.value).then(() => {
|
||||
const item_row = frappe.get_doc(me.doctype, me.name);
|
||||
const doc = me.events.get_frm().doc;
|
||||
|
||||
me.$item_price.html(format_currency(item_row.rate, doc.currency));
|
||||
me.render_discount_dom(item_row);
|
||||
});
|
||||
}
|
||||
if (this.allow_rate_change) {
|
||||
this.rate_control.df.onchange = function() {
|
||||
if (this.value || flt(this.value) === 0) {
|
||||
me.events.form_updated(me.doctype, me.name, 'rate', this.value).then(() => {
|
||||
const item_row = frappe.get_doc(me.doctype, me.name);
|
||||
const doc = me.events.get_frm().doc;
|
||||
|
||||
me.$item_price.html(format_currency(item_row.rate, doc.currency));
|
||||
me.render_discount_dom(item_row);
|
||||
});
|
||||
}
|
||||
};
|
||||
} else {
|
||||
this.rate_control.df.read_only = 1;
|
||||
}
|
||||
this.rate_control.refresh();
|
||||
}
|
||||
|
||||
if (this.discount_percentage_control && !this.allow_discount_change) {
|
||||
this.discount_percentage_control.df.read_only = 1;
|
||||
this.discount_percentage_control.refresh();
|
||||
}
|
||||
|
||||
if (this.warehouse_control) {
|
||||
@ -294,8 +306,16 @@ erpnext.PointOfSale.ItemDetails = class {
|
||||
}
|
||||
|
||||
frappe.model.on("POS Invoice Item", "*", (fieldname, value, item_row) => {
|
||||
const { item_code, batch_no, uom } = this.current_item;
|
||||
const item_code_is_same = item_code === item_row.item_code;
|
||||
const batch_is_same = batch_no == item_row.batch_no;
|
||||
const uom_is_same = uom === item_row.uom;
|
||||
// check if current_item is same as item_row
|
||||
const item_is_same = item_code_is_same && batch_is_same && uom_is_same ? true : false;
|
||||
|
||||
const field_control = me[`${fieldname}_control`];
|
||||
if (field_control) {
|
||||
|
||||
if (item_is_same && field_control && field_control.get_value() !== value) {
|
||||
field_control.set_value(value);
|
||||
cur_pos.update_cart_html(item_row);
|
||||
}
|
||||
|
@ -265,6 +265,14 @@ erpnext.PointOfSale.PastOrderSummary = class {
|
||||
this.$summary_wrapper.addClass('d-none');
|
||||
});
|
||||
|
||||
this.$summary_container.on('click', '.delete-btn', () => {
|
||||
this.events.delete_order(this.doc.name);
|
||||
this.show_summary_placeholder();
|
||||
// this.toggle_component(false);
|
||||
// this.$component.find('.no-summary-placeholder').removeClass('d-none');
|
||||
// this.$summary_wrapper.addClass('d-none');
|
||||
});
|
||||
|
||||
this.$summary_container.on('click', '.new-btn', () => {
|
||||
this.events.new_order();
|
||||
this.toggle_component(false);
|
||||
@ -401,7 +409,7 @@ erpnext.PointOfSale.PastOrderSummary = class {
|
||||
return [{ condition: true, visible_btns: ['Print Receipt', 'Email Receipt', 'New Order'] }];
|
||||
|
||||
return [
|
||||
{ condition: this.doc.docstatus === 0, visible_btns: ['Edit Order'] },
|
||||
{ condition: this.doc.docstatus === 0, visible_btns: ['Edit Order', 'Delete Order'] },
|
||||
{ condition: !this.doc.is_return && this.doc.docstatus === 1, visible_btns: ['Print Receipt', 'Email Receipt', 'Return']},
|
||||
{ condition: this.doc.is_return && this.doc.docstatus === 1, visible_btns: ['Print Receipt', 'Email Receipt']}
|
||||
];
|
||||
|
@ -399,6 +399,10 @@ erpnext.selling.SellingController = erpnext.TransactionController.extend({
|
||||
}
|
||||
},
|
||||
|
||||
batch_no: function(doc, cdt, cdn) {
|
||||
this._super(doc, cdt, cdn);
|
||||
},
|
||||
|
||||
qty: function(doc, cdt, cdn) {
|
||||
this._super(doc, cdt, cdn);
|
||||
|
||||
|
@ -180,6 +180,13 @@ def create_lead_for_item_inquiry(lead, subject, message):
|
||||
lead_doc.update(lead)
|
||||
lead_doc.set('lead_owner', '')
|
||||
|
||||
if not frappe.db.exists('Lead Source', 'Product Inquiry'):
|
||||
frappe.get_doc({
|
||||
'doctype': 'Lead Source',
|
||||
'source_name' : 'Product Inquiry'
|
||||
}).insert(ignore_permissions=True)
|
||||
lead_doc.set('source', 'Product Inquiry')
|
||||
|
||||
try:
|
||||
lead_doc.save(ignore_permissions=True)
|
||||
except frappe.exceptions.DuplicateEntryError:
|
||||
|
@ -8,6 +8,8 @@ import unittest
|
||||
|
||||
from erpnext.stock.doctype.batch.batch import get_batch_qty, UnableToSelectBatchError, get_batch_no
|
||||
from frappe.utils import cint, flt
|
||||
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
|
||||
from erpnext.stock.get_item_details import get_item_details
|
||||
|
||||
class TestBatch(unittest.TestCase):
|
||||
def test_item_has_batch_enabled(self):
|
||||
@ -182,7 +184,7 @@ class TestBatch(unittest.TestCase):
|
||||
stock_entry.cancel()
|
||||
current_batch_qty = flt(frappe.db.get_value("Batch", "B100", "batch_qty"))
|
||||
self.assertEqual(current_batch_qty, existing_batch_qty)
|
||||
|
||||
|
||||
@classmethod
|
||||
def make_new_batch_and_entry(cls, item_name, batch_name, warehouse):
|
||||
'''Make a new stock entry for given target warehouse and batch name of item'''
|
||||
@ -252,6 +254,72 @@ class TestBatch(unittest.TestCase):
|
||||
|
||||
return batch
|
||||
|
||||
def test_batch_wise_item_price(self):
|
||||
if not frappe.db.get_value('Item', '_Test Batch Price Item'):
|
||||
frappe.get_doc({
|
||||
'doctype': 'Item',
|
||||
'is_stock_item': 1,
|
||||
'item_code': '_Test Batch Price Item',
|
||||
'item_group': 'Products',
|
||||
'has_batch_no': 1,
|
||||
'create_new_batch': 1
|
||||
}).insert(ignore_permissions=True)
|
||||
|
||||
batch1 = create_batch('_Test Batch Price Item', 200, 1)
|
||||
batch2 = create_batch('_Test Batch Price Item', 300, 1)
|
||||
batch3 = create_batch('_Test Batch Price Item', 400, 0)
|
||||
|
||||
args = frappe._dict({
|
||||
"item_code": "_Test Batch Price Item",
|
||||
"company": "_Test Company with perpetual inventory",
|
||||
"price_list": "_Test Price List",
|
||||
"currency": "_Test Currency",
|
||||
"doctype": "Sales Invoice",
|
||||
"conversion_rate": 1,
|
||||
"price_list_currency": "_Test Currency",
|
||||
"plc_conversion_rate": 1,
|
||||
"customer": "_Test Customer",
|
||||
"name": None
|
||||
})
|
||||
|
||||
#test price for batch1
|
||||
args.update({'batch_no': batch1})
|
||||
details = get_item_details(args)
|
||||
self.assertEqual(details.get('price_list_rate'), 200)
|
||||
|
||||
#test price for batch2
|
||||
args.update({'batch_no': batch2})
|
||||
details = get_item_details(args)
|
||||
self.assertEqual(details.get('price_list_rate'), 300)
|
||||
|
||||
#test price for batch3
|
||||
args.update({'batch_no': batch3})
|
||||
details = get_item_details(args)
|
||||
self.assertEqual(details.get('price_list_rate'), 400)
|
||||
|
||||
def create_batch(item_code, rate, create_item_price_for_batch):
|
||||
pi = make_purchase_invoice(company="_Test Company with perpetual inventory",
|
||||
warehouse= "Stores - TCP1", cost_center = "Main - TCP1", update_stock=1,
|
||||
expense_account ="_Test Account Cost for Goods Sold - TCP1", item_code=item_code)
|
||||
|
||||
batch = frappe.db.get_value('Batch', {'item': item_code, 'reference_name': pi.name})
|
||||
|
||||
if not create_item_price_for_batch:
|
||||
create_price_list_for_batch(item_code, None, rate)
|
||||
else:
|
||||
create_price_list_for_batch(item_code, batch, rate)
|
||||
|
||||
return batch
|
||||
|
||||
def create_price_list_for_batch(item_code, batch, rate):
|
||||
frappe.get_doc({
|
||||
'doctype': 'Item Price',
|
||||
'item_code': '_Test Batch Price Item',
|
||||
'price_list': '_Test Price List',
|
||||
'batch_no': batch,
|
||||
'price_list_rate': rate
|
||||
}).insert()
|
||||
|
||||
def make_new_batch(**args):
|
||||
args = frappe._dict(args)
|
||||
|
||||
|
@ -95,13 +95,19 @@ frappe.ui.form.on("Delivery Note", {
|
||||
frm.page.set_inner_btn_group_as_primary(__('Create'));
|
||||
}
|
||||
|
||||
if (frm.doc.docstatus === 1 && frm.doc.is_internal_customer && !frm.doc.inter_company_reference) {
|
||||
frm.add_custom_button(__('Purchase Receipt'), function() {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: 'erpnext.stock.doctype.delivery_note.delivery_note.make_inter_company_purchase_receipt',
|
||||
frm: frm,
|
||||
})
|
||||
}, __('Create'));
|
||||
if (frm.doc.docstatus == 1 && !frm.doc.inter_company_reference) {
|
||||
let internal = me.frm.doc.is_internal_customer;
|
||||
if (internal) {
|
||||
let button_label = (me.frm.doc.company === me.frm.doc.represents_company) ? "Internal Purchase Receipt" :
|
||||
"Inter Company Purchase Receipt";
|
||||
|
||||
me.frm.add_custom_button(button_label, function() {
|
||||
frappe.model.open_mapped_doc({
|
||||
method: 'erpnext.stock.doctype.delivery_note.delivery_note.make_inter_company_purchase_receipt',
|
||||
frm: frm,
|
||||
});
|
||||
}, __('Create'));
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -297,15 +303,6 @@ erpnext.stock.DeliveryNoteController = erpnext.selling.SellingController.extend(
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
to_warehouse: function() {
|
||||
let packed_items_table = this.frm.doc["packed_items"];
|
||||
this.autofill_warehouse(this.frm.doc["items"], "target_warehouse", this.frm.doc.to_warehouse);
|
||||
if (packed_items_table && packed_items_table.length) {
|
||||
this.autofill_warehouse(packed_items_table, "target_warehouse", this.frm.doc.to_warehouse);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
$.extend(cur_frm.cscript, new erpnext.stock.DeliveryNoteController({frm: cur_frm}));
|
||||
|
@ -53,7 +53,7 @@
|
||||
"sec_warehouse",
|
||||
"set_warehouse",
|
||||
"col_break_warehouse",
|
||||
"to_warehouse",
|
||||
"set_target_warehouse",
|
||||
"items_section",
|
||||
"scan_barcode",
|
||||
"items",
|
||||
@ -117,6 +117,7 @@
|
||||
"source",
|
||||
"column_break5",
|
||||
"is_internal_customer",
|
||||
"represents_company",
|
||||
"inter_company_reference",
|
||||
"per_billed",
|
||||
"customer_group",
|
||||
@ -502,18 +503,6 @@
|
||||
"fieldname": "col_break_warehouse",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"description": "Required only for sample item.",
|
||||
"fieldname": "to_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"in_standard_filter": 1,
|
||||
"label": "To Warehouse",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "to_warehouse",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Warehouse",
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "items_section",
|
||||
"fieldtype": "Section Break",
|
||||
@ -1261,13 +1250,34 @@
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.is_internal_customer",
|
||||
"fieldname": "set_target_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"in_standard_filter": 1,
|
||||
"label": "Set Target Warehouse",
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "to_warehouse",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Warehouse",
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"description": "Company which internal customer represents.",
|
||||
"fetch_from": "customer.represents_company",
|
||||
"fieldname": "represents_company",
|
||||
"fieldtype": "Link",
|
||||
"label": "Represents Company",
|
||||
"options": "Company",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-truck",
|
||||
"idx": 146,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-11-30 12:54:45.407289",
|
||||
"modified": "2020-12-26 17:07:59.194403",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Delivery Note",
|
||||
|
@ -664,7 +664,8 @@ def make_inter_company_purchase_receipt(source_name, target_doc=None):
|
||||
return make_inter_company_transaction("Delivery Note", source_name, target_doc)
|
||||
|
||||
def make_inter_company_transaction(doctype, source_name, target_doc=None):
|
||||
from erpnext.accounts.doctype.sales_invoice.sales_invoice import validate_inter_company_transaction, get_inter_company_details
|
||||
from erpnext.accounts.doctype.sales_invoice.sales_invoice import (validate_inter_company_transaction,
|
||||
get_inter_company_details, update_address, update_taxes, set_purchase_references)
|
||||
|
||||
if doctype == 'Delivery Note':
|
||||
source_doc = frappe.get_doc(doctype, source_name)
|
||||
@ -682,6 +683,7 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
|
||||
|
||||
def set_missing_values(source, target):
|
||||
target.run_method("set_missing_values")
|
||||
set_purchase_references(target)
|
||||
|
||||
if target.doctype == 'Purchase Receipt':
|
||||
master_doctype = 'Purchase Taxes and Charges Template'
|
||||
@ -697,21 +699,35 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
|
||||
if target_doc.doctype == 'Purchase Receipt':
|
||||
target_doc.company = details.get("company")
|
||||
target_doc.supplier = details.get("party")
|
||||
target_doc.supplier_address = source_doc.company_address
|
||||
target_doc.shipping_address = source_doc.shipping_address_name or source_doc.customer_address
|
||||
target_doc.buying_price_list = source_doc.selling_price_list
|
||||
target_doc.is_internal_supplier = 1
|
||||
target_doc.inter_company_reference = source_doc.name
|
||||
|
||||
# Invert the address on target doc creation
|
||||
update_address(target_doc, 'supplier_address', 'address_display', source_doc.company_address)
|
||||
update_address(target_doc, 'shipping_address', 'shipping_address_display', source_doc.customer_address)
|
||||
|
||||
update_taxes(target_doc, party=target_doc.supplier, party_type='Supplier', company=target_doc.company,
|
||||
doctype=target_doc.doctype, party_address=target_doc.supplier_address,
|
||||
company_address=target_doc.shipping_address)
|
||||
else:
|
||||
target_doc.company = details.get("company")
|
||||
target_doc.customer = details.get("party")
|
||||
target_doc.company_address = source_doc.supplier_address
|
||||
target_doc.shipping_address_name = source_doc.shipping_address
|
||||
target_doc.selling_price_list = source_doc.buying_price_list
|
||||
target_doc.is_internal_customer = 1
|
||||
target_doc.inter_company_reference = source_doc.name
|
||||
|
||||
doclist = get_mapped_doc(doctype, source_name, {
|
||||
# Invert the address on target doc creation
|
||||
update_address(target_doc, 'company_address', 'company_address_display', source_doc.supplier_address)
|
||||
update_address(target_doc, 'shipping_address_name', 'shipping_address', source_doc.shipping_address)
|
||||
update_address(target_doc, 'customer_address', 'address_display', source_doc.shipping_address)
|
||||
|
||||
update_taxes(target_doc, party=target_doc.customer, party_type='Customer', company=target_doc.company,
|
||||
doctype=target_doc.doctype, party_address=target_doc.customer_address,
|
||||
company_address=target_doc.company_address, shipping_address_name=target_doc.shipping_address_name)
|
||||
|
||||
doclist = get_mapped_doc(doctype, source_name, {
|
||||
doctype: {
|
||||
"doctype": target_doctype,
|
||||
"postprocess": update_details,
|
||||
@ -722,7 +738,10 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
|
||||
doctype +" Item": {
|
||||
"doctype": target_doctype + " Item",
|
||||
"field_map": {
|
||||
source_document_warehouse_field: target_document_warehouse_field
|
||||
source_document_warehouse_field: target_document_warehouse_field,
|
||||
'name': 'delivery_note_item',
|
||||
'batch_no': 'batch_no',
|
||||
'serial_no': 'serial_no'
|
||||
},
|
||||
"field_no_map": [
|
||||
"warehouse"
|
||||
|
@ -458,7 +458,7 @@
|
||||
"fieldname": "warehouse",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "From Warehouse",
|
||||
"label": "Warehouse",
|
||||
"oldfieldname": "warehouse",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "Warehouse",
|
||||
@ -467,11 +467,12 @@
|
||||
"width": "100px"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:parent.is_internal_customer",
|
||||
"fieldname": "target_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 1,
|
||||
"label": "Customer Warehouse (Optional)",
|
||||
"label": "Target Warehouse",
|
||||
"no_copy": 1,
|
||||
"options": "Warehouse",
|
||||
"print_hide": 1
|
||||
@ -748,7 +749,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-12-07 19:59:27.119856",
|
||||
"modified": "2020-12-26 17:31:27.029803",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Delivery Note Item",
|
||||
|
@ -106,9 +106,9 @@
|
||||
"item_tax_section_break",
|
||||
"taxes",
|
||||
"inspection_criteria",
|
||||
"quality_inspection_template",
|
||||
"inspection_required_before_purchase",
|
||||
"inspection_required_before_delivery",
|
||||
"quality_inspection_template",
|
||||
"manufacturing",
|
||||
"default_bom",
|
||||
"is_sub_contracted_item",
|
||||
@ -814,7 +814,6 @@
|
||||
"label": "Inspection Required before Delivery"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:(doc.inspection_required_before_purchase || doc.inspection_required_before_delivery)",
|
||||
"fieldname": "quality_inspection_template",
|
||||
"fieldtype": "Link",
|
||||
"label": "Quality Inspection Template",
|
||||
@ -1069,7 +1068,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"max_attachments": 1,
|
||||
"modified": "2020-08-07 14:24:58.384992",
|
||||
"modified": "2021-01-25 20:49:50.222976",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Item",
|
||||
@ -1131,4 +1130,4 @@
|
||||
"sort_order": "DESC",
|
||||
"title_field": "item_name",
|
||||
"track_changes": 1
|
||||
}
|
||||
}
|
@ -672,13 +672,14 @@ class Item(WebsiteGenerator):
|
||||
if not records: return
|
||||
document = _("Stock Reconciliation") if len(records) == 1 else _("Stock Reconciliations")
|
||||
|
||||
msg = _("The items {0} and {1} are present in the following {2} : <br>"
|
||||
.format(frappe.bold(old_name), frappe.bold(new_name), document))
|
||||
msg = _("The items {0} and {1} are present in the following {2} : ").format(
|
||||
frappe.bold(old_name), frappe.bold(new_name), document)
|
||||
|
||||
msg += '<br>'
|
||||
msg += ', '.join([get_link_to_form("Stock Reconciliation", d.parent) for d in records]) + "<br><br>"
|
||||
|
||||
msg += _("Note: To merge the items, create a separate Stock Reconciliation for the old item {0}"
|
||||
.format(frappe.bold(old_name)))
|
||||
msg += _("Note: To merge the items, create a separate Stock Reconciliation for the old item {0}").format(
|
||||
frappe.bold(old_name))
|
||||
|
||||
frappe.throw(_(msg), title=_("Merge not allowed"))
|
||||
|
||||
@ -971,7 +972,7 @@ class Item(WebsiteGenerator):
|
||||
frappe.throw(_("As there are existing transactions against item {0}, you can not change the value of {1}").format(self.name, frappe.bold(self.meta.get_label(field))))
|
||||
|
||||
def check_if_linked_document_exists(self, field):
|
||||
linked_doctypes = ["Delivery Note Item", "Sales Invoice Item", "Purchase Receipt Item",
|
||||
linked_doctypes = ["Delivery Note Item", "Sales Invoice Item", "POS Invoice Item", "Purchase Receipt Item",
|
||||
"Purchase Invoice Item", "Stock Entry Detail", "Stock Reconciliation Item"]
|
||||
|
||||
# For "Is Stock Item", following doctypes is important
|
||||
|
@ -15,5 +15,13 @@ frappe.ui.form.on("Item Price", {
|
||||
|
||||
frm.set_df_property("bulk_import_help", "options",
|
||||
'<a href="#data-import-tool/Item Price">' + __("Import in Bulk") + '</a>');
|
||||
|
||||
frm.set_query('batch_no', function() {
|
||||
return {
|
||||
filters: {
|
||||
'item': frm.doc.item_code
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
@ -18,6 +18,7 @@
|
||||
"price_list",
|
||||
"customer",
|
||||
"supplier",
|
||||
"batch_no",
|
||||
"column_break_3",
|
||||
"buying",
|
||||
"selling",
|
||||
@ -47,31 +48,41 @@
|
||||
"oldfieldtype": "Select",
|
||||
"options": "Item",
|
||||
"reqd": 1,
|
||||
"search_index": 1
|
||||
"search_index": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "uom",
|
||||
"fieldtype": "Link",
|
||||
"label": "UOM",
|
||||
"options": "UOM"
|
||||
"options": "UOM",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"description": "Quantity that must be bought or sold per UOM",
|
||||
"fieldname": "packing_unit",
|
||||
"fieldtype": "Int",
|
||||
"label": "Packing Unit"
|
||||
"label": "Packing Unit",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_17",
|
||||
"fieldtype": "Column Break"
|
||||
"fieldtype": "Column Break",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "item_name",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Item Name",
|
||||
"read_only": 1
|
||||
"read_only": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fetch_from": "item_code.brand",
|
||||
@ -79,19 +90,25 @@
|
||||
"fieldtype": "Read Only",
|
||||
"in_list_view": 1,
|
||||
"label": "Brand",
|
||||
"read_only": 1
|
||||
"read_only": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "item_description",
|
||||
"fieldtype": "Text",
|
||||
"label": "Item Description",
|
||||
"read_only": 1
|
||||
"read_only": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "price_list_details",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Price List",
|
||||
"options": "fa fa-tags"
|
||||
"options": "fa fa-tags",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "price_list",
|
||||
@ -100,7 +117,9 @@
|
||||
"in_standard_filter": 1,
|
||||
"label": "Price List",
|
||||
"options": "Price List",
|
||||
"reqd": 1
|
||||
"reqd": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"bold": 1,
|
||||
@ -108,37 +127,49 @@
|
||||
"fieldname": "customer",
|
||||
"fieldtype": "Link",
|
||||
"label": "Customer",
|
||||
"options": "Customer"
|
||||
"options": "Customer",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:doc.buying == 1",
|
||||
"fieldname": "supplier",
|
||||
"fieldtype": "Link",
|
||||
"label": "Supplier",
|
||||
"options": "Supplier"
|
||||
"options": "Supplier",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_3",
|
||||
"fieldtype": "Column Break"
|
||||
"fieldtype": "Column Break",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "buying",
|
||||
"fieldtype": "Check",
|
||||
"label": "Buying",
|
||||
"read_only": 1
|
||||
"read_only": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "selling",
|
||||
"fieldtype": "Check",
|
||||
"label": "Selling",
|
||||
"read_only": 1
|
||||
"read_only": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "item_details",
|
||||
"fieldtype": "Section Break",
|
||||
"options": "fa fa-tag"
|
||||
"options": "fa fa-tag",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"bold": 1,
|
||||
@ -146,11 +177,15 @@
|
||||
"fieldtype": "Link",
|
||||
"label": "Currency",
|
||||
"options": "Currency",
|
||||
"read_only": 1
|
||||
"read_only": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "col_br_1",
|
||||
"fieldtype": "Column Break"
|
||||
"fieldtype": "Column Break",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "price_list_rate",
|
||||
@ -162,53 +197,80 @@
|
||||
"oldfieldname": "ref_rate",
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "currency",
|
||||
"reqd": 1
|
||||
"reqd": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_15",
|
||||
"fieldtype": "Section Break"
|
||||
"fieldtype": "Section Break",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"default": "Today",
|
||||
"fieldname": "valid_from",
|
||||
"fieldtype": "Date",
|
||||
"label": "Valid From"
|
||||
"label": "Valid From",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "lead_time_days",
|
||||
"fieldtype": "Int",
|
||||
"label": "Lead Time in days"
|
||||
"label": "Lead Time in days",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_18",
|
||||
"fieldtype": "Column Break"
|
||||
"fieldtype": "Column Break",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "valid_upto",
|
||||
"fieldtype": "Date",
|
||||
"label": "Valid Upto"
|
||||
"label": "Valid Upto",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_24",
|
||||
"fieldtype": "Section Break"
|
||||
"fieldtype": "Section Break",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "note",
|
||||
"fieldtype": "Text",
|
||||
"label": "Note"
|
||||
"label": "Note",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "reference",
|
||||
"fieldtype": "Data",
|
||||
"in_list_view": 1,
|
||||
"label": "Reference"
|
||||
"label": "Reference",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "batch_no",
|
||||
"fieldtype": "Link",
|
||||
"label": "Batch No",
|
||||
"options": "Batch",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-flag",
|
||||
"idx": 1,
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2020-07-06 22:31:32.943475",
|
||||
"modified": "2020-12-08 18:12:15.395772",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Item Price",
|
||||
|
@ -54,7 +54,8 @@ class ItemPrice(Document):
|
||||
"valid_upto",
|
||||
"packing_unit",
|
||||
"customer",
|
||||
"supplier",]:
|
||||
"supplier",
|
||||
"batch_no"]:
|
||||
if self.get(field):
|
||||
conditions += " and {0} = %({0})s ".format(field)
|
||||
else:
|
||||
@ -68,7 +69,7 @@ class ItemPrice(Document):
|
||||
self.as_dict(),)
|
||||
|
||||
if price_list_rate:
|
||||
frappe.throw(_("Item Price appears multiple times based on Price List, Supplier/Customer, Currency, Item, UOM, Qty, and Dates."), ItemPriceDuplicateItem,)
|
||||
frappe.throw(_("Item Price appears multiple times based on Price List, Supplier/Customer, Currency, Item, Batch, UOM, Qty, and Dates."), ItemPriceDuplicateItem,)
|
||||
|
||||
def before_save(self):
|
||||
if self.selling:
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"actions": [],
|
||||
"creation": "2013-02-22 01:28:02",
|
||||
"doctype": "DocType",
|
||||
"document_type": "Document",
|
||||
@ -29,6 +30,8 @@
|
||||
"options": "Item",
|
||||
"read_only": 1,
|
||||
"reqd": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1,
|
||||
"width": "100px"
|
||||
},
|
||||
{
|
||||
@ -41,6 +44,8 @@
|
||||
"print_width": "300px",
|
||||
"read_only": 1,
|
||||
"reqd": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1,
|
||||
"width": "120px"
|
||||
},
|
||||
{
|
||||
@ -50,7 +55,9 @@
|
||||
"no_copy": 1,
|
||||
"options": "Purchase Invoice\nPurchase Receipt",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
"read_only": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "receipt_document",
|
||||
@ -59,25 +66,33 @@
|
||||
"no_copy": 1,
|
||||
"options": "receipt_document_type",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
"read_only": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "col_break2",
|
||||
"fieldtype": "Column Break"
|
||||
"fieldtype": "Column Break",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "qty",
|
||||
"fieldtype": "Float",
|
||||
"in_list_view": 1,
|
||||
"label": "Qty",
|
||||
"read_only": 1
|
||||
"read_only": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "rate",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Rate",
|
||||
"options": "Company:company:default_currency",
|
||||
"read_only": 1
|
||||
"read_only": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "amount",
|
||||
@ -88,14 +103,19 @@
|
||||
"oldfieldtype": "Currency",
|
||||
"options": "Company:company:default_currency",
|
||||
"read_only": 1,
|
||||
"reqd": 1
|
||||
"reqd": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "applicable_charges",
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 1,
|
||||
"label": "Applicable Charges",
|
||||
"options": "Company:company:default_currency"
|
||||
"options": "Company:company:default_currency",
|
||||
"read_only_depends_on": "eval:parent.distribute_charges_based_on != 'Distribute Manually'",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "purchase_receipt_item",
|
||||
@ -104,22 +124,30 @@
|
||||
"label": "Purchase Receipt Item",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
"read_only": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "cost_center",
|
||||
"fieldtype": "Link",
|
||||
"label": "Cost Center",
|
||||
"options": "Cost Center"
|
||||
"options": "Cost Center",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "accounting_dimensions_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Accounting Dimensions"
|
||||
"label": "Accounting Dimensions",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "dimension_col_break",
|
||||
"fieldtype": "Column Break"
|
||||
"fieldtype": "Column Break",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
@ -128,12 +156,15 @@
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"label": "Is Fixed Asset",
|
||||
"read_only": 1
|
||||
"read_only": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"modified": "2020-09-18 17:26:09.703215",
|
||||
"links": [],
|
||||
"modified": "2021-01-25 23:09:23.322282",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Landed Cost Item",
|
||||
|
@ -6,8 +6,11 @@
|
||||
"engine": "InnoDB",
|
||||
"field_order": [
|
||||
"expense_account",
|
||||
"account_currency",
|
||||
"exchange_rate",
|
||||
"description",
|
||||
"col_break3",
|
||||
"base_amount",
|
||||
"amount"
|
||||
],
|
||||
"fields": [
|
||||
@ -28,7 +31,7 @@
|
||||
"fieldtype": "Currency",
|
||||
"in_list_view": 1,
|
||||
"label": "Amount",
|
||||
"options": "Company:company:default_currency",
|
||||
"options": "account_currency",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
@ -38,13 +41,33 @@
|
||||
"in_list_view": 1,
|
||||
"label": "Expense Account",
|
||||
"mandatory_depends_on": "eval:cint(erpnext.is_perpetual_inventory_enabled(parent.company))",
|
||||
"options": "Account",
|
||||
"print_hide": 1
|
||||
"options": "Account"
|
||||
},
|
||||
{
|
||||
"fieldname": "account_currency",
|
||||
"fieldtype": "Link",
|
||||
"label": "Account Currency",
|
||||
"options": "Currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "exchange_rate",
|
||||
"fieldtype": "Float",
|
||||
"label": "Exchange Rate",
|
||||
"precision": "9"
|
||||
},
|
||||
{
|
||||
"fieldname": "base_amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Base Amount",
|
||||
"options": "Company:company:default_currency",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-12-04 00:22:14.373312",
|
||||
"modified": "2020-12-26 01:07:23.233604",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Landed Cost Taxes and Charges",
|
||||
|
@ -1,6 +1,7 @@
|
||||
// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
|
||||
// License: GNU General Public License v3. See license.txt
|
||||
|
||||
{% include 'erpnext/stock/landed_taxes_and_charges_common.js' %};
|
||||
|
||||
frappe.provide("erpnext.stock");
|
||||
|
||||
@ -29,20 +30,9 @@ erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({
|
||||
this.frm.add_fetch("receipt_document", "supplier", "supplier");
|
||||
this.frm.add_fetch("receipt_document", "posting_date", "posting_date");
|
||||
this.frm.add_fetch("receipt_document", "base_grand_total", "grand_total");
|
||||
|
||||
this.frm.set_query("expense_account", "taxes", function() {
|
||||
return {
|
||||
query: "erpnext.controllers.queries.tax_account_query",
|
||||
filters: {
|
||||
"account_type": ["Tax", "Chargeable", "Income Account", "Expenses Included In Valuation", "Expenses Included In Asset Valuation"],
|
||||
"company": me.frm.doc.company
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
refresh: function(frm) {
|
||||
refresh: function() {
|
||||
var help_content =
|
||||
`<br><br>
|
||||
<table class="table table-bordered" style="background-color: #f9f9f9;">
|
||||
@ -72,6 +62,11 @@ erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({
|
||||
</table>`;
|
||||
|
||||
set_field_options("landed_cost_help", help_content);
|
||||
|
||||
if (this.frm.doc.company) {
|
||||
let company_currency = frappe.get_doc(":Company", this.frm.doc.company).default_currency;
|
||||
this.frm.set_currency_labels(["total_taxes_and_charges"], company_currency);
|
||||
}
|
||||
},
|
||||
|
||||
get_items_from_purchase_receipts: function() {
|
||||
@ -97,34 +92,36 @@ erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({
|
||||
set_total_taxes_and_charges: function() {
|
||||
var total_taxes_and_charges = 0.0;
|
||||
$.each(this.frm.doc.taxes || [], function(i, d) {
|
||||
total_taxes_and_charges += flt(d.amount)
|
||||
total_taxes_and_charges += flt(d.base_amount);
|
||||
});
|
||||
cur_frm.set_value("total_taxes_and_charges", total_taxes_and_charges);
|
||||
this.frm.set_value("total_taxes_and_charges", total_taxes_and_charges);
|
||||
},
|
||||
|
||||
set_applicable_charges_for_item: function() {
|
||||
var me = this;
|
||||
|
||||
if(this.frm.doc.taxes.length) {
|
||||
|
||||
var total_item_cost = 0.0;
|
||||
var based_on = this.frm.doc.distribute_charges_based_on.toLowerCase();
|
||||
$.each(this.frm.doc.items || [], function(i, d) {
|
||||
total_item_cost += flt(d[based_on])
|
||||
});
|
||||
|
||||
var total_charges = 0.0;
|
||||
$.each(this.frm.doc.items || [], function(i, item) {
|
||||
item.applicable_charges = flt(item[based_on]) * flt(me.frm.doc.total_taxes_and_charges) / flt(total_item_cost)
|
||||
item.applicable_charges = flt(item.applicable_charges, precision("applicable_charges", item))
|
||||
total_charges += item.applicable_charges
|
||||
});
|
||||
if (based_on != 'distribute manually') {
|
||||
$.each(this.frm.doc.items || [], function(i, d) {
|
||||
total_item_cost += flt(d[based_on])
|
||||
});
|
||||
|
||||
if (total_charges != this.frm.doc.total_taxes_and_charges){
|
||||
var diff = this.frm.doc.total_taxes_and_charges - flt(total_charges)
|
||||
this.frm.doc.items.slice(-1)[0].applicable_charges += diff
|
||||
var total_charges = 0.0;
|
||||
$.each(this.frm.doc.items || [], function(i, item) {
|
||||
item.applicable_charges = flt(item[based_on]) * flt(me.frm.doc.total_taxes_and_charges) / flt(total_item_cost)
|
||||
item.applicable_charges = flt(item.applicable_charges, precision("applicable_charges", item))
|
||||
total_charges += item.applicable_charges
|
||||
});
|
||||
|
||||
if (total_charges != this.frm.doc.total_taxes_and_charges){
|
||||
var diff = this.frm.doc.total_taxes_and_charges - flt(total_charges)
|
||||
this.frm.doc.items.slice(-1)[0].applicable_charges += diff
|
||||
}
|
||||
refresh_field("items");
|
||||
}
|
||||
refresh_field("items");
|
||||
}
|
||||
},
|
||||
distribute_charges_based_on: function (frm) {
|
||||
@ -134,7 +131,16 @@ erpnext.stock.LandedCostVoucher = erpnext.stock.StockController.extend({
|
||||
items_remove: () => {
|
||||
this.trigger('set_applicable_charges_for_item');
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
cur_frm.script_manager.make(erpnext.stock.LandedCostVoucher);
|
||||
|
||||
frappe.ui.form.on('Landed Cost Taxes and Charges', {
|
||||
expense_account: function(frm, cdt, cdn) {
|
||||
frm.events.set_account_currency(frm, cdt, cdn);
|
||||
},
|
||||
|
||||
amount: function(frm, cdt, cdn) {
|
||||
frm.events.set_base_amount(frm, cdt, cdn);
|
||||
}
|
||||
});
|
||||
|
@ -1,4 +1,5 @@
|
||||
{
|
||||
"actions": [],
|
||||
"autoname": "naming_series:",
|
||||
"creation": "2014-07-11 11:33:42.547339",
|
||||
"doctype": "DocType",
|
||||
@ -7,6 +8,9 @@
|
||||
"field_order": [
|
||||
"naming_series",
|
||||
"company",
|
||||
"column_break_2",
|
||||
"posting_date",
|
||||
"section_break_5",
|
||||
"purchase_receipts",
|
||||
"purchase_receipt_items",
|
||||
"get_items_from_purchase_receipts",
|
||||
@ -30,7 +34,9 @@
|
||||
"options": "MAT-LCV-.YYYY.-",
|
||||
"print_hide": 1,
|
||||
"reqd": 1,
|
||||
"set_only_once": 1
|
||||
"set_only_once": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "company",
|
||||
@ -40,24 +46,32 @@
|
||||
"label": "Company",
|
||||
"options": "Company",
|
||||
"remember_last_selected_value": 1,
|
||||
"reqd": 1
|
||||
"reqd": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "purchase_receipts",
|
||||
"fieldtype": "Table",
|
||||
"label": "Purchase Receipts",
|
||||
"options": "Landed Cost Purchase Receipt",
|
||||
"reqd": 1
|
||||
"reqd": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "purchase_receipt_items",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Purchase Receipt Items"
|
||||
"label": "Purchase Receipt Items",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "get_items_from_purchase_receipts",
|
||||
"fieldtype": "Button",
|
||||
"label": "Get Items From Purchase Receipts"
|
||||
"label": "Get Items From Purchase Receipts",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "items",
|
||||
@ -65,42 +79,56 @@
|
||||
"label": "Purchase Receipt Items",
|
||||
"no_copy": 1,
|
||||
"options": "Landed Cost Item",
|
||||
"reqd": 1
|
||||
"reqd": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "sec_break1",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Applicable Charges"
|
||||
"label": "Applicable Charges",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "taxes",
|
||||
"fieldtype": "Table",
|
||||
"label": "Taxes and Charges",
|
||||
"options": "Landed Cost Taxes and Charges",
|
||||
"reqd": 1
|
||||
"reqd": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_9",
|
||||
"fieldtype": "Section Break"
|
||||
"fieldtype": "Section Break",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "total_taxes_and_charges",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Total Taxes and Charges",
|
||||
"label": "Total Taxes and Charges (Company Currency)",
|
||||
"options": "Company:company:default_currency",
|
||||
"read_only": 1,
|
||||
"reqd": 1
|
||||
"reqd": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "col_break1",
|
||||
"fieldtype": "Column Break"
|
||||
"fieldtype": "Column Break",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "distribute_charges_based_on",
|
||||
"fieldtype": "Select",
|
||||
"label": "Distribute Charges Based On",
|
||||
"options": "Qty\nAmount",
|
||||
"reqd": 1
|
||||
"options": "Qty\nAmount\nDistribute Manually",
|
||||
"reqd": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "amended_from",
|
||||
@ -109,21 +137,51 @@
|
||||
"no_copy": 1,
|
||||
"options": "Landed Cost Voucher",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
"read_only": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "sec_break2",
|
||||
"fieldtype": "Section Break"
|
||||
"fieldtype": "Section Break",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "landed_cost_help",
|
||||
"fieldtype": "HTML",
|
||||
"label": "Landed Cost Help"
|
||||
"label": "Landed Cost Help",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_2",
|
||||
"fieldtype": "Column Break",
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"default": "Today",
|
||||
"fieldname": "posting_date",
|
||||
"fieldtype": "Date",
|
||||
"label": "Posting Date",
|
||||
"reqd": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_5",
|
||||
"fieldtype": "Section Break",
|
||||
"hide_border": 1,
|
||||
"show_days": 1,
|
||||
"show_seconds": 1
|
||||
}
|
||||
],
|
||||
"icon": "icon-usd",
|
||||
"index_web_pages_for_search": 1,
|
||||
"is_submittable": 1,
|
||||
"modified": "2019-11-21 15:34:10.846093",
|
||||
"links": [],
|
||||
"modified": "2021-01-25 23:07:30.468423",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Landed Cost Voucher",
|
||||
|
@ -9,6 +9,7 @@ from frappe.model.meta import get_field_precision
|
||||
from frappe.model.document import Document
|
||||
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
|
||||
from erpnext.accounts.doctype.account.account import get_account_currency
|
||||
from erpnext.controllers.taxes_and_totals import init_landed_taxes_and_totals
|
||||
|
||||
class LandedCostVoucher(Document):
|
||||
def get_items_from_purchase_receipts(self):
|
||||
@ -39,13 +40,15 @@ class LandedCostVoucher(Document):
|
||||
|
||||
def validate(self):
|
||||
self.check_mandatory()
|
||||
self.validate_purchase_receipts()
|
||||
init_landed_taxes_and_totals(self)
|
||||
self.set_total_taxes_and_charges()
|
||||
if not self.get("items"):
|
||||
self.get_items_from_purchase_receipts()
|
||||
else:
|
||||
self.validate_applicable_charges_for_item()
|
||||
self.validate_purchase_receipts()
|
||||
self.validate_expense_accounts()
|
||||
self.set_total_taxes_and_charges()
|
||||
|
||||
self.set_applicable_charges_on_item()
|
||||
self.validate_applicable_charges_for_item()
|
||||
|
||||
|
||||
def check_mandatory(self):
|
||||
if not self.get("purchase_receipts"):
|
||||
@ -73,21 +76,37 @@ class LandedCostVoucher(Document):
|
||||
frappe.throw(_("Row {0}: Cost center is required for an item {1}")
|
||||
.format(item.idx, item.item_code))
|
||||
|
||||
def validate_expense_accounts(self):
|
||||
company_currency = erpnext.get_company_currency(self.company)
|
||||
for account in self.taxes:
|
||||
if get_account_currency(account.expense_account) != company_currency:
|
||||
frappe.throw(_("Row {}: Expense account currency should be same as company's default currency.").format(account.idx)
|
||||
+ _("Please select expense account with account currency as {}.").format(frappe.bold(company_currency)),
|
||||
title=_("Invalid Account Currency"))
|
||||
|
||||
def set_total_taxes_and_charges(self):
|
||||
self.total_taxes_and_charges = sum([flt(d.amount) for d in self.get("taxes")])
|
||||
self.total_taxes_and_charges = sum([flt(d.base_amount) for d in self.get("taxes")])
|
||||
|
||||
def set_applicable_charges_on_item(self):
|
||||
if self.get('taxes') and self.distribute_charges_based_on != 'Distribute Manually':
|
||||
total_item_cost = 0.0
|
||||
total_charges = 0.0
|
||||
item_count = 0
|
||||
based_on_field = frappe.scrub(self.distribute_charges_based_on)
|
||||
|
||||
for item in self.get('items'):
|
||||
total_item_cost += item.get(based_on_field)
|
||||
|
||||
for item in self.get('items'):
|
||||
item.applicable_charges = flt(flt(item.get(based_on_field)) * (flt(self.total_taxes_and_charges) / flt(total_item_cost)),
|
||||
item.precision('applicable_charges'))
|
||||
total_charges += item.applicable_charges
|
||||
item_count += 1
|
||||
|
||||
if total_charges != self.total_taxes_and_charges:
|
||||
diff = self.total_taxes_and_charges - total_charges
|
||||
self.get('items')[item_count - 1].applicable_charges += diff
|
||||
|
||||
def validate_applicable_charges_for_item(self):
|
||||
based_on = self.distribute_charges_based_on.lower()
|
||||
|
||||
total = sum([flt(d.get(based_on)) for d in self.get("items")])
|
||||
if based_on != 'distribute manually':
|
||||
total = sum([flt(d.get(based_on)) for d in self.get("items")])
|
||||
else:
|
||||
# consider for proportion while distributing manually
|
||||
total = sum([flt(d.get('applicable_charges')) for d in self.get("items")])
|
||||
|
||||
if not total:
|
||||
frappe.throw(_("Total {0} for all items is zero, may be you should change 'Distribute Charges Based On'").format(based_on))
|
||||
@ -153,13 +172,13 @@ class LandedCostVoucher(Document):
|
||||
docs = frappe.db.get_all('Asset', filters={ receipt_document_type: item.receipt_document,
|
||||
'item_code': item.item_code }, fields=['name', 'docstatus'])
|
||||
if not docs or len(docs) != item.qty:
|
||||
frappe.throw(_('There are not enough asset created or linked to {0}.').format(item.receipt_document)
|
||||
+ _('Please create or link {0} Assets with respective document.').format(item.qty))
|
||||
frappe.throw(_('There are not enough asset created or linked to {0}. Please create or link {1} Assets with respective document.').format(
|
||||
item.receipt_document, item.qty))
|
||||
if docs:
|
||||
for d in docs:
|
||||
if d.docstatus == 1:
|
||||
frappe.throw(_('{0} {1} has submitted Assets. Remove Item {2} from table to continue.')
|
||||
.format(item.receipt_document_type, frappe.bold(item.receipt_document), frappe.bold(item.item_code)))
|
||||
frappe.throw(_('{2} <b>{0}</b> has submitted Assets. Remove Item <b>{1}</b> from table to continue.').format(
|
||||
item.receipt_document, item.item_code, item.receipt_document_type))
|
||||
|
||||
def update_rate_in_serial_no_for_non_asset_items(self, receipt_document):
|
||||
for item in receipt_document.get("items"):
|
||||
|
@ -10,6 +10,7 @@ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt \
|
||||
import get_gl_entries, test_records as pr_test_records, make_purchase_receipt
|
||||
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
|
||||
from erpnext.accounts.doctype.account.test_account import get_inventory_account
|
||||
from erpnext.accounts.doctype.account.test_account import create_account
|
||||
|
||||
class TestLandedCostVoucher(unittest.TestCase):
|
||||
def test_landed_cost_voucher(self):
|
||||
@ -162,8 +163,8 @@ class TestLandedCostVoucher(unittest.TestCase):
|
||||
|
||||
lcv = create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company, 123.22)
|
||||
|
||||
self.assertEqual(lcv.items[0].applicable_charges, 41.07)
|
||||
self.assertEqual(lcv.items[2].applicable_charges, 41.08)
|
||||
self.assertEqual(flt(lcv.items[0].applicable_charges, 2), 41.07)
|
||||
self.assertEqual(flt(lcv.items[2].applicable_charges, 2), 41.08)
|
||||
|
||||
def test_multiple_landed_cost_voucher_against_pr(self):
|
||||
pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1",
|
||||
@ -206,6 +207,46 @@ class TestLandedCostVoucher(unittest.TestCase):
|
||||
self.assertEqual(pr.items[0].landed_cost_voucher_amount, 100)
|
||||
self.assertEqual(pr.items[1].landed_cost_voucher_amount, 100)
|
||||
|
||||
def test_multi_currency_lcv(self):
|
||||
## Create USD Shipping charges_account
|
||||
usd_shipping = create_account(account_name="Shipping Charges USD",
|
||||
parent_account="Duties and Taxes - TCP1", company="_Test Company with perpetual inventory",
|
||||
account_currency="USD")
|
||||
|
||||
pr = make_purchase_receipt(company="_Test Company with perpetual inventory", warehouse = "Stores - TCP1",
|
||||
supplier_warehouse = "Stores - TCP1")
|
||||
pr.submit()
|
||||
|
||||
lcv = make_landed_cost_voucher(company = pr.company, receipt_document_type = "Purchase Receipt",
|
||||
receipt_document=pr.name, charges=100, do_not_save=True)
|
||||
|
||||
lcv.append("taxes", {
|
||||
"description": "Shipping Charges",
|
||||
"expense_account": usd_shipping,
|
||||
"amount": 10
|
||||
})
|
||||
|
||||
lcv.save()
|
||||
lcv.submit()
|
||||
pr.load_from_db()
|
||||
|
||||
# Considering exchange rate from USD to INR as 62.9
|
||||
self.assertEqual(lcv.total_taxes_and_charges, 729)
|
||||
self.assertEqual(pr.items[0].landed_cost_voucher_amount, 729)
|
||||
|
||||
gl_entries = frappe.get_all("GL Entry", fields=["account", "credit", "credit_in_account_currency"],
|
||||
filters={"voucher_no": pr.name, "account": ("in", ["Shipping Charges USD - TCP1", "Expenses Included In Valuation - TCP1"])})
|
||||
|
||||
expected_gl_entries = {
|
||||
"Shipping Charges USD - TCP1": [629, 10],
|
||||
"Expenses Included In Valuation - TCP1": [100, 100]
|
||||
}
|
||||
|
||||
for entry in gl_entries:
|
||||
amounts = expected_gl_entries.get(entry.account)
|
||||
self.assertEqual(entry.credit, amounts[0])
|
||||
self.assertEqual(entry.credit_in_account_currency, amounts[1])
|
||||
|
||||
def make_landed_cost_voucher(** args):
|
||||
args = frappe._dict(args)
|
||||
ref_doc = frappe.get_doc(args.receipt_document_type, args.receipt_document)
|
||||
|
@ -1,9 +1,10 @@
|
||||
frappe.listview_settings['Material Request'] = {
|
||||
add_fields: ["material_request_type", "status", "per_ordered", "per_received", "transfer_status"],
|
||||
get_indicator: function(doc) {
|
||||
if(doc.status=="Stopped") {
|
||||
var precision = frappe.defaults.get_default("float_precision");
|
||||
if (doc.status=="Stopped") {
|
||||
return [__("Stopped"), "red", "status,=,Stopped"];
|
||||
} else if(doc.transfer_status && doc.docstatus != 2) {
|
||||
} else if (doc.transfer_status && doc.docstatus != 2) {
|
||||
if (doc.transfer_status == "Not Started") {
|
||||
return [__("Not Started"), "orange"];
|
||||
} else if (doc.transfer_status == "In Transit") {
|
||||
@ -11,14 +12,14 @@ frappe.listview_settings['Material Request'] = {
|
||||
} else if (doc.transfer_status == "Completed") {
|
||||
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"];
|
||||
} 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"];
|
||||
} else if(doc.docstatus==1 && flt(doc.per_ordered, 2) == 100) {
|
||||
if (doc.material_request_type == "Purchase" && flt(doc.per_received, 2) < 100 && flt(doc.per_received, 2) > 0) {
|
||||
} else if (doc.docstatus==1 && flt(doc.per_ordered, precision) == 100) {
|
||||
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"];
|
||||
} 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"];
|
||||
} else if (doc.material_request_type == "Purchase") {
|
||||
return [__("Ordered"), "green", "per_ordered,=,100"];
|
||||
|
@ -48,6 +48,7 @@
|
||||
"set_warehouse",
|
||||
"rejected_warehouse",
|
||||
"col_break_warehouse",
|
||||
"set_from_warehouse",
|
||||
"is_subcontracted",
|
||||
"supplier_warehouse",
|
||||
"items_section",
|
||||
@ -115,6 +116,7 @@
|
||||
"per_returned",
|
||||
"is_internal_supplier",
|
||||
"inter_company_reference",
|
||||
"represents_company",
|
||||
"subscription_detail",
|
||||
"auto_repeat",
|
||||
"printing_settings",
|
||||
@ -1087,7 +1089,9 @@
|
||||
"fieldname": "inter_company_reference",
|
||||
"fieldtype": "Link",
|
||||
"label": "Inter Company Reference",
|
||||
"no_copy": 1,
|
||||
"options": "Delivery Note",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
@ -1121,13 +1125,29 @@
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval: doc.is_internal_supplier",
|
||||
"description": "Sets 'From Warehouse' in each row of the items table.",
|
||||
"fieldname": "set_from_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"label": "Set From Warehouse",
|
||||
"options": "Warehouse"
|
||||
},
|
||||
{
|
||||
"fetch_from": "supplier.represents_company",
|
||||
"fieldname": "represents_company",
|
||||
"fieldtype": "Link",
|
||||
"label": "Represents Company",
|
||||
"options": "Company",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-truck",
|
||||
"idx": 261,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-12-08 18:31:32.234503",
|
||||
"modified": "2020-12-26 20:49:39.106049",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Purchase Receipt",
|
||||
|
@ -288,12 +288,15 @@ class PurchaseReceipt(BuyingController):
|
||||
# Amount added through landed-cost-voucher
|
||||
if d.landed_cost_voucher_amount and landed_cost_entries:
|
||||
for account, amount in iteritems(landed_cost_entries[(d.item_code, d.name)]):
|
||||
account_currency = get_account_currency(account)
|
||||
gl_entries.append(self.get_gl_dict({
|
||||
"account": account,
|
||||
"account_currency": account_currency,
|
||||
"against": warehouse_account[d.warehouse]["account"],
|
||||
"cost_center": d.cost_center,
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||
"credit": flt(amount),
|
||||
"credit": flt(amount["base_amount"]),
|
||||
"credit_in_account_currency": flt(amount["amount"]),
|
||||
"project": d.project
|
||||
}, item=d))
|
||||
|
||||
@ -728,7 +731,13 @@ def get_item_account_wise_additional_cost(purchase_document):
|
||||
|
||||
for lcv in landed_cost_vouchers:
|
||||
landed_cost_voucher_doc = frappe.get_doc("Landed Cost Voucher", lcv.parent)
|
||||
based_on_field = frappe.scrub(landed_cost_voucher_doc.distribute_charges_based_on)
|
||||
|
||||
#Use amount field for total item cost for manually cost distributed LCVs
|
||||
if landed_cost_voucher_doc.distribute_charges_based_on == 'Distribute Manually':
|
||||
based_on_field = 'amount'
|
||||
else:
|
||||
based_on_field = frappe.scrub(landed_cost_voucher_doc.distribute_charges_based_on)
|
||||
|
||||
total_item_cost = 0
|
||||
|
||||
for item in landed_cost_voucher_doc.items:
|
||||
@ -738,9 +747,16 @@ def get_item_account_wise_additional_cost(purchase_document):
|
||||
if item.receipt_document == purchase_document:
|
||||
for account in landed_cost_voucher_doc.taxes:
|
||||
item_account_wise_cost.setdefault((item.item_code, item.purchase_receipt_item), {})
|
||||
item_account_wise_cost[(item.item_code, item.purchase_receipt_item)].setdefault(account.expense_account, 0.0)
|
||||
item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][account.expense_account] += \
|
||||
item_account_wise_cost[(item.item_code, item.purchase_receipt_item)].setdefault(account.expense_account, {
|
||||
"amount": 0.0,
|
||||
"base_amount": 0.0
|
||||
})
|
||||
|
||||
item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][account.expense_account]["amount"] += \
|
||||
account.amount * item.get(based_on_field) / total_item_cost
|
||||
|
||||
item_account_wise_cost[(item.item_code, item.purchase_receipt_item)][account.expense_account]["base_amount"] += \
|
||||
account.base_amount * item.get(based_on_field) / total_item_cost
|
||||
|
||||
return item_account_wise_cost
|
||||
|
||||
|
@ -76,6 +76,7 @@
|
||||
"purchase_order_item",
|
||||
"material_request_item",
|
||||
"purchase_receipt_item",
|
||||
"delivery_note_item",
|
||||
"putaway_rule",
|
||||
"section_break_45",
|
||||
"allow_zero_valuation_rate",
|
||||
@ -819,11 +820,12 @@
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:parent.is_internal_supplier",
|
||||
"fieldname": "from_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"hidden": 1,
|
||||
"ignore_user_permissions": 1,
|
||||
"label": "Supplier Warehouse",
|
||||
"label": "From Warehouse",
|
||||
"options": "Warehouse"
|
||||
},
|
||||
{
|
||||
@ -871,12 +873,20 @@
|
||||
"fieldtype": "Float",
|
||||
"label": "Received Qty in Stock UOM",
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "delivery_note_item",
|
||||
"fieldtype": "Data",
|
||||
"label": "Delivery Note Item",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2020-12-09 10:00:38.204294",
|
||||
"modified": "2020-12-26 16:50:56.479347",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Purchase Receipt Item",
|
||||
|
@ -3,8 +3,18 @@
|
||||
frappe.provide("erpnext.stock");
|
||||
frappe.provide("erpnext.accounts.dimensions");
|
||||
|
||||
{% include 'erpnext/stock/landed_taxes_and_charges_common.js' %};
|
||||
|
||||
frappe.ui.form.on('Stock Entry', {
|
||||
setup: function(frm) {
|
||||
frm.set_indicator_formatter('item_code', function(doc) {
|
||||
if (!doc.s_warehouse) {
|
||||
return 'blue';
|
||||
} else {
|
||||
return (doc.qty<=doc.actual_qty) ? 'green' : 'orange';
|
||||
}
|
||||
});
|
||||
|
||||
frm.set_query('work_order', function() {
|
||||
return {
|
||||
filters: [
|
||||
@ -87,15 +97,6 @@ frappe.ui.form.on('Stock Entry', {
|
||||
}
|
||||
});
|
||||
|
||||
frm.set_query("expense_account", "additional_costs", function() {
|
||||
return {
|
||||
query: "erpnext.controllers.queries.tax_account_query",
|
||||
filters: {
|
||||
"account_type": ["Tax", "Chargeable", "Income Account", "Expenses Included In Valuation", "Expenses Included In Asset Valuation"],
|
||||
"company": frm.doc.company
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
frm.add_fetch("bom_no", "inspection_required", "inspection_required");
|
||||
erpnext.accounts.dimensions.setup_dimension_filters(frm, frm.doctype);
|
||||
@ -551,7 +552,7 @@ frappe.ui.form.on('Stock Entry', {
|
||||
|
||||
calculate_total_additional_costs: function(frm) {
|
||||
const total_additional_costs = frappe.utils.sum(
|
||||
(frm.doc.additional_costs || []).map(function(c) { return flt(c.amount); })
|
||||
(frm.doc.additional_costs || []).map(function(c) { return flt(c.base_amount); })
|
||||
);
|
||||
|
||||
frm.set_value("total_additional_costs",
|
||||
@ -730,8 +731,18 @@ var validate_sample_quantity = function(frm, cdt, cdn) {
|
||||
};
|
||||
|
||||
frappe.ui.form.on('Landed Cost Taxes and Charges', {
|
||||
amount: function(frm) {
|
||||
frm.events.calculate_amount(frm);
|
||||
amount: function(frm, cdt, cdn) {
|
||||
frm.events.set_base_amount(frm, cdt, cdn);
|
||||
|
||||
// Adding this check because same table in used in LCV
|
||||
// This causes an error if you try to post an LCV immediately after a Stock Entry
|
||||
if (frm.doc.doctype == 'Stock Entry') {
|
||||
frm.events.calculate_amount(frm);
|
||||
}
|
||||
},
|
||||
|
||||
expense_account: function(frm, cdt, cdn) {
|
||||
frm.events.set_account_currency(frm, cdt, cdn);
|
||||
}
|
||||
});
|
||||
|
||||
@ -779,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");
|
||||
|
||||
frappe.dynamic_link = { doc: this.frm.doc, fieldname: 'supplier', doctype: 'Supplier' }
|
||||
|
@ -19,6 +19,7 @@ from frappe.model.mapper import get_mapped_doc
|
||||
from erpnext.stock.doctype.serial_no.serial_no import update_serial_nos_after_submit, get_serial_nos
|
||||
from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import OpeningEntryAccountError
|
||||
from erpnext.accounts.general_ledger import process_gl_map
|
||||
from erpnext.controllers.taxes_and_totals import init_landed_taxes_and_totals
|
||||
import json
|
||||
|
||||
from six import string_types, itervalues, iteritems
|
||||
@ -195,7 +196,7 @@ class StockEntry(StockController):
|
||||
and (sed.t_warehouse is null or sed.t_warehouse = '')""", self.project, as_list=1)
|
||||
|
||||
amount = amount[0][0] if amount else 0
|
||||
additional_costs = frappe.db.sql(""" select ifnull(sum(sed.amount), 0)
|
||||
additional_costs = frappe.db.sql(""" select ifnull(sum(sed.base_amount), 0)
|
||||
from
|
||||
`tabStock Entry` se, `tabLanded Cost Taxes and Charges` sed
|
||||
where
|
||||
@ -445,6 +446,7 @@ class StockEntry(StockController):
|
||||
|
||||
def calculate_rate_and_amount(self, reset_outgoing_rate=True, raise_error_if_no_rate=True):
|
||||
self.set_basic_rate(reset_outgoing_rate, raise_error_if_no_rate)
|
||||
init_landed_taxes_and_totals(self)
|
||||
self.distribute_additional_costs()
|
||||
self.update_valuation_rate()
|
||||
self.set_total_incoming_outgoing_value()
|
||||
@ -533,7 +535,7 @@ class StockEntry(StockController):
|
||||
if not any([d.item_code for d in self.items if d.t_warehouse]):
|
||||
self.additional_costs = []
|
||||
|
||||
self.total_additional_costs = sum([flt(t.amount) for t in self.get("additional_costs")])
|
||||
self.total_additional_costs = sum([flt(t.base_amount) for t in self.get("additional_costs")])
|
||||
|
||||
if self.purpose in ("Repack", "Manufacture"):
|
||||
incoming_items_cost = sum([flt(t.basic_amount) for t in self.get("items") if t.is_finished_item])
|
||||
@ -773,13 +775,19 @@ class StockEntry(StockController):
|
||||
for d in self.get("items"):
|
||||
if d.t_warehouse:
|
||||
item_account_wise_additional_cost.setdefault((d.item_code, d.name), {})
|
||||
item_account_wise_additional_cost[(d.item_code, d.name)].setdefault(t.expense_account, 0.0)
|
||||
item_account_wise_additional_cost[(d.item_code, d.name)].setdefault(t.expense_account, {
|
||||
"amount": 0.0,
|
||||
"base_amount": 0.0
|
||||
})
|
||||
|
||||
multiply_based_on = d.basic_amount if total_basic_amount else d.qty
|
||||
|
||||
item_account_wise_additional_cost[(d.item_code, d.name)][t.expense_account] += \
|
||||
item_account_wise_additional_cost[(d.item_code, d.name)][t.expense_account]["amount"] += \
|
||||
flt(t.amount * multiply_based_on) / divide_based_on
|
||||
|
||||
item_account_wise_additional_cost[(d.item_code, d.name)][t.expense_account]["base_amount"] += \
|
||||
flt(t.base_amount * multiply_based_on) / divide_based_on
|
||||
|
||||
if item_account_wise_additional_cost:
|
||||
for d in self.get("items"):
|
||||
for account, amount in iteritems(item_account_wise_additional_cost.get((d.item_code, d.name), {})):
|
||||
@ -790,7 +798,8 @@ class StockEntry(StockController):
|
||||
"against": d.expense_account,
|
||||
"cost_center": d.cost_center,
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||
"credit": amount
|
||||
"credit_in_account_currency": flt(amount["amount"]),
|
||||
"credit": flt(amount["base_amount"])
|
||||
}, item=d))
|
||||
|
||||
gl_entries.append(self.get_gl_dict({
|
||||
@ -798,7 +807,7 @@ class StockEntry(StockController):
|
||||
"against": account,
|
||||
"cost_center": d.cost_center,
|
||||
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
|
||||
"credit": -1 * amount # put it as negative credit instead of debit purposefully
|
||||
"credit": -1 * amount['base_amount'] # put it as negative credit instead of debit purposefully
|
||||
}, item=d))
|
||||
|
||||
return process_gl_map(gl_entries)
|
||||
|
@ -16,6 +16,7 @@
|
||||
"action_if_quality_inspection_is_not_submitted",
|
||||
"show_barcode_field",
|
||||
"clean_description_html",
|
||||
"disable_serial_no_and_batch_selector",
|
||||
"section_break_7",
|
||||
"auto_insert_price_list_rate_if_missing",
|
||||
"allow_negative_stock",
|
||||
@ -227,6 +228,12 @@
|
||||
"fieldname": "control_historical_stock_transactions_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Control Historical Stock Transactions"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "disable_serial_no_and_batch_selector",
|
||||
"fieldtype": "Check",
|
||||
"label": "Disable Serial No And Batch Selector"
|
||||
}
|
||||
],
|
||||
"icon": "icon-cog",
|
||||
@ -234,7 +241,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2020-12-29 12:53:31.162247",
|
||||
"modified": "2021-01-18 13:15:38.352796",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Stock Settings",
|
||||
|
@ -19,7 +19,7 @@ from erpnext.stock.doctype.item_manufacturer.item_manufacturer import get_item_m
|
||||
|
||||
from six import string_types, iteritems
|
||||
|
||||
sales_doctypes = ['Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice']
|
||||
sales_doctypes = ['Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice', 'POS Invoice']
|
||||
purchase_doctypes = ['Material Request', 'Supplier Quotation', 'Purchase Order', 'Purchase Receipt', 'Purchase Invoice']
|
||||
|
||||
@frappe.whitelist()
|
||||
@ -674,6 +674,8 @@ def get_item_price(args, item_code, ignore_party=False):
|
||||
and price_list=%(price_list)s
|
||||
and ifnull(uom, '') in ('', %(uom)s)"""
|
||||
|
||||
conditions += "and ifnull(batch_no, '') in ('', %(batch_no)s)"
|
||||
|
||||
if not ignore_party:
|
||||
if args.get("customer"):
|
||||
conditions += " and customer=%(customer)s"
|
||||
@ -692,7 +694,7 @@ def get_item_price(args, item_code, ignore_party=False):
|
||||
|
||||
return frappe.db.sql(""" select name, price_list_rate, uom
|
||||
from `tabItem Price` {conditions}
|
||||
order by valid_from desc, uom desc """.format(conditions=conditions), args)
|
||||
order by valid_from desc, batch_no desc, uom desc """.format(conditions=conditions), args)
|
||||
|
||||
def get_price_list_rate_for(args, item_code):
|
||||
"""
|
||||
@ -711,6 +713,7 @@ def get_price_list_rate_for(args, item_code):
|
||||
"uom": args.get('uom'),
|
||||
"transaction_date": args.get('transaction_date'),
|
||||
"posting_date": args.get('posting_date'),
|
||||
"batch_no": args.get('batch_no')
|
||||
}
|
||||
|
||||
item_price_data = 0
|
||||
|
62
erpnext/stock/landed_taxes_and_charges_common.js
Normal file
62
erpnext/stock/landed_taxes_and_charges_common.js
Normal file
@ -0,0 +1,62 @@
|
||||
let document_list = ['Landed Cost Voucher', 'Stock Entry'];
|
||||
|
||||
document_list.forEach((doctype) => {
|
||||
frappe.ui.form.on(doctype, {
|
||||
refresh: function(frm) {
|
||||
let tax_field = frm.doc.doctype == 'Landed Cost Voucher' ? 'taxes' : 'additional_costs';
|
||||
frm.set_query("expense_account", tax_field, function() {
|
||||
return {
|
||||
filters: {
|
||||
"account_type": ['in', ["Tax", "Chargeable", "Income Account", "Expenses Included In Valuation", "Expenses Included In Asset Valuation"]],
|
||||
"company": frm.doc.company
|
||||
}
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
set_account_currency: function(frm, cdt, cdn) {
|
||||
let row = locals[cdt][cdn];
|
||||
if (row.expense_account) {
|
||||
frappe.db.get_value('Account', row.expense_account, 'account_currency', function(value) {
|
||||
frappe.model.set_value(cdt, cdn, "account_currency", value.account_currency);
|
||||
frm.events.set_exchange_rate(frm, cdt, cdn);
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
set_exchange_rate: function(frm, cdt, cdn) {
|
||||
let row = locals[cdt][cdn];
|
||||
let company_currency = frappe.get_doc(":Company", frm.doc.company).default_currency;
|
||||
|
||||
if (row.account_currency == company_currency) {
|
||||
row.exchange_rate = 1;
|
||||
frm.set_df_property('taxes', 'hidden', 1, row.name, 'exchange_rate');
|
||||
} else if (!row.exchange_rate || row.exchange_rate == 1) {
|
||||
frm.set_df_property('taxes', 'hidden', 0, row.name, 'exchange_rate');
|
||||
frappe.call({
|
||||
method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_exchange_rate",
|
||||
args: {
|
||||
posting_date: frm.doc.posting_date,
|
||||
account: row.expense_account,
|
||||
account_currency: row.account_currency,
|
||||
company: frm.doc.company
|
||||
},
|
||||
callback: function(r) {
|
||||
if (r.message) {
|
||||
frappe.model.set_value(cdt, cdn, "exchange_rate", r.message);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
frm.refresh_field('taxes');
|
||||
},
|
||||
|
||||
set_base_amount: function(frm, cdt, cdn) {
|
||||
let row = locals[cdt][cdn];
|
||||
frappe.model.set_value(cdt, cdn, "base_amount",
|
||||
flt(flt(row.amount)*row.exchange_rate, precision("base_amount", row)));
|
||||
}
|
||||
});
|
||||
});
|
||||
|
@ -41,7 +41,7 @@ def make_sl_entries(sl_entries, allow_negative_stock=False, via_landed_cost_vouc
|
||||
|
||||
if sle.get("actual_qty") or sle.get("voucher_type")=="Stock Reconciliation":
|
||||
sle_doc = make_entry(sle, allow_negative_stock, via_landed_cost_voucher)
|
||||
|
||||
|
||||
args = sle_doc.as_dict()
|
||||
update_bin(args, allow_negative_stock, via_landed_cost_voucher)
|
||||
|
||||
@ -65,7 +65,7 @@ def make_entry(args, allow_negative_stock=False, via_landed_cost_voucher=False):
|
||||
def repost_future_sle(args=None, voucher_type=None, voucher_no=None, allow_negative_stock=False, via_landed_cost_voucher=False):
|
||||
if not args and voucher_type and voucher_no:
|
||||
args = get_args_for_voucher(voucher_type, voucher_no)
|
||||
|
||||
|
||||
distinct_item_warehouses = [(d.item_code, d.warehouse) for d in args]
|
||||
|
||||
i = 0
|
||||
@ -80,7 +80,7 @@ def repost_future_sle(args=None, voucher_type=None, voucher_no=None, allow_negat
|
||||
for item_wh, new_sle in iteritems(obj.new_items):
|
||||
if item_wh not in distinct_item_warehouses:
|
||||
args.append(new_sle)
|
||||
|
||||
|
||||
i += 1
|
||||
|
||||
def get_args_for_voucher(voucher_type, voucher_no):
|
||||
@ -127,7 +127,7 @@ class update_entries_after(object):
|
||||
self.initialize_previous_data(self.args)
|
||||
|
||||
self.build()
|
||||
|
||||
|
||||
def get_precision(self):
|
||||
company_base_currency = frappe.get_cached_value('Company', self.company, "default_currency")
|
||||
self.precision = get_field_precision(frappe.get_meta("Stock Ledger Entry").get_field("stock_value"),
|
||||
@ -213,13 +213,13 @@ class update_entries_after(object):
|
||||
# includes current entry!
|
||||
args = self.data[self.args.warehouse].previous_sle \
|
||||
or frappe._dict({"item_code": self.item_code, "warehouse": self.args.warehouse})
|
||||
|
||||
|
||||
return list(self.get_sle_after_datetime(args))
|
||||
|
||||
def get_dependent_entries_to_fix(self, entries_to_fix, sle):
|
||||
dependant_sle = get_sle_by_voucher_detail_no(sle.dependant_sle_voucher_detail_no,
|
||||
excluded_sle=sle.name)
|
||||
|
||||
|
||||
if not dependant_sle:
|
||||
return
|
||||
elif dependant_sle.item_code == self.item_code and dependant_sle.warehouse == self.args.warehouse:
|
||||
@ -251,7 +251,7 @@ class update_entries_after(object):
|
||||
|
||||
# Get dynamic incoming/outgoing rate
|
||||
self.get_dynamic_incoming_outgoing_rate(sle)
|
||||
|
||||
|
||||
if sle.serial_no:
|
||||
self.get_serialized_values(sle)
|
||||
self.wh_data.qty_after_transaction += flt(sle.actual_qty)
|
||||
@ -329,7 +329,7 @@ class update_entries_after(object):
|
||||
rate = get_rate_for_return(sle.voucher_type, sle.voucher_no, sle.item_code, voucher_detail_no=sle.voucher_detail_no)
|
||||
else:
|
||||
if sle.voucher_type in ("Purchase Receipt", "Purchase Invoice"):
|
||||
rate_field = "valuation_rate"
|
||||
rate_field = "valuation_rate"
|
||||
else:
|
||||
rate_field = "incoming_rate"
|
||||
|
||||
@ -344,7 +344,7 @@ class update_entries_after(object):
|
||||
ref_doctype = "Packed Item"
|
||||
else:
|
||||
ref_doctype = "Purchase Receipt Item Supplied"
|
||||
|
||||
|
||||
rate = frappe.db.get_value(ref_doctype, {"parent_detail_docname": sle.voucher_detail_no,
|
||||
"item_code": sle.item_code}, rate_field)
|
||||
|
||||
@ -374,7 +374,7 @@ class update_entries_after(object):
|
||||
stock_entry.db_update()
|
||||
for d in stock_entry.items:
|
||||
d.db_update()
|
||||
|
||||
|
||||
def update_rate_on_delivery_and_sales_return(self, sle, outgoing_rate):
|
||||
# Update item's incoming rate on transaction
|
||||
item_code = frappe.db.get_value(sle.voucher_type + " Item", sle.voucher_detail_no, "item_code")
|
||||
@ -487,7 +487,6 @@ class update_entries_after(object):
|
||||
self.wh_data.valuation_rate = new_stock_value / new_stock_qty
|
||||
else:
|
||||
self.wh_data.valuation_rate = sle.outgoing_rate
|
||||
|
||||
else:
|
||||
if flt(self.wh_data.qty_after_transaction) >= 0 and sle.outgoing_rate:
|
||||
self.wh_data.valuation_rate = sle.outgoing_rate
|
||||
@ -631,7 +630,7 @@ class update_entries_after(object):
|
||||
frappe.throw(message, NegativeStockError, title='Insufficient Stock')
|
||||
else:
|
||||
raise NegativeStockError(message)
|
||||
|
||||
|
||||
def update_bin(self):
|
||||
# update bin for each warehouse
|
||||
for warehouse, data in iteritems(self.data):
|
||||
@ -766,7 +765,7 @@ def update_qty_in_future_sle(args, allow_negative_stock=None):
|
||||
frappe.db.sql("""
|
||||
update `tabStock Ledger Entry`
|
||||
set qty_after_transaction = qty_after_transaction + {qty}
|
||||
where
|
||||
where
|
||||
item_code = %(item_code)s
|
||||
and warehouse = %(warehouse)s
|
||||
and voucher_no != %(voucher_no)s
|
||||
@ -794,7 +793,7 @@ def validate_negative_qty_in_future_sle(args, allow_negative_stock=None):
|
||||
frappe.get_desk_link('Warehouse', args.warehouse),
|
||||
sle[0]["posting_date"], sle[0]["posting_time"],
|
||||
frappe.get_desk_link(sle[0]["voucher_type"], sle[0]["voucher_no"]))
|
||||
|
||||
|
||||
frappe.throw(message, NegativeStockError, title='Insufficient Stock')
|
||||
|
||||
def get_future_sle_with_negative_qty(args):
|
||||
@ -803,7 +802,7 @@ def get_future_sle_with_negative_qty(args):
|
||||
qty_after_transaction, posting_date, posting_time,
|
||||
voucher_type, voucher_no
|
||||
from `tabStock Ledger Entry`
|
||||
where
|
||||
where
|
||||
item_code = %(item_code)s
|
||||
and warehouse = %(warehouse)s
|
||||
and voucher_no != %(voucher_no)s
|
||||
|
@ -40,7 +40,7 @@
|
||||
<div class="col-md-{{ section.column_value }} mb-4">
|
||||
<div class="card h-100 justify-content-between">
|
||||
{% 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 %}
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">{{ card.title }}</h5>
|
||||
|
@ -47,6 +47,9 @@
|
||||
|
||||
{% if doc.items %}
|
||||
<div class="place-order-container">
|
||||
<a class="btn btn-primary-light mr-2" href="/all-products">
|
||||
{{ _("Continue Shopping") }}
|
||||
</a>
|
||||
{% if cart_settings.enable_checkout %}
|
||||
<button class="btn btn-primary btn-place-order" type="button">
|
||||
{{ _("Place Order") }}
|
||||
|
Loading…
x
Reference in New Issue
Block a user