diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 2490f3b42e..ee37c8d780 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -5,7 +5,7 @@ import frappe from erpnext.hooks import regional_overrides from frappe.utils import getdate -__version__ = '12.0.8' +__version__ = '12.1.0' def get_default_company(user=None): '''Get default company for user''' diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py index 0e57b3f198..1adc4c4d2f 100644 --- a/erpnext/accounts/doctype/account/account.py +++ b/erpnext/accounts/doctype/account/account.py @@ -128,7 +128,8 @@ class Account(NestedSet): "account_currency": self.account_currency, "parent_account": parent_acc_name_map[company] }) - doc.save() + if not self.check_if_child_acc_exists(doc): + doc.save() frappe.msgprint(_("Account {0} is added in the child company {1}") .format(doc.name, company)) @@ -172,6 +173,24 @@ class Account(NestedSet): if frappe.db.get_value("GL Entry", {"account": self.name}): frappe.throw(_("Currency can not be changed after making entries using some other currency")) + def check_if_child_acc_exists(self, doc): + ''' Checks if a account in parent company exists in the ''' + info = frappe.db.get_value("Account", { + "account_name": doc.account_name, + "account_number": doc.account_number + }, ['company', 'account_currency', 'is_group', 'root_type', 'account_type', 'balance_must_be', 'account_name'], as_dict=1) + + if not info: + return + + doc = vars(doc) + dict_diff = [k for k in info if k in doc and info[k] != doc[k] and k != "company"] + if dict_diff: + frappe.throw(_("Account {0} already exists in child company {1}. The following fields have different values, they should be same:") + .format(info.account_name, info.company, '
  • '.join(dict_diff))) + else: + return True + def convert_group_to_ledger(self): if self.check_if_child_exists(): throw(_("Account with child nodes cannot be converted to ledger")) diff --git a/erpnext/accounts/doctype/budget/test_budget.py b/erpnext/accounts/doctype/budget/test_budget.py index b126b1fb0a..33aefd67d1 100644 --- a/erpnext/accounts/doctype/budget/test_budget.py +++ b/erpnext/accounts/doctype/budget/test_budget.py @@ -11,32 +11,32 @@ from erpnext.buying.doctype.purchase_order.test_purchase_order import create_pur from erpnext.accounts.doctype.budget.budget import get_actual_expense, BudgetError from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry -class TestBudget(unittest.TestCase): +class TestBudget(unittest.TestCase): def test_monthly_budget_crossed_ignore(self): set_total_expense_zero("2013-02-28", "Cost Center") budget = make_budget(budget_against="Cost Center") - + jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC", "_Test Bank - _TC", 40000, "_Test Cost Center - _TC", posting_date="2013-02-28", submit=True) self.assertTrue(frappe.db.get_value("GL Entry", {"voucher_type": "Journal Entry", "voucher_no": jv.name})) - + budget.cancel() def test_monthly_budget_crossed_stop1(self): set_total_expense_zero("2013-02-28", "Cost Center") budget = make_budget(budget_against="Cost Center") - + frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop") jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC", "_Test Bank - _TC", 40000, "_Test Cost Center - _TC", posting_date="2013-02-28") self.assertRaises(BudgetError, jv.submit) - + budget.load_from_db() budget.cancel() @@ -46,7 +46,7 @@ class TestBudget(unittest.TestCase): budget = make_budget(budget_against="Cost Center") frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop") - + jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC", "_Test Bank - _TC", 40000, "_Test Cost Center - _TC", posting_date="2013-03-02") @@ -117,14 +117,14 @@ class TestBudget(unittest.TestCase): set_total_expense_zero("2013-02-28", "Project") budget = make_budget(budget_against="Project") - + frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop") jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC", "_Test Bank - _TC", 40000, "_Test Cost Center - _TC", project="_Test Project", posting_date="2013-02-28") self.assertRaises(BudgetError, jv.submit) - + budget.load_from_db() budget.cancel() @@ -132,31 +132,31 @@ class TestBudget(unittest.TestCase): set_total_expense_zero("2013-02-28", "Cost Center") budget = make_budget(budget_against="Cost Center") - + jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC", "_Test Bank - _TC", 150000, "_Test Cost Center - _TC", posting_date="2013-03-28") self.assertRaises(BudgetError, jv.submit) - + budget.cancel() def test_yearly_budget_crossed_stop2(self): set_total_expense_zero("2013-02-28", "Project") budget = make_budget(budget_against="Project") - + jv = make_journal_entry("_Test Account Cost for Goods Sold - _TC", "_Test Bank - _TC", 150000, "_Test Cost Center - _TC", project="_Test Project", posting_date="2013-03-28") self.assertRaises(BudgetError, jv.submit) - + budget.cancel() def test_monthly_budget_on_cancellation1(self): set_total_expense_zero("2013-02-28", "Cost Center") budget = make_budget(budget_against="Cost Center") - + jv1 = make_journal_entry("_Test Account Cost for Goods Sold - _TC", "_Test Bank - _TC", 20000, "_Test Cost Center - _TC", posting_date="2013-02-28", submit=True) @@ -170,9 +170,9 @@ class TestBudget(unittest.TestCase): {"voucher_type": "Journal Entry", "voucher_no": jv2.name})) frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop") - + self.assertRaises(BudgetError, jv1.cancel) - + budget.load_from_db() budget.cancel() @@ -180,7 +180,7 @@ class TestBudget(unittest.TestCase): set_total_expense_zero("2013-02-28", "Project") budget = make_budget(budget_against="Project") - + jv1 = make_journal_entry("_Test Account Cost for Goods Sold - _TC", "_Test Bank - _TC", 20000, "_Test Cost Center - _TC", posting_date="2013-02-28", submit=True, project="_Test Project") @@ -194,16 +194,16 @@ class TestBudget(unittest.TestCase): {"voucher_type": "Journal Entry", "voucher_no": jv2.name})) frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop") - + self.assertRaises(BudgetError, jv1.cancel) - + budget.load_from_db() budget.cancel() def test_monthly_budget_against_group_cost_center(self): set_total_expense_zero("2013-02-28", "Cost Center") set_total_expense_zero("2013-02-28", "Cost Center", "_Test Cost Center 2 - _TC") - + budget = make_budget(budget_against="Cost Center", cost_center="_Test Company - _TC") frappe.db.set_value("Budget", budget.name, "action_if_accumulated_monthly_budget_exceeded", "Stop") @@ -211,7 +211,7 @@ class TestBudget(unittest.TestCase): "_Test Bank - _TC", 40000, "_Test Cost Center 2 - _TC", posting_date="2013-02-28") self.assertRaises(BudgetError, jv.submit) - + budget.load_from_db() budget.cancel() @@ -239,8 +239,6 @@ class TestBudget(unittest.TestCase): budget.cancel() jv.cancel() - frappe.delete_doc('Journal Entry', jv.name) - frappe.delete_doc('Cost Center', cost_center) def set_total_expense_zero(posting_date, budget_against_field=None, budget_against_CC=None): if budget_against_field == "Project": @@ -256,7 +254,7 @@ def set_total_expense_zero(posting_date, budget_against_field=None, budget_again "budget_against_field": budget_against_field, "budget_against": budget_against })) - + if existing_expense: if budget_against_field == "Cost Center": make_journal_entry("_Test Account Cost for Goods Sold - _TC", @@ -281,13 +279,13 @@ def make_budget(**args): frappe.db.sql("delete from `tabBudget Account` where parent = %(name)s", d) budget = frappe.new_doc("Budget") - + if budget_against == "Project": budget.project = "_Test Project" else: budget.cost_center =cost_center or "_Test Cost Center - _TC" - - + + budget.fiscal_year = "_Test Fiscal Year 2013" budget.monthly_distribution = "_Test Distribution" budget.company = "_Test Company" @@ -299,7 +297,7 @@ def make_budget(**args): "account": "_Test Account Cost for Goods Sold - _TC", "budget_amount": 100000 }) - + if args.applicable_on_material_request: budget.applicable_on_material_request = 1 budget.action_if_annual_budget_exceeded_on_mr = args.action_if_annual_budget_exceeded_on_mr or 'Warn' diff --git a/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json b/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json index 8d7ed74eb9..04d6303774 100644 --- a/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json +++ b/erpnext/accounts/doctype/discounted_invoice/discounted_invoice.json @@ -8,7 +8,8 @@ "customer", "column_break_3", "posting_date", - "outstanding_amount" + "outstanding_amount", + "debit_to" ], "fields": [ { @@ -48,10 +49,18 @@ { "fieldname": "column_break_3", "fieldtype": "Column Break" + }, + { + "fetch_from": "sales_invoice.debit_to", + "fieldname": "debit_to", + "fieldtype": "Link", + "label": "Debit to", + "options": "Account", + "read_only": 1 } ], "istable": 1, - "modified": "2019-05-30 19:27:29.436153", + "modified": "2019-08-07 15:13:55.808349", "modified_by": "Administrator", "module": "Accounts", "name": "Discounted Invoice", diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js index 5563f031bb..f1f88a8d64 100644 --- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js +++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.js @@ -13,41 +13,57 @@ frappe.ui.form.on('Invoice Discounting', { }; }); - frm.events.filter_accounts("bank_account", frm, {"account_type": "Bank"}); - frm.events.filter_accounts("bank_charges_account", frm, {"root_type": "Expense"}); - frm.events.filter_accounts("short_term_loan", frm, {"root_type": "Liability"}); - frm.events.filter_accounts("accounts_receivable_credit", frm, {"account_type": "Receivable"}); - frm.events.filter_accounts("accounts_receivable_discounted", frm, {"account_type": "Receivable"}); - frm.events.filter_accounts("accounts_receivable_unpaid", frm, {"account_type": "Receivable"}); + + frm.events.filter_accounts("bank_account", frm, [["account_type", "=", "Bank"]]); + frm.events.filter_accounts("bank_charges_account", frm, [["root_type", "=", "Expense"]]); + frm.events.filter_accounts("short_term_loan", frm, [["root_type", "=", "Liability"]]); + frm.events.filter_accounts("accounts_receivable_discounted", frm, [["account_type", "=", "Receivable"]]); + frm.events.filter_accounts("accounts_receivable_credit", frm, [["account_type", "=", "Receivable"]]); + frm.events.filter_accounts("accounts_receivable_unpaid", frm, [["account_type", "=", "Receivable"]]); }, filter_accounts: (fieldname, frm, addl_filters) => { - let filters = { - "company": frm.doc.company, - "is_group": 0 - }; - if(addl_filters) Object.assign(filters, addl_filters); + let filters = [ + ["company", "=", frm.doc.company], + ["is_group", "=", 0] + ]; + if(addl_filters){ + filters = $.merge(filters , addl_filters); + } frm.set_query(fieldname, () => { return { "filters": filters }; }); }, + refresh_filters: (frm) =>{ + let invoice_accounts = Object.keys(frm.doc.invoices).map(function(key) { + return frm.doc.invoices[key].debit_to; + }); + let filters = [ + ["account_type", "=", "Receivable"], + ["name", "not in", invoice_accounts] + ]; + frm.events.filter_accounts("accounts_receivable_credit", frm, filters); + frm.events.filter_accounts("accounts_receivable_discounted", frm, filters); + frm.events.filter_accounts("accounts_receivable_unpaid", frm, filters); + }, + refresh: (frm) => { frm.events.show_general_ledger(frm); - if(frm.doc.docstatus === 0) { + if (frm.doc.docstatus === 0) { frm.add_custom_button(__('Get Invoices'), function() { frm.events.get_invoices(frm); }); } - if(frm.doc.docstatus === 1 && frm.doc.status !== "Settled") { - if(frm.doc.status == "Sanctioned") { + if (frm.doc.docstatus === 1 && frm.doc.status !== "Settled") { + if (frm.doc.status == "Sanctioned") { frm.add_custom_button(__('Disburse Loan'), function() { frm.events.create_disbursement_entry(frm); }).addClass("btn-primary"); } - if(frm.doc.status == "Disbursed") { + if (frm.doc.status == "Disbursed") { frm.add_custom_button(__('Close Loan'), function() { frm.events.close_loan(frm); }).addClass("btn-primary"); @@ -64,7 +80,7 @@ frappe.ui.form.on('Invoice Discounting', { }, set_end_date: (frm) => { - if(frm.doc.loan_start_date && frm.doc.loan_period) { + if (frm.doc.loan_start_date && frm.doc.loan_period) { let end_date = frappe.datetime.add_days(frm.doc.loan_start_date, frm.doc.loan_period); frm.set_value("loan_end_date", end_date); } @@ -132,6 +148,7 @@ frappe.ui.form.on('Invoice Discounting', { frm.doc.invoices = frm.doc.invoices.filter(row => row.sales_invoice); let row = frm.add_child("invoices"); $.extend(row, v); + frm.events.refresh_filters(frm); }); refresh_field("invoices"); } @@ -190,8 +207,10 @@ frappe.ui.form.on('Invoice Discounting', { frappe.ui.form.on('Discounted Invoice', { sales_invoice: (frm) => { frm.events.calculate_total_amount(frm); + frm.events.refresh_filters(frm); }, invoices_remove: (frm) => { frm.events.calculate_total_amount(frm); + frm.events.refresh_filters(frm); } -}); \ No newline at end of file +}); diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py index 29475d5644..36c29113ea 100644 --- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py +++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py @@ -12,6 +12,7 @@ from erpnext.accounts.general_ledger import make_gl_entries class InvoiceDiscounting(AccountsController): def validate(self): self.validate_mandatory() + self.validate_invoices() self.calculate_total_amount() self.set_status() self.set_end_date() @@ -24,6 +25,15 @@ class InvoiceDiscounting(AccountsController): if self.docstatus == 1 and not (self.loan_start_date and self.loan_period): frappe.throw(_("Loan Start Date and Loan Period are mandatory to save the Invoice Discounting")) + def validate_invoices(self): + discounted_invoices = [record.sales_invoice for record in + frappe.get_all("Discounted Invoice",fields = ["sales_invoice"], filters= {"docstatus":1})] + + for record in self.invoices: + if record.sales_invoice in discounted_invoices: + frappe.throw("Row({0}): {1} is already discounted in {2}" + .format(record.idx, frappe.bold(record.sales_invoice), frappe.bold(record.parent))) + def calculate_total_amount(self): self.total_amount = sum([flt(d.outstanding_amount) for d in self.invoices]) @@ -212,7 +222,8 @@ def get_invoices(filters): name as sales_invoice, customer, posting_date, - outstanding_amount + outstanding_amount, + debit_to from `tabSales Invoice` si where docstatus = 1 diff --git a/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py b/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py index 56a0d2f39c..4a7406e0cb 100644 --- a/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py +++ b/erpnext/accounts/doctype/loyalty_program/test_loyalty_program.py @@ -49,7 +49,6 @@ class TestLoyaltyProgram(unittest.TestCase): # cancel and delete for d in [si_redeem, si_original]: d.cancel() - frappe.delete_doc('Sales Invoice', d.name) def test_loyalty_points_earned_multiple_tier(self): frappe.db.set_value("Customer", "Test Loyalty Customer", "loyalty_program", "Test Multiple Loyalty") @@ -91,7 +90,6 @@ class TestLoyaltyProgram(unittest.TestCase): # cancel and delete for d in [si_redeem, si_original]: d.cancel() - frappe.delete_doc('Sales Invoice', d.name) def test_cancel_sales_invoice(self): ''' cancelling the sales invoice should cancel the earned points''' @@ -143,7 +141,6 @@ class TestLoyaltyProgram(unittest.TestCase): d.cancel() except frappe.TimestampMismatchError: frappe.get_doc('Sales Invoice', d.name).cancel() - frappe.delete_doc('Sales Invoice', d.name) def test_loyalty_points_for_dashboard(self): doc = frappe.get_doc('Customer', 'Test Loyalty Customer') diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 6bd8d29730..536a6ed2c5 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -947,10 +947,15 @@ def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount= paid_amount = abs(outstanding_amount) if bank_amount: received_amount = bank_amount + else: + received_amount = paid_amount * doc.conversion_rate else: received_amount = abs(outstanding_amount) if bank_amount: paid_amount = bank_amount + else: + # if party account currency and bank currency is different then populate paid amount as well + paid_amount = received_amount * doc.conversion_rate pe = frappe.new_doc("Payment Entry") pe.payment_type = payment_type diff --git a/erpnext/accounts/doctype/payment_order/regional/india.js b/erpnext/accounts/doctype/payment_order/regional/india.js deleted file mode 100644 index 66d0f60c61..0000000000 --- a/erpnext/accounts/doctype/payment_order/regional/india.js +++ /dev/null @@ -1,29 +0,0 @@ -frappe.ui.form.on('Payment Order', { - refresh: function(frm) { - if (frm.doc.docstatus==1 && frm.doc.payment_order_type==='Payment Entry') { - frm.add_custom_button(__('Generate Text File'), function() { - frm.trigger("generate_text_and_download_file"); - }); - } - }, - generate_text_and_download_file: (frm) => { - return frappe.call({ - method: "erpnext.regional.india.bank_remittance.generate_report", - args: { - name: frm.doc.name - }, - freeze: true, - callback: function(r) { - { - frm.reload_doc(); - const a = document.createElement('a'); - let file_obj = r.message; - a.href = file_obj.file_url; - a.target = '_blank'; - a.download = file_obj.file_name; - a.click(); - } - } - }); - } -}); \ No newline at end of file diff --git a/erpnext/accounts/doctype/payment_request/payment_request.js b/erpnext/accounts/doctype/payment_request/payment_request.js index a455e74940..e2510f675f 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.js +++ b/erpnext/accounts/doctype/payment_request/payment_request.js @@ -1,7 +1,6 @@ -cur_frm.add_fetch("payment_gateway", "payment_account", "payment_account") -cur_frm.add_fetch("payment_gateway", "payment_gateway", "payment_gateway") -cur_frm.add_fetch("payment_gateway", "message", "message") -cur_frm.add_fetch("payment_gateway", "payment_url_message", "payment_url_message") +cur_frm.add_fetch("payment_gateway_account", "payment_account", "payment_account") +cur_frm.add_fetch("payment_gateway_account", "payment_gateway", "payment_gateway") +cur_frm.add_fetch("payment_gateway_account", "message", "message") frappe.ui.form.on("Payment Request", "onload", function(frm, dt, dn){ if (frm.doc.reference_doctype) { diff --git a/erpnext/accounts/doctype/sales_invoice/pos.py b/erpnext/accounts/doctype/sales_invoice/pos.py index 479548c6e3..7e23793700 100755 --- a/erpnext/accounts/doctype/sales_invoice/pos.py +++ b/erpnext/accounts/doctype/sales_invoice/pos.py @@ -227,8 +227,8 @@ def get_contacts(customers): customers = [frappe._dict({'name': customers})] for data in customers: - contact = frappe.db.sql(""" select email_id, phone, mobile_no from `tabContact` - where is_primary_contact =1 and name in + contact = frappe.db.sql(""" select email_id, phone from `tabContact` + where is_primary_contact=1 and name in (select parent from `tabDynamic Link` where link_doctype = 'Customer' and link_name = %s and parenttype = 'Contact')""", data.name, as_dict=1) if contact: @@ -432,7 +432,6 @@ def get_customer_id(doc, customer=None): return cust_id - def make_customer_and_address(customers): customers_list = [] for customer, data in iteritems(customers): @@ -449,7 +448,6 @@ def make_customer_and_address(customers): frappe.db.commit() return customers_list - def add_customer(data): customer = data.get('full_name') or data.get('customer') if frappe.db.exists("Customer", customer.strip()): @@ -466,21 +464,18 @@ def add_customer(data): frappe.db.commit() return customer_doc.name - def get_territory(data): if data.get('territory'): return data.get('territory') return frappe.db.get_single_value('Selling Settings','territory') or _('All Territories') - def get_customer_group(data): if data.get('customer_group'): return data.get('customer_group') return frappe.db.get_single_value('Selling Settings', 'customer_group') or frappe.db.get_value('Customer Group', {'is_group': 0}, 'name') - def make_contact(args, customer): if args.get('email_id') or args.get('phone'): name = frappe.db.get_value('Dynamic Link', @@ -506,7 +501,6 @@ def make_contact(args, customer): doc.flags.ignore_mandatory = True doc.save(ignore_permissions=True) - def make_address(args, customer): if not args.get('address_line1'): return @@ -521,7 +515,10 @@ def make_address(args, customer): address = frappe.get_doc('Address', name) else: address = frappe.new_doc('Address') - address.country = frappe.get_cached_value('Company', args.get('company'), 'country') + if args.get('company'): + address.country = frappe.get_cached_value('Company', + args.get('company'), 'country') + address.append('links', { 'link_doctype': 'Customer', 'link_name': customer @@ -533,7 +530,6 @@ def make_address(args, customer): address.flags.ignore_mandatory = True address.save(ignore_permissions=True) - def make_email_queue(email_queue): name_list = [] for key, data in iteritems(email_queue): @@ -550,7 +546,6 @@ def make_email_queue(email_queue): return name_list - def validate_item(doc): for item in doc.get('items'): if not frappe.db.exists('Item', item.get('item_code')): @@ -569,7 +564,6 @@ def validate_item(doc): item_doc.save(ignore_permissions=True) frappe.db.commit() - def submit_invoice(si_doc, name, doc, name_list): try: si_doc.insert() @@ -585,7 +579,6 @@ def submit_invoice(si_doc, name, doc, name_list): return name_list - def save_invoice(doc, name, name_list): try: if not frappe.db.exists('Sales Invoice', {'offline_pos_name': name}): diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 4f80b78c88..95c5dd5cd4 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -206,9 +206,9 @@ class SalesInvoice(SellingController): total_amount_in_payments = 0 for payment in self.payments: total_amount_in_payments += payment.amount - - if total_amount_in_payments < self.rounded_total: - frappe.throw(_("Total payments amount can't be greater than {}".format(-self.rounded_total))) + invoice_total = self.rounded_total or self.grand_total + if total_amount_in_payments < invoice_total: + frappe.throw(_("Total payments amount can't be greater than {}".format(-invoice_total))) def validate_pos_paid_amount(self): if len(self.payments) == 0 and self.is_pos: @@ -1510,4 +1510,4 @@ def create_invoice_discounting(source_name, target_doc=None): "outstanding_amount": invoice.outstanding_amount }) - return invoice_discounting \ No newline at end of file + return invoice_discounting diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index dff55947df..4f253b69f7 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -818,7 +818,6 @@ class TestSalesInvoice(unittest.TestCase): self.assertEqual(expected_gl_entries[i][2], gle.credit) si.cancel() - frappe.delete_doc('Sales Invoice', si.name) gle = frappe.db.sql("""select * from `tabGL Entry` where voucher_type='Sales Invoice' and voucher_no=%s""", si.name) diff --git a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py index 638e57ed2b..b1468999fc 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py @@ -48,7 +48,6 @@ class TestTaxWithholdingCategory(unittest.TestCase): #delete invoices to avoid clashing for d in invoices: d.cancel() - frappe.delete_doc("Purchase Invoice", d.name) def test_single_threshold_tds(self): invoices = [] @@ -83,7 +82,6 @@ class TestTaxWithholdingCategory(unittest.TestCase): # delete invoices to avoid clashing for d in invoices: d.cancel() - frappe.delete_doc("Purchase Invoice", d.name) def test_single_threshold_tds_with_previous_vouchers(self): invoices = [] @@ -102,7 +100,6 @@ class TestTaxWithholdingCategory(unittest.TestCase): # delete invoices to avoid clashing for d in invoices: d.cancel() - frappe.delete_doc("Purchase Invoice", d.name) def create_purchase_invoice(**args): # return sales invoice doc object diff --git a/erpnext/accounts/page/pos/pos.js b/erpnext/accounts/page/pos/pos.js index 3834c96bfb..0e7301204a 100755 --- a/erpnext/accounts/page/pos/pos.js +++ b/erpnext/accounts/page/pos/pos.js @@ -816,7 +816,6 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ contact = me.contacts[data.name]; if(reg.test(data.name.toLowerCase()) || reg.test(data.customer_name.toLowerCase()) - || (contact && reg.test(contact["mobile_no"])) || (contact && reg.test(contact["phone"])) || (data.customer_group && reg.test(data.customer_group.toLowerCase()))){ return data; @@ -834,7 +833,6 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ if(contact && !c['phone']) { c["phone"] = contact["phone"]; c["email_id"] = contact["email_id"]; - c["mobile_no"] = contact["mobile_no"]; } me.customers_mapper.push({ @@ -844,10 +842,9 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ customer_group: c.customer_group, territory: c.territory, phone: contact ? contact["phone"] : '', - mobile_no: contact ? contact["mobile_no"] : '', email_id: contact ? contact["email_id"] : '', searchtext: ['customer_name', 'customer_group', 'name', 'value', - 'label', 'email_id', 'phone', 'mobile_no'] + 'label', 'email_id', 'phone'] .map(key => c[key]).join(' ') .toLowerCase() }); @@ -1762,18 +1759,11 @@ erpnext.pos.PointOfSale = erpnext.taxes_and_totals.extend({ this.si_docs = this.get_submitted_invoice() || []; this.email_queue_list = this.get_email_queue() || {}; this.customers_list = this.get_customers_details() || {}; - if(this.customer_doc) { - this.freeze = this.customer_doc.display - } - - freeze_screen = this.freeze_screen || false; - - if ((this.si_docs.length || this.email_queue_list || this.customers_list) && !this.freeze) { - this.freeze = true; + if (this.si_docs.length || this.email_queue_list || this.customers_list) { frappe.call({ method: "erpnext.accounts.doctype.sales_invoice.pos.make_invoice", - freeze: freeze_screen, + freeze: true, args: { doc_list: me.si_docs, email_queue_list: me.email_queue_list, diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.html b/erpnext/accounts/report/accounts_receivable/accounts_receivable.html index 192b6d7be3..d00bcf643e 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.html +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.html @@ -40,7 +40,7 @@ -{% if(filters.show_pdc_in_print) { %} +{% if(filters.show_future_payments) { %} {% var balance_row = data.slice(-1).pop(); var range1 = report.columns[11].label; var range2 = report.columns[12].label; @@ -122,22 +122,22 @@ {%= __("Date") %} {%= __("Age (Days)") %} - {% if(report.report_name === "Accounts Receivable" && filters.show_sales_person_in_print) { %} + {% if(report.report_name === "Accounts Receivable" && filters.show_sales_person) { %} {%= __("Reference") %} {%= __("Sales Person") %} {% } else { %} {%= __("Reference") %} {% } %} - {% if(!filters.show_pdc_in_print) { %} + {% if(!filters.show_future_payments) { %} {%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %} {% } %} {%= __("Invoiced Amount") %} - {% if(!filters.show_pdc_in_print) { %} + {% if(!filters.show_future_payments) { %} {%= __("Paid Amount") %} {%= report.report_name === "Accounts Receivable" ? __('Credit Note') : __('Debit Note') %} {% } %} {%= __("Outstanding Amount") %} - {% if(filters.show_pdc_in_print) { %} + {% if(filters.show_future_payments) { %} {% if(report.report_name === "Accounts Receivable") { %} {%= __("Customer LPO No.") %} {% } %} @@ -162,18 +162,18 @@ {%= frappe.datetime.str_to_user(data[i]["posting_date"]) %} {%= data[i][__("Age (Days)")] %} - {% if(!filters.show_pdc_in_print) { %} + {% if(!filters.show_future_payments) { %} {%= data[i]["voucher_type"] %}
    {% } %} {%= data[i]["voucher_no"] %} - {% if(report.report_name === "Accounts Receivable" && filters.show_sales_person_in_print) { %} + {% if(report.report_name === "Accounts Receivable" && filters.show_sales_person) { %} {%= data[i]["sales_person"] %} {% } %} - {% if(!filters.show_pdc_in_print) { %} + {% if(!filters.show_future_payments) { %} {% if(!(filters.customer || filters.supplier)) { %} {%= data[i][__("Customer")] || data[i][__("Supplier")] %} @@ -195,7 +195,7 @@ {%= format_currency(data[i]["invoiced_amount"], data[i]["currency"]) %} - {% if(!filters.show_pdc_in_print) { %} + {% if(!filters.show_future_payments) { %} {%= format_currency(data[i]["paid_amount"], data[i]["currency"]) %} @@ -204,7 +204,7 @@ {%= format_currency(data[i]["outstanding_amount"], data[i]["currency"]) %} - {% if(filters.show_pdc_in_print) { %} + {% if(filters.show_future_payments) { %} {% if(report.report_name === "Accounts Receivable") { %} {%= data[i]["po_no"] %} @@ -215,10 +215,10 @@ {% } %} {% } else { %} - {% if(!filters.show_pdc_in_print) { %} + {% if(!filters.show_future_payments) { %} {% } %} - {% if(report.report_name === "Accounts Receivable" && filters.show_sales_person_in_print) { %} + {% if(report.report_name === "Accounts Receivable" && filters.show_sales_person) { %} {% } %} @@ -226,7 +226,7 @@ {%= format_currency(data[i]["invoiced_amount"], data[i]["currency"] ) %} - {% if(!filters.show_pdc_in_print) { %} + {% if(!filters.show_future_payments) { %} {%= format_currency(data[i]["paid_amount"], data[i]["currency"]) %} {%= report.report_name === "Accounts Receivable" ? format_currency(data[i]["credit_note"], data[i]["currency"]) : format_currency(data[i]["debit_note"], data[i]["currency"]) %} @@ -234,7 +234,7 @@ {%= format_currency(data[i]["outstanding_amount"], data[i]["currency"]) %} - {% if(filters.show_pdc_in_print) { %} + {% if(filters.show_future_payments) { %} {% if(report.report_name === "Accounts Receivable") { %} {%= data[i][__("Customer LPO")] %} diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js index 4551973ac6..228be18d21 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js @@ -130,13 +130,18 @@ frappe.query_reports["Accounts Receivable"] = { "fieldtype": "Check", }, { - "fieldname":"show_pdc_in_print", - "label": __("Show PDC in Print"), + "fieldname":"show_future_payments", + "label": __("Show Future Payments"), "fieldtype": "Check", }, { - "fieldname":"show_sales_person_in_print", - "label": __("Show Sales Person in Print"), + "fieldname":"show_delivery_notes", + "label": __("Show Delivery Notes"), + "fieldtype": "Check", + }, + { + "fieldname":"show_sales_person", + "label": __("Show Sales Person"), "fieldtype": "Check", }, { diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 0e4ee12548..88e4227837 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -4,9 +4,33 @@ from __future__ import unicode_literals import frappe, erpnext from frappe import _, scrub -from frappe.utils import getdate, nowdate, flt, cint, formatdate, cstr +from frappe.utils import getdate, nowdate, flt, cint, formatdate, cstr, now, time_diff_in_seconds +from collections import OrderedDict +from erpnext.accounts.utils import get_currency_precision from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import get_accounting_dimensions +# This report gives a summary of all Outstanding Invoices considering the following + +# 1. Invoice can be booked via Sales/Purchase Invoice or Journal Entry +# 2. Report handles both receivable and payable +# 3. Key balances for each row are "Invoiced Amount", "Paid Amount", "Credit/Debit Note Amount", "Oustanding Amount" +# 4. For explicit payment terms in invoice (example: 30% advance, 30% on delivery, 40% post delivery), +# the invoice will be broken up into multiple rows, one for each payment term +# 5. If there are payments after the report date (post dated), these will be updated in additional columns +# for future amount +# 6. Configurable Ageing Groups (0-30, 30-60 etc) can be set via filters +# 7. For overpayment against an invoice with payment terms, there will be an additional row +# 8. Invoice details like Sales Persons, Delivery Notes are also fetched comma separated +# 9. Report amounts are in "Party Currency" if party is selected, or company currency for multi-party +# 10. This reports is based on all GL Entries that are made against account_type "Receivable" or "Payable" + +def execute(filters=None): + args = { + "party_type": "Customer", + "naming_by": ["Selling Settings", "cust_master_name"], + } + return ReceivablePayableReport(filters).run(args) + class ReceivablePayableReport(object): def __init__(self, filters=None): self.filters = frappe._dict(filters or {}) @@ -16,459 +40,429 @@ class ReceivablePayableReport(object): else self.filters.report_date def run(self, args): - party_naming_by = frappe.db.get_value(args.get("naming_by")[0], None, args.get("naming_by")[1]) - columns = self.get_columns(party_naming_by, args) - data = self.get_data(party_naming_by, args) - chart = self.get_chart_data(columns, data) - return columns, data, None, chart - - def get_columns(self, party_naming_by, args): - columns = [] - columns.append({ - "label": _("Posting Date"), - "fieldtype": "Date", - "fieldname": "posting_date", - "width": 90 - }) - - columns += [_(args.get("party_type")) + ":Link/" + args.get("party_type") + ":200"] - - if party_naming_by == "Naming Series": - columns += [args.get("party_type") + " Name::110"] - - if args.get("party_type") == 'Customer': - columns.append({ - "label": _("Customer Contact"), - "fieldtype": "Link", - "fieldname": "contact", - "options":"Contact", - "width": 100 - }) - - columns.append({ - "label": _("Voucher Type"), - "fieldtype": "Data", - "fieldname": "voucher_type", - "width": 110 - }) - - columns.append({ - "label": _("Voucher No"), - "fieldtype": "Dynamic Link", - "fieldname": "voucher_no", - "width": 110, - "options": "voucher_type", - }) - - columns += [_("Due Date") + ":Date:80"] - - if args.get("party_type") == "Supplier": - columns += [_("Bill No") + "::80", _("Bill Date") + ":Date:80"] - - credit_or_debit_note = "Credit Note" if args.get("party_type") == "Customer" else "Debit Note" - - if self.filters.based_on_payment_terms: - columns.append({ - "label": _("Payment Term"), - "fieldname": "payment_term", - "fieldtype": "Data", - "width": 120 - }) - columns.append({ - "label": _("Invoice Grand Total"), - "fieldname": "invoice_grand_total", - "fieldtype": "Currency", - "options": "currency", - "width": 120 - }) - - for label in ("Invoiced Amount", "Paid Amount", credit_or_debit_note, "Outstanding Amount"): - columns.append({ - "label": _(label), - "fieldname": frappe.scrub(label), - "fieldtype": "Currency", - "options": "currency", - "width": 120 - }) - - columns += [_("Age (Days)") + ":Int:80"] - - self.ageing_col_idx_start = len(columns) - - if not "range1" in self.filters: - self.filters["range1"] = "30" - if not "range2" in self.filters: - self.filters["range2"] = "60" - if not "range3" in self.filters: - self.filters["range3"] = "90" - if not "range4" in self.filters: - self.filters["range4"] = "120" - - for label in ("0-{range1}".format(range1=self.filters["range1"]), - "{range1}-{range2}".format(range1=cint(self.filters["range1"])+ 1, range2=self.filters["range2"]), - "{range2}-{range3}".format(range2=cint(self.filters["range2"])+ 1, range3=self.filters["range3"]), - "{range3}-{range4}".format(range3=cint(self.filters["range3"])+ 1, range4=self.filters["range4"]), - "{range4}-{above}".format(range4=cint(self.filters["range4"])+ 1, above=_("Above"))): - columns.append({ - "label": label, - "fieldname":label, - "fieldtype": "Currency", - "options": "currency", - "width": 120 - }) - - columns += [ - { - "fieldname": "currency", - "label": _("Currency"), - "fieldtype": "Link", - "options": "Currency", - "width": 100 - }, - { - "fieldname": "pdc/lc_ref", - "label": _("PDC/LC Ref"), - "fieldtype": "Data", - "width": 110 - }, - { - "fieldname": "pdc/lc_amount", - "label": _("PDC/LC Amount"), - "fieldtype": "Currency", - "options": "currency", - "width": 130 - }, - { - "fieldname": "remaining_balance", - "label": _("Remaining Balance"), - "fieldtype": "Currency", - "options": "currency", - "width": 130 - }] - - if args.get('party_type') == 'Customer': - columns += [ - { - "label": _("Customer LPO"), - "fieldtype": "Data", - "fieldname": "po_no", - "width": 100, - }, - _("Delivery Note") + ":Data:100", - _("Territory") + ":Link/Territory:80", - _("Customer Group") + ":Link/Customer Group:120", - { - "label": _("Sales Person"), - "fieldtype": "Data", - "fieldname": "sales_person", - "width": 120, - } - ] - if args.get("party_type") == "Supplier": - columns += [_("Supplier Group") + ":Link/Supplier Group:80"] - - columns.append(_("Remarks") + "::200") - - return columns - - def get_data(self, party_naming_by, args): - from erpnext.accounts.utils import get_currency_precision - self.currency_precision = get_currency_precision() or 2 - self.dr_or_cr = "debit" if args.get("party_type") == "Customer" else "credit" - - future_vouchers = self.get_entries_after(self.filters.report_date, args.get("party_type")) + self.filters.update(args) + self.set_defaults() + self.party_naming_by = frappe.db.get_value(args.get("naming_by")[0], None, args.get("naming_by")[1]) + self.get_columns() + self.get_data() + self.get_chart_data() + return self.columns, self.data, None, self.chart + def set_defaults(self): if not self.filters.get("company"): - self.filters["company"] = frappe.db.get_single_value('Global Defaults', 'default_company') - + self.filters.company = frappe.db.get_single_value('Global Defaults', 'default_company') self.company_currency = frappe.get_cached_value('Company', self.filters.get("company"), "default_currency") + self.currency_precision = get_currency_precision() or 2 + self.dr_or_cr = "debit" if self.filters.party_type == "Customer" else "credit" + self.party_type = self.filters.party_type + self.party_details = {} + self.invoices = set() - return_entries = self.get_return_entries(args.get("party_type")) + def get_data(self): + t1 = now() + self.get_gl_entries() + self.voucher_balance = OrderedDict() + self.init_voucher_balance() # invoiced, paid, credit_note, outstanding - data = [] - self.pdc_details = get_pdc_details(args.get("party_type"), self.filters.report_date) - gl_entries_data = self.get_entries_till(self.filters.report_date, args.get("party_type")) + # Build delivery note map against all sales invoices + self.build_delivery_note_map() - if gl_entries_data: - voucher_nos = [d.voucher_no for d in gl_entries_data] or [] - dn_details = get_dn_details(args.get("party_type"), voucher_nos) - self.voucher_details = get_voucher_details(args.get("party_type"), voucher_nos, dn_details) + # Get invoice details like bill_no, due_date etc for all invoices + self.get_invoice_details() - if self.filters.based_on_payment_terms and gl_entries_data: - self.payment_term_map = self.get_payment_term_detail(voucher_nos) + # fetch future payments against invoices + self.get_future_payments() - self.gle_inclusion_map = {} - for gle in gl_entries_data: - if self.is_receivable_or_payable(gle, self.dr_or_cr, future_vouchers, return_entries): - self.gle_inclusion_map[gle.name] = True - outstanding_amount, credit_note_amount, payment_amount = self.get_outstanding_amount( - gle,self.filters.report_date, self.dr_or_cr, return_entries) - temp_outstanding_amt = outstanding_amount - temp_credit_note_amt = credit_note_amount + self.data = [] + for gle in self.gl_entries: + self.update_voucher_balance(gle) - if abs(outstanding_amount) > 0.1/10**self.currency_precision: - if self.filters.based_on_payment_terms and self.payment_term_map.get(gle.voucher_no): - for d in self.payment_term_map.get(gle.voucher_no): - # Allocate payment amount based on payment terms(FIFO order) - payment_amount, d.payment_amount = self.allocate_based_on_fifo(payment_amount, d.payment_term_amount) + self.build_data() - term_outstanding_amount = d.payment_term_amount - d.payment_amount + def init_voucher_balance(self): + # build all keys, since we want to exclude vouchers beyond the report date + for gle in self.gl_entries: + # get the balance object for voucher_type + key = (gle.voucher_type, gle.voucher_no, gle.party) + if not key in self.voucher_balance: + self.voucher_balance[key] = frappe._dict( + voucher_type = gle.voucher_type, + voucher_no = gle.voucher_no, + party = gle.party, + posting_date = gle.posting_date, + remarks = gle.remarks, + invoiced = 0.0, + paid = 0.0, + credit_note = 0.0, + outstanding = 0.0 + ) + self.get_invoices(gle) - # Allocate credit note based on payment terms(FIFO order) - credit_note_amount, d.credit_note_amount = self.allocate_based_on_fifo(credit_note_amount, term_outstanding_amount) + def get_invoices(self, gle): + if gle.voucher_type in ('Sales Invoice', 'Purchase Invoice'): + self.invoices.add(gle.voucher_no) - term_outstanding_amount -= d.credit_note_amount - - row_outstanding = term_outstanding_amount - # Allocate PDC based on payment terms(FIFO order) - d.pdc_details, d.pdc_amount = self.allocate_pdc_amount_in_fifo(gle, row_outstanding) - - if term_outstanding_amount > 0: - row = self.prepare_row(party_naming_by, args, gle, term_outstanding_amount, - d.credit_note_amount, d.due_date, d.payment_amount , d.payment_term_amount, - d.description, d.pdc_amount, d.pdc_details) - data.append(row) - - if credit_note_amount: - row = self.prepare_row_without_payment_terms(party_naming_by, args, gle, temp_outstanding_amt, - temp_credit_note_amt) - data.append(row) - - else: - row = self.prepare_row_without_payment_terms(party_naming_by, args, gle, outstanding_amount, - credit_note_amount) - data.append(row) - return data - - def allocate_pdc_amount_in_fifo(self, gle, row_outstanding): - pdc_list = self.pdc_details.get((gle.voucher_no, gle.party), []) - - pdc_details = [] - pdc_amount = 0 - for pdc in pdc_list: - if row_outstanding <= pdc.pdc_amount: - pdc_amount += row_outstanding - pdc.pdc_amount -= row_outstanding - if row_outstanding and pdc.pdc_ref and pdc.pdc_date: - pdc_details.append(cstr(pdc.pdc_ref) + "/" + formatdate(pdc.pdc_date)) - row_outstanding = 0 + def update_voucher_balance(self, gle): + # get the row where this balance needs to be updated + # if its a payment, it will return the linked invoice or will be considered as advance + row = self.get_voucher_balance(gle) + # gle_balance will be the total "debit - credit" for receivable type reports and + # and vice-versa for payable type reports + gle_balance = self.get_gle_balance(gle) + if gle_balance > 0: + if gle.voucher_type in ('Journal Entry', 'Payment Entry') and gle.against_voucher: + # debit against sales / purchase invoice + row.paid -= gle_balance else: - pdc_amount = pdc.pdc_amount - if pdc.pdc_amount and pdc.pdc_ref and pdc.pdc_date: - pdc_details.append(cstr(pdc.pdc_ref) + "/" + formatdate(pdc.pdc_date)) - pdc.pdc_amount = 0 - row_outstanding -= pdc_amount - - return pdc_details, pdc_amount - - def prepare_row_without_payment_terms(self, party_naming_by, args, gle, outstanding_amount, credit_note_amount): - pdc_list = self.pdc_details.get((gle.voucher_no, gle.party), []) - pdc_amount = 0 - pdc_details = [] - for d in pdc_list: - pdc_amount += flt(d.pdc_amount) - if pdc_amount and d.pdc_ref and d.pdc_date: - pdc_details.append(cstr(d.pdc_ref) + "/" + formatdate(d.pdc_date)) - - row = self.prepare_row(party_naming_by, args, gle, outstanding_amount, - credit_note_amount, pdc_amount=pdc_amount, pdc_details=pdc_details) - - return row - - - def allocate_based_on_fifo(self, total_amount, row_amount): - allocated_amount = 0 - if row_amount <= total_amount: - allocated_amount = row_amount - total_amount -= row_amount + # invoice + row.invoiced += gle_balance else: - allocated_amount = total_amount - total_amount = 0 + # payment or credit note for receivables + if self.is_invoice(gle): + # stand alone debit / credit note + row.credit_note -= gle_balance + else: + # advance / unlinked payment or other adjustment + row.paid -= gle_balance - return total_amount, allocated_amount + def get_voucher_balance(self, gle): + voucher_balance = None - def prepare_row(self, party_naming_by, args, gle, outstanding_amount, credit_note_amount, - due_date=None, paid_amt=None, payment_term_amount=None, payment_term=None, pdc_amount=None, pdc_details=None): - row = [gle.posting_date, gle.party] + if gle.against_voucher: + # find invoice + voucher_balance = self.voucher_balance.get((gle.against_voucher_type, gle.against_voucher, gle.party)) - # customer / supplier name - if party_naming_by == "Naming Series": - row += [self.get_party_name(gle.party_type, gle.party)] + if not voucher_balance: + # no invoice, this is an invoice / stand-alone payment / credit note + voucher_balance = self.voucher_balance.get((gle.voucher_type, gle.voucher_no, gle.party)) - if args.get("party_type") == 'Customer': - row += [self.get_customer_contact(gle.party_type, gle.party)] + return voucher_balance - # get due date - if not due_date: - due_date = self.voucher_details.get(gle.voucher_no, {}).get("due_date", "") - bill_date = self.voucher_details.get(gle.voucher_no, {}).get("bill_date", "") + def build_data(self): + # set outstanding for all the accumulated balances + # as we can use this to filter out invoices without outstanding + for key, row in self.voucher_balance.items(): + row.outstanding = flt(row.invoiced - row.paid - row.credit_note, self.currency_precision) + row.invoice_grand_total = row.invoiced - row += [gle.voucher_type, gle.voucher_no, due_date] + if abs(row.outstanding) > 0.1/10 ** self.currency_precision: + # non-zero oustanding, we must consider this row - # get supplier bill details - if args.get("party_type") == "Supplier": - row += [ - self.voucher_details.get(gle.voucher_no, {}).get("bill_no", ""), - self.voucher_details.get(gle.voucher_no, {}).get("bill_date", "") - ] + if self.is_invoice(row) and self.filters.based_on_payment_terms: + # is an invoice, allocate based on fifo + # adds a list `payment_terms` which contains new rows for each term + self.allocate_outstanding_based_on_payment_terms(row) - # invoiced and paid amounts - invoiced_amount = gle.get(self.dr_or_cr) if (gle.get(self.dr_or_cr) > 0) else 0 + if row.payment_terms: + # make separate rows for each payment term + for d in row.payment_terms: + if d.outstanding > 0: + self.append_row(d) - if self.filters.based_on_payment_terms: - row+=[payment_term, invoiced_amount] - if payment_term_amount: - invoiced_amount = payment_term_amount - - if not payment_term_amount: - paid_amt = invoiced_amount - outstanding_amount - credit_note_amount - row += [invoiced_amount, paid_amt, credit_note_amount, outstanding_amount] - - # ageing data - if self.filters.ageing_based_on == "Due Date": - entry_date = due_date - elif self.filters.ageing_based_on == "Supplier Invoice Date": - entry_date = bill_date - else: - entry_date = gle.posting_date - - row += get_ageing_data(cint(self.filters.range1), cint(self.filters.range2), - cint(self.filters.range3), cint(self.filters.range4), self.age_as_on, entry_date, outstanding_amount) - - # issue 6371-Ageing buckets should not have amounts if due date is not reached - if self.filters.ageing_based_on == "Due Date" \ - and getdate(due_date) > getdate(self.filters.report_date): - row[-1]=row[-2]=row[-3]=row[-4]=row[-5]=0 - - if self.filters.ageing_based_on == "Supplier Invoice Date" \ - and getdate(bill_date) > getdate(self.filters.report_date): - - row[-1]=row[-2]=row[-3]=row[-4]=row[-5]=0 - - if self.filters.get(scrub(args.get("party_type"))): - row.append(gle.account_currency) - else: - row.append(self.company_currency) - - remaining_balance = outstanding_amount - flt(pdc_amount) - pdc_details = ", ".join(pdc_details) - row += [pdc_details, pdc_amount, remaining_balance] - - if args.get('party_type') == 'Customer': - # customer LPO - row += [self.voucher_details.get(gle.voucher_no, {}).get("po_no")] - - # Delivery Note - row += [self.voucher_details.get(gle.voucher_no, {}).get("delivery_note")] - - # customer territory / supplier group - if args.get("party_type") == "Customer": - row += [self.get_territory(gle.party), self.get_customer_group(gle.party), - self.voucher_details.get(gle.voucher_no, {}).get("sales_person")] - if args.get("party_type") == "Supplier": - row += [self.get_supplier_group(gle.party)] - - row.append(gle.remarks) - - return row - - def get_entries_after(self, report_date, party_type): - # returns a distinct list - return list(set([(e.voucher_type, e.voucher_no) for e in self.get_gl_entries(party_type, report_date, for_future=True)])) - - def get_entries_till(self, report_date, party_type): - # returns a generator - return self.get_gl_entries(party_type, report_date) - - def is_receivable_or_payable(self, gle, dr_or_cr, future_vouchers, return_entries): - return ( - # advance - (not gle.against_voucher) or - - # against sales order/purchase order - (gle.against_voucher_type in ["Sales Order", "Purchase Order"]) or - - # sales invoice/purchase invoice - (gle.against_voucher==gle.voucher_no and gle.get(dr_or_cr) > 0) or - - # standalone credit notes - (gle.against_voucher==gle.voucher_no and gle.voucher_no in return_entries and not return_entries.get(gle.voucher_no)) or - - # entries adjusted with future vouchers - ((gle.against_voucher_type, gle.against_voucher) in future_vouchers) - ) - - def get_return_entries(self, party_type): - doctype = "Sales Invoice" if party_type=="Customer" else "Purchase Invoice" - return_entries = frappe._dict(frappe.get_all(doctype, - filters={"is_return": 1, "docstatus": 1}, fields=["name", "return_against"], as_list=1)) - return return_entries - - def get_outstanding_amount(self, gle, report_date, dr_or_cr, return_entries): - payment_amount, credit_note_amount = 0.0, 0.0 - reverse_dr_or_cr = "credit" if dr_or_cr=="debit" else "debit" - for e in self.get_gl_entries_for(gle.party, gle.party_type, gle.voucher_type, gle.voucher_no): - if getdate(e.posting_date) <= report_date \ - and (e.name!=gle.name or (e.voucher_no in return_entries and not return_entries.get(e.voucher_no))): - if e.name!=gle.name and self.gle_inclusion_map.get(e.name): - continue - self.gle_inclusion_map[e.name] = True - amount = flt(e.get(reverse_dr_or_cr), self.currency_precision) - flt(e.get(dr_or_cr), self.currency_precision) - if e.voucher_no not in return_entries: - payment_amount += amount + # if there is overpayment, add another row + self.allocate_extra_payments_or_credits(row) + else: + self.append_row(row) else: - credit_note_amount += amount + self.append_row(row) - voucher_amount = flt(gle.get(dr_or_cr), self.currency_precision) - flt(gle.get(reverse_dr_or_cr), self.currency_precision) - if gle.voucher_no in return_entries and not return_entries.get(gle.voucher_no): - voucher_amount = 0 + def append_row(self, row): + self.allocate_future_payments(row) + self.set_invoice_details(row) + self.set_party_details(row) + self.set_ageing(row) + self.data.append(row) - outstanding_amount = flt((voucher_amount - payment_amount - credit_note_amount), self.currency_precision) - credit_note_amount = flt(credit_note_amount, self.currency_precision) + def set_invoice_details(self, row): + row.update(self.invoice_details.get(row.voucher_no, {})) + if row.voucher_type == 'Sales Invoice': + if self.filters.show_delivery_notes: + self.set_delivery_notes(row) - return outstanding_amount, credit_note_amount, payment_amount + if self.filters.show_sales_person and row.sales_team: + row.sales_person = ", ".join(row.sales_team) + del row['sales_team'] - def get_party_name(self, party_type, party_name): - return self.get_party_map(party_type).get(party_name, {}).get("customer_name" if party_type == "Customer" else "supplier_name") or "" + def set_delivery_notes(self, row): + delivery_notes = self.delivery_notes.get(row.voucher_no, []) + if delivery_notes: + row.delivery_notes = ', '.join(delivery_notes) - def get_customer_contact(self, party_type, party_name): - return self.get_party_map(party_type).get(party_name, {}).get("customer_primary_contact") + def build_delivery_note_map(self): + if self.invoices and self.filters.show_delivery_notes: + self.delivery_notes = frappe._dict() - def get_territory(self, party_name): - return self.get_party_map("Customer").get(party_name, {}).get("territory") or "" + # delivery note link inside sales invoice + si_against_dn = frappe.db.sql(""" + select parent, delivery_note + from `tabSales Invoice Item` + where docstatus=1 and parent in (%s) + """ % (','.join(['%s'] * len(self.invoices))), tuple(self.invoices), as_dict=1) - def get_customer_group(self, party_name): - return self.get_party_map("Customer").get(party_name, {}).get("customer_group") or "" + for d in si_against_dn: + if d.delivery_note: + self.delivery_notes.setdefault(d.parent, set()).add(d.delivery_note) - def get_supplier_group(self, party_name): - return self.get_party_map("Supplier").get(party_name, {}).get("supplier_group") or "" + dn_against_si = frappe.db.sql(""" + select distinct parent, against_sales_invoice + from `tabDelivery Note Item` + where against_sales_invoice in (%s) + """ % (','.join(['%s'] * len(self.invoices))), tuple(self.invoices) , as_dict=1) - def get_party_map(self, party_type): - if not hasattr(self, "party_map"): - if party_type == "Customer": - select_fields = "name, customer_name, territory, customer_group, customer_primary_contact" - elif party_type == "Supplier": - select_fields = "name, supplier_name, supplier_group" + for d in dn_against_si: + self.delivery_notes.setdefault(d.against_sales_invoice, set()).add(d.parent) - self.party_map = dict(((r.name, r) for r in frappe.db.sql("select {0} from `tab{1}`" - .format(select_fields, party_type), as_dict=True))) + def get_invoice_details(self): + self.invoice_details = frappe._dict() + if self.party_type == "Customer": + si_list = frappe.db.sql(""" + select name, due_date, po_no + from `tabSales Invoice` + where posting_date <= %s + """,self.filters.report_date, as_dict=1) + for d in si_list: + self.invoice_details.setdefault(d.name, d) - return self.party_map + # Get Sales Team + if self.filters.show_sales_person: + sales_team = frappe.db.sql(""" + select parent, sales_person + from `tabSales Team` + where parenttype = 'Sales Invoice' + """, as_dict=1) + for d in sales_team: + self.invoice_details.setdefault(d.parent, {})\ + .setdefault('sales_team', []).append(d.sales_person) - def get_gl_entries(self, party_type, date=None, for_future=False): - conditions, values = self.prepare_conditions(party_type) + if self.party_type == "Supplier": + for pi in frappe.db.sql(""" + select name, due_date, bill_no, bill_date + from `tabPurchase Invoice` + where posting_date <= %s + """, self.filters.report_date, as_dict=1): + self.invoice_details.setdefault(pi.name, pi) - if self.filters.get(scrub(party_type)): - select_fields = "sum(debit_in_account_currency) as debit, sum(credit_in_account_currency) as credit" + # Invoices booked via Journal Entries + journal_entries = frappe.db.sql(""" + select name, due_date, bill_no, bill_date + from `tabJournal Entry` + where posting_date <= %s + """, self.filters.report_date, as_dict=1) + + for je in journal_entries: + if je.bill_no: + self.invoice_details.setdefault(je.name, je) + + def set_party_details(self, row): + # customer / supplier name + party_details = self.get_party_details(row.party) + row.update(party_details) + + if self.filters.get(scrub(self.filters.party_type)): + row.currency = row.account_currency else: - select_fields = "sum(debit) as debit, sum(credit) as credit" + row.currency = self.company_currency - if date and not for_future: - conditions += " and posting_date <= '%s'" % date + def allocate_outstanding_based_on_payment_terms(self, row): + self.get_payment_terms(row) + for term in row.payment_terms: + term.outstanding = term.invoiced - if date and for_future: - conditions += " and posting_date > '%s'" % date + # update "paid" and "oustanding" for this term + self.allocate_closing_to_term(row, term, 'paid') + + # update "credit_note" and "oustanding" for this term + if term.outstanding: + self.allocate_closing_to_term(row, term, 'credit_note') + + def get_payment_terms(self, row): + # build payment_terms for row + payment_terms_details = frappe.db.sql(""" + select + si.name, si.party_account_currency, si.currency, si.conversion_rate, + ps.due_date, ps.payment_amount, ps.description + from `tab{0}` si, `tabPayment Schedule` ps + where + si.name = ps.parent and + si.name = %s + order by ps.due_date + """.format(row.voucher_type), row.voucher_no, as_dict = 1) + + + original_row = frappe._dict(row) + row.payment_terms = [] + + # If no or single payment terms, no need to split the row + if len(payment_terms_details) <= 1: + return + + for d in payment_terms_details: + term = frappe._dict(original_row) + self.append_payment_term(row, d, term) + + def append_payment_term(self, row, d, term): + if self.filters.get("customer") and d.currency == d.party_account_currency: + invoiced = d.payment_amount + else: + invoiced = flt(flt(d.payment_amount) * flt(d.conversion_rate), self.currency_precision) + + row.payment_terms.append(term.update({ + "due_date": d.due_date, + "invoiced": invoiced, + "invoice_grand_total": row.invoiced, + "payment_term": d.description, + "paid": 0.0, + "credit_note": 0.0, + "outstanding": 0.0 + })) + + def allocate_closing_to_term(self, row, term, key): + if row[key]: + if row[key] > term.outstanding: + term[key] = term.outstanding + row[key] -= term.outstanding + else: + term[key] = row[key] + row[key] = 0 + term.outstanding -= term[key] + + def allocate_extra_payments_or_credits(self, row): + # allocate extra payments / credits + additional_row = None + for key in ('paid', 'credit_note'): + if row[key] > 0: + if not additional_row: + additional_row = frappe._dict(row) + additional_row.invoiced = 0.0 + additional_row[key] = row[key] + + if additional_row: + additional_row.outstanding = additional_row.invoiced - additional_row.paid - additional_row.credit_note + self.append_row(additional_row) + + def get_future_payments(self): + if self.filters.show_future_payments: + self.future_payments = frappe._dict() + future_payments = list(self.get_future_payments_from_payment_entry()) + future_payments += list(self.get_future_payments_from_journal_entry()) + if future_payments: + for d in future_payments: + if d.future_amount and d.invoice_no: + self.future_payments.setdefault((d.invoice_no, d.party), []).append(d) + + def get_future_payments_from_payment_entry(self): + return frappe.db.sql(""" + select + ref.reference_name as invoice_no, + payment_entry.party, + payment_entry.party_type, + payment_entry.posting_date as future_date, + ref.allocated_amount as future_amount, + payment_entry.reference_no as future_ref + from + `tabPayment Entry` as payment_entry inner join `tabPayment Entry Reference` as ref + on + (ref.parent = payment_entry.name) + where + payment_entry.docstatus = 1 + and payment_entry.posting_date > %s + and payment_entry.party_type = %s + """, (self.filters.report_date, self.party_type), as_dict=1) + + def get_future_payments_from_journal_entry(self): + if self.filters.get('party'): + amount_field = ("jea.debit_in_account_currency - jea.credit_in_account_currency" + if self.party_type == 'Supplier' else "jea.credit_in_account_currency - jea.debit_in_account_currency") + else: + amount_field = ("jea.debit - " if self.party_type == 'Supplier' else "jea.credit") + + return frappe.db.sql(""" + select + jea.reference_name as invoice_no, + jea.party, + jea.party_type, + je.posting_date as future_date, + sum({0}) as future_amount, + je.cheque_no as future_ref + from + `tabJournal Entry` as je inner join `tabJournal Entry Account` as jea + on + (jea.parent = je.name) + where + je.docstatus = 1 + and je.posting_date > %s + and jea.party_type = %s + and jea.reference_name is not null and jea.reference_name != '' + group by je.name, jea.reference_name + having future_amount > 0 + """.format(amount_field), (self.filters.report_date, self.party_type), as_dict=1) + + def allocate_future_payments(self, row): + # future payments are captured in additional columns + # this method allocates pending future payments against a voucher to + # the current row (which could be generated from payment terms) + if not self.filters.show_future_payments: + return + + row.remaining_balance = row.outstanding + row.future_amount = 0.0 + for future in self.future_payments.get((row.voucher_no, row.party), []): + if row.remaining_balance > 0 and future.future_amount: + if future.future_amount > row.outstanding: + row.future_amount = row.outstanding + future.future_amount = future.future_amount - row.outstanding + row.remaining_balance = 0 + else: + row.future_amount += future.future_amount + future.future_amount = 0 + row.remaining_balance = row.outstanding - row.future_amount + + row.setdefault('future_ref', []).append(cstr(future.future_ref) + '/' + cstr(future.future_date)) + + if row.future_ref: + row.future_ref = ', '.join(row.future_ref) + + def set_ageing(self, row): + if self.filters.ageing_based_on == "Due Date": + entry_date = row.due_date + elif self.filters.ageing_based_on == "Supplier Invoice Date": + entry_date = row.bill_date + else: + entry_date = row.posting_date + + self.get_ageing_data(entry_date, row) + + # ageing buckets should not have amounts if due date is not reached + if getdate(entry_date) > getdate(self.filters.report_date): + row.range1 = row.range2 = row.range3 = row.range4 = row.range5 = 0.0 + + def get_ageing_data(self, entry_date, row): + # [0-30, 30-60, 60-90, 90-120, 120-above] + row.range1 = row.range2 = row.range3 = row.range4 = range5 = 0.0 + + if not (self.age_as_on and entry_date): + return + + row.age = (getdate(self.age_as_on) - getdate(entry_date)).days or 0 + index = None + for i, days in enumerate([self.filters.range1, self.filters.range2, self.filters.range3, self.filters.range4]): + if row.age <= days: + index = i + break + + if index is None: index = 4 + row['range' + str(index+1)] = row.outstanding + + def get_gl_entries(self): + # get all the GL entries filtered by the given filters + + conditions, values = self.prepare_conditions() + + if self.filters.get(scrub(self.party_type)): + select_fields = "debit_in_account_currency as debit, credit_in_account_currency as credit" + else: + select_fields = "debit, credit" self.gl_entries = frappe.db.sql(""" select @@ -477,91 +471,95 @@ class ReceivablePayableReport(object): from `tabGL Entry` where - docstatus < 2 and party_type=%s and (party is not null and party != '') {1} - group by voucher_type, voucher_no, against_voucher_type, against_voucher, party - order by posting_date, party""" + docstatus < 2 + and party_type=%s + and (party is not null and party != '') + and posting_date <= %s + {1} + order by posting_date, party""" .format(select_fields, conditions), values, as_dict=True) - return self.gl_entries - - def prepare_conditions(self, party_type): + def prepare_conditions(self): conditions = [""] - values = [party_type] + values = [self.party_type, self.filters.report_date] + party_type_field = scrub(self.party_type) - party_type_field = scrub(party_type) + self.add_common_filters(conditions, values, party_type_field) + if party_type_field=="customer": + self.add_customer_filters(conditions, values) + + elif party_type_field=="supplier": + self.add_supplier_filters(conditions, values) + + self.add_accounting_dimensions_filters() + + return " and ".join(conditions), values + + def add_common_filters(self, conditions, values, party_type_field): if self.filters.company: conditions.append("company=%s") values.append(self.filters.company) if self.filters.finance_book: - conditions.append("ifnull(finance_book,'') in (%s, '')") + conditions.append("ifnull(finance_book, '') in (%s, '')") values.append(self.filters.finance_book) if self.filters.get(party_type_field): conditions.append("party=%s") values.append(self.filters.get(party_type_field)) - if party_type_field=="customer": - account_type = "Receivable" - if self.filters.get("customer_group"): - lft, rgt = frappe.db.get_value("Customer Group", - self.filters.get("customer_group"), ["lft", "rgt"]) + # get GL with "receivable" or "payable" account_type + account_type = "Receivable" if self.party_type == "Customer" else "Payable" + accounts = [d.name for d in frappe.get_all("Account", + filters={"account_type": account_type, "company": self.filters.company})] + conditions.append("account in (%s)" % ','.join(['%s'] *len(accounts))) + values += accounts - conditions.append("""party in (select name from tabCustomer - where exists(select name from `tabCustomer Group` where lft >= {0} and rgt <= {1} - and name=tabCustomer.customer_group))""".format(lft, rgt)) + def add_customer_filters(self, conditions, values): + if self.filters.get("customer_group"): + conditions.append(self.get_hierarchical_filters('Customer Group', 'customer_group')) - if self.filters.get("territory"): - lft, rgt = frappe.db.get_value("Territory", - self.filters.get("territory"), ["lft", "rgt"]) + if self.filters.get("territory"): + conditions.append(self.get_hierarchical_filters('Territory', 'territory')) - conditions.append("""party in (select name from tabCustomer - where exists(select name from `tabTerritory` where lft >= {0} and rgt <= {1} - and name=tabCustomer.territory))""".format(lft, rgt)) + if self.filters.get("payment_terms_template"): + conditions.append("party in (select name from tabCustomer where payment_terms=%s)") + values.append(self.filters.get("payment_terms_template")) - if self.filters.get("payment_terms_template"): - conditions.append("party in (select name from tabCustomer where payment_terms=%s)") - values.append(self.filters.get("payment_terms_template")) + if self.filters.get("sales_partner"): + conditions.append("party in (select name from tabCustomer where default_sales_partner=%s)") + values.append(self.filters.get("sales_partner")) - if self.filters.get("sales_partner"): - conditions.append("party in (select name from tabCustomer where default_sales_partner=%s)") - values.append(self.filters.get("sales_partner")) + if self.filters.get("sales_person"): + lft, rgt = frappe.db.get_value("Sales Person", + self.filters.get("sales_person"), ["lft", "rgt"]) - if self.filters.get("sales_person"): - lft, rgt = frappe.db.get_value("Sales Person", - self.filters.get("sales_person"), ["lft", "rgt"]) + conditions.append("""exists(select name from `tabSales Team` steam where + steam.sales_person in (select name from `tabSales Person` where lft >= {0} and rgt <= {1}) + and ((steam.parent = voucher_no and steam.parenttype = voucher_type) + or (steam.parent = against_voucher and steam.parenttype = against_voucher_type) + or (steam.parent = party and steam.parenttype = 'Customer')))""".format(lft, rgt)) - conditions.append("""exists(select name from `tabSales Team` steam where - steam.sales_person in (select name from `tabSales Person` where lft >= {0} and rgt <= {1}) - and ((steam.parent = voucher_no and steam.parenttype = voucher_type) - or (steam.parent = against_voucher and steam.parenttype = against_voucher_type) - or (steam.parent = party and steam.parenttype = 'Customer')))""".format(lft, rgt)) + def add_supplier_filters(self, conditions, values): + if self.filters.get("supplier_group"): + conditions.append("""party in (select name from tabSupplier + where supplier_group=%s)""") + values.append(self.filters.get("supplier_group")) - elif party_type_field=="supplier": - account_type = "Payable" - if self.filters.get("supplier_group"): - conditions.append("""party in (select name from tabSupplier - where supplier_group=%s)""") - values.append(self.filters.get("supplier_group")) + if self.filters.get("payment_terms_template"): + conditions.append("party in (select name from tabSupplier where payment_terms=%s)") + values.append(self.filters.get("payment_terms_template")) - if self.filters.get("payment_terms_template"): - conditions.append("party in (select name from tabSupplier where payment_terms=%s)") - values.append(self.filters.get("payment_terms_template")) + def get_hierarchical_filters(self, doctype, key): + lft, rgt = frappe.db.get_value(doctype, self.filters.get(key), ["lft", "rgt"]) - if self.filters.get("cost_center"): - lft, rgt = frappe.get_cached_value("Cost Center", - self.filters.get("cost_center"), ['lft', 'rgt']) - - conditions.append("""cost_center in (select name from `tabCost Center` where - lft >= {0} and rgt <= {1})""".format(lft, rgt)) - - if self.filters.company: - accounts = [d.name for d in frappe.get_all("Account", - filters={"account_type": account_type, "company": self.filters.company})] - conditions.append("account in (%s)" % ','.join(['%s'] *len(accounts))) - values += accounts + return """party in (select name from tabCustomer + where exists(select name from `tab{doctype}` where lft >= {lft} and rgt <= {rgt} + and name=tabCustomer.{key}))""".format( + doctype=doctype, lft=lft, rgt=rgt, key=key) + def add_accounting_dimensions_filters(self, conditions, values): accounting_dimensions = get_accounting_dimensions() if accounting_dimensions: @@ -570,195 +568,133 @@ class ReceivablePayableReport(object): conditions.append("{0} = %s".format(dimension)) values.append(self.filters.get(dimension)) - return " and ".join(conditions), values + def get_gle_balance(self, gle): + # get the balance of the GL (debit - credit) or reverse balance based on report type + return gle.get(self.dr_or_cr) - self.get_reverse_balance(gle) - def get_gl_entries_for(self, party, party_type, against_voucher_type, against_voucher): - if not hasattr(self, "gl_entries_map"): - self.gl_entries_map = {} - for gle in self.get_gl_entries(party_type): - if gle.against_voucher_type and gle.against_voucher: - self.gl_entries_map.setdefault(gle.party, {})\ - .setdefault(gle.against_voucher_type, {})\ - .setdefault(gle.against_voucher, [])\ - .append(gle) + def get_reverse_balance(self, gle): + # get "credit" balance if report type is "debit" and vice versa + return gle.get('debit' if self.dr_or_cr=='credit' else 'credit') - return self.gl_entries_map.get(party, {})\ - .get(against_voucher_type, {})\ - .get(against_voucher, []) + def is_invoice(self, gle): + if gle.voucher_type in ('Sales Invoice', 'Purchase Invoice'): + return True - def get_payment_term_detail(self, voucher_nos): - payment_term_map = frappe._dict() - payment_terms_details = frappe.db.sql(""" select si.name, - party_account_currency, currency, si.conversion_rate, - ps.due_date, ps.payment_amount, ps.description - from `tabSales Invoice` si, `tabPayment Schedule` ps - where si.name = ps.parent and - si.docstatus = 1 and si.company = %s and - si.name in (%s) order by ps.due_date - """ % (frappe.db.escape(self.filters.company), ','.join(['%s'] *len(voucher_nos))), - (tuple(voucher_nos)), as_dict = 1) - - for d in payment_terms_details: - if self.filters.get("customer") and d.currency == d.party_account_currency: - payment_term_amount = d.payment_amount + def get_party_details(self, party): + if not party in self.party_details: + if self.party_type == 'Customer': + self.party_details[party] = frappe.db.get_value('Customer', party, ['customer_name', + 'territory', 'customer_group', 'customer_primary_contact'], as_dict=True) else: - payment_term_amount = flt(flt(d.payment_amount) * flt(d.conversion_rate), self.currency_precision) + self.party_details[party] = frappe.db.get_value('Supplier', party, ['supplier_name', + 'supplier_group'], as_dict=True) - payment_term_map.setdefault(d.name, []).append(frappe._dict({ - "due_date": d.due_date, - "payment_term_amount": payment_term_amount, - "description": d.description - })) - return payment_term_map + return self.party_details[party] - def get_chart_data(self, columns, data): - ageing_columns = columns[self.ageing_col_idx_start : self.ageing_col_idx_start+5] + def get_columns(self): + self.columns = [] + self.add_column('Posting Date', fieldtype='Date') + self.add_column(label=_(self.party_type), fieldname='party', + fieldtype='Link', options=self.party_type, width=180) + + if self.party_naming_by == "Naming Series": + self.add_column(_('{0} Name').format(self.party_type), + fieldname = scrub(self.party_type) + '_name', fieldtype='Data') + + if self.party_type == 'Customer': + self.add_column(_("Customer Contact"), fieldname='customer_primary_contact', + fieldtype='Link', options='Contact') + + self.add_column(label=_('Voucher Type'), fieldname='voucher_type', fieldtype='Data') + self.add_column(label=_('Voucher No'), fieldname='voucher_no', fieldtype='Dynamic Link', + options='voucher_type', width=180) + self.add_column(label='Due Date', fieldtype='Date') + + if self.party_type == "Supplier": + self.add_column(label=_('Bill No'), fieldname='bill_no', fieldtype='Data') + self.add_column(label=_('Bill Date'), fieldname='bill_date', fieldtype='Date') + + if self.filters.based_on_payment_terms: + self.add_column(label=_('Payment Term'), fieldname='payment_term', fieldtype='Data') + self.add_column(label=_('Invoice Grand Total'), fieldname='invoice_grand_total') + + self.add_column(_('Invoiced Amount'), fieldname='invoiced') + self.add_column(_('Paid Amount'), fieldname='paid') + if self.party_type == "Customer": + self.add_column(_('Credit Note'), fieldname='credit_note') + else: + # note: fieldname is still `credit_note` + self.add_column(_('Debit Note'), fieldname='credit_note') + self.add_column(_('Outstanding Amount'), fieldname='outstanding') + + self.setup_ageing_columns() + + self.add_column(label=_('Currency'), fieldname='currency', fieldtype='Link', options='Currency', width=80) + + if self.filters.show_future_payments: + self.add_column(label=_('Future Payment Ref'), fieldname='future_ref', fieldtype='Data') + self.add_column(label=_('Future Payment Amount'), fieldname='future_amount') + self.add_column(label=_('Remaining Balance'), fieldname='remaining_balance') + + if self.filters.party_type == 'Customer': + self.add_column(label=_('Customer LPO'), fieldname='po_no', fieldtype='Data') + + # comma separated list of linked delivery notes + if self.filters.show_delivery_notes: + self.add_column(label=_('Delivery Notes'), fieldname='delivery_notes', fieldtype='Data') + self.add_column(label=_('Territory'), fieldname='territory', fieldtype='Link', + options='Territory') + self.add_column(label=_('Customer Group'), fieldname='customer_group', fieldtype='Link', + options='Customer Group') + if self.filters.show_sales_person: + self.add_column(label=_('Sales Person'), fieldname='sales_person', fieldtype='Data') + + if self.filters.party_type == "Supplier": + self.add_column(label=_('Supplier Group'), fieldname='supplier_group', fieldtype='Link', + options='Supplier Group') + + self.add_column(label=_('Remarks'), fieldname='remarks', fieldtype='Text', width=200) + + def add_column(self, label, fieldname=None, fieldtype='Currency', options=None, width=120): + if not fieldname: fieldname = scrub(label) + if fieldtype=='Currency': options='currency' + if fieldtype=='Date': width = 90 + + self.columns.append(dict( + label=label, + fieldname=fieldname, + fieldtype=fieldtype, + options=options, + width=width + )) + + def setup_ageing_columns(self): + # for charts + self.ageing_column_labels = [] + self.add_column(label=_('Age (Days)'), fieldname='age', fieldtype='Int', width=80) + + for i, label in enumerate(["0-{range1}".format(range1=self.filters["range1"]), + "{range1}-{range2}".format(range1=cint(self.filters["range1"])+ 1, range2=self.filters["range2"]), + "{range2}-{range3}".format(range2=cint(self.filters["range2"])+ 1, range3=self.filters["range3"]), + "{range3}-{range4}".format(range3=cint(self.filters["range3"])+ 1, range4=self.filters["range4"]), + "{range4}-{above}".format(range4=cint(self.filters["range4"])+ 1, above=_("Above"))]): + self.add_column(label=label, fieldname='range' + str(i+1)) + self.ageing_column_labels.append(label) + + def get_chart_data(self): rows = [] - for d in data: - values = d[self.ageing_col_idx_start : self.ageing_col_idx_start+5] - precision = cint(frappe.db.get_default("float_precision")) or 2 - formatted_values = [frappe.utils.rounded(val, precision) for val in values] + for row in self.data: rows.append( { - 'values': formatted_values + 'values': [row.range1, row.range2, row.range3, row.range4, row.range5] } ) - return { + self.chart = { "data": { - 'labels': [d.get("label") for d in ageing_columns], + 'labels': self.ageing_column_labels, 'datasets': rows }, "type": 'percentage' - } - -def execute(filters=None): - args = { - "party_type": "Customer", - "naming_by": ["Selling Settings", "cust_master_name"], - } - return ReceivablePayableReport(filters).run(args) - -def get_ageing_data(first_range, second_range, third_range, - fourth_range, age_as_on, entry_date, outstanding_amount): - # [0-30, 30-60, 60-90, 90-120, 120-above] - outstanding_range = [0.0, 0.0, 0.0, 0.0, 0.0] - - if not (age_as_on and entry_date): - return [0] + outstanding_range - - age = (getdate(age_as_on) - getdate(entry_date)).days or 0 - index = None - for i, days in enumerate([first_range, second_range, third_range, fourth_range]): - if age <= days: - index = i - break - - if index is None: index = 4 - outstanding_range[index] = outstanding_amount - - return [age] + outstanding_range - -def get_pdc_details(party_type, report_date): - pdc_details = frappe._dict() - pdc_via_pe = frappe.db.sql(""" - select - pref.reference_name as invoice_no, pent.party, pent.party_type, - pent.posting_date as pdc_date, ifnull(pref.allocated_amount,0) as pdc_amount, - pent.reference_no as pdc_ref - from - `tabPayment Entry` as pent inner join `tabPayment Entry Reference` as pref - on - (pref.parent = pent.name) - where - pent.docstatus < 2 and pent.posting_date > %s - and pent.party_type = %s - """, (report_date, party_type), as_dict=1) - - for pdc in pdc_via_pe: - pdc_details.setdefault((pdc.invoice_no, pdc.party), []).append(pdc) - - if scrub(party_type): - amount_field = ("jea.debit_in_account_currency" - if party_type == 'Supplier' else "jea.credit_in_account_currency") - else: - amount_field = "jea.debit + jea.credit" - - pdc_via_je = frappe.db.sql(""" - select - jea.reference_name as invoice_no, jea.party, jea.party_type, - je.posting_date as pdc_date, ifnull({0},0) as pdc_amount, - je.cheque_no as pdc_ref - from - `tabJournal Entry` as je inner join `tabJournal Entry Account` as jea - on - (jea.parent = je.name) - where - je.docstatus < 2 and je.posting_date > %s - and jea.party_type = %s - """.format(amount_field), (report_date, party_type), as_dict=1) - - for pdc in pdc_via_je: - pdc_details.setdefault((pdc.invoice_no, pdc.party), []).append(pdc) - - return pdc_details - -def get_dn_details(party_type, voucher_nos): - dn_details = frappe._dict() - - if party_type == "Customer": - for si in frappe.db.sql(""" - select - parent, GROUP_CONCAT(delivery_note SEPARATOR ', ') as dn - from - `tabSales Invoice Item` - where - docstatus=1 and delivery_note is not null and delivery_note != '' - and parent in (%s) group by parent - """ %(','.join(['%s'] * len(voucher_nos))), tuple(voucher_nos) , as_dict=1): - dn_details.setdefault(si.parent, si.dn) - - for si in frappe.db.sql(""" - select - against_sales_invoice as parent, GROUP_CONCAT(parent SEPARATOR ', ') as dn - from - `tabDelivery Note Item` - where - docstatus=1 and against_sales_invoice is not null and against_sales_invoice != '' - and against_sales_invoice in (%s) - group by against_sales_invoice - """ %(','.join(['%s'] * len(voucher_nos))), tuple(voucher_nos) , as_dict=1): - if si.parent in dn_details: - dn_details[si.parent] += ', %s' %(si.dn) - else: - dn_details.setdefault(si.parent, si.dn) - - return dn_details - -def get_voucher_details(party_type, voucher_nos, dn_details): - voucher_details = frappe._dict() - - if party_type == "Customer": - for si in frappe.db.sql(""" - select inv.name, inv.due_date, inv.po_no, GROUP_CONCAT(steam.sales_person SEPARATOR ', ') as sales_person - from `tabSales Invoice` inv - left join `tabSales Team` steam on steam.parent = inv.name and steam.parenttype = 'Sales Invoice' - where inv.docstatus=1 and inv.name in (%s) - group by inv.name - """ %(','.join(['%s'] *len(voucher_nos))), (tuple(voucher_nos)), as_dict=1): - si['delivery_note'] = dn_details.get(si.name) - voucher_details.setdefault(si.name, si) - - if party_type == "Supplier": - for pi in frappe.db.sql("""select name, due_date, bill_no, bill_date - from `tabPurchase Invoice` where docstatus = 1 and name in (%s) - """ %(','.join(['%s'] *len(voucher_nos))), (tuple(voucher_nos)), as_dict=1): - voucher_details.setdefault(pi.name, pi) - - for pi in frappe.db.sql("""select name, due_date, bill_no, bill_date from - `tabJournal Entry` where docstatus = 1 and bill_no is not NULL and name in (%s) - """ %(','.join(['%s'] *len(voucher_nos))), (tuple(voucher_nos)), as_dict=1): - voucher_details.setdefault(pi.name, pi) - - return voucher_details + } \ No newline at end of file diff --git a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py index 43786a4446..f0274b4472 100644 --- a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py @@ -14,33 +14,44 @@ class TestAccountsReceivable(unittest.TestCase): filters = { 'company': '_Test Company 2', - 'based_on_payment_terms': 1 + 'based_on_payment_terms': 1, + 'report_date': today(), + 'range1': 30, + 'range2': 60, + 'range3': 90, + 'range4': 120 } + # check invoice grand total and invoiced column's value for 3 payment terms name = make_sales_invoice() report = execute(filters) - expected_data = [[100,30], [100,50], [100,20]] + expected_data = [[100, 30], [100, 50], [100, 20]] - self.assertEqual(expected_data[0], report[1][0][7:9]) - self.assertEqual(expected_data[1], report[1][1][7:9]) - self.assertEqual(expected_data[2], report[1][2][7:9]) + for i in range(3): + row = report[1][i-1] + self.assertEqual(expected_data[i-1], [row.invoice_grand_total, row.invoiced]) + # check invoice grand total, invoiced, paid and outstanding column's value after payment make_payment(name) report = execute(filters) - expected_data_after_payment = [[100,50], [100,20]] + expected_data_after_payment = [[100, 50, 10, 40], [100, 20, 0, 20]] - self.assertEqual(expected_data_after_payment[0], report[1][0][7:9]) - self.assertEqual(expected_data_after_payment[1], report[1][1][7:9]) + for i in range(2): + row = report[1][i-1] + self.assertEqual(expected_data_after_payment[i-1], + [row.invoice_grand_total, row.invoiced, row.paid, row.outstanding]) + # check invoice grand total, invoiced, paid and outstanding column's value after credit note make_credit_note(name) report = execute(filters) - expected_data_after_credit_note = [[100,100,30,100,-30]] - - self.assertEqual(expected_data_after_credit_note[0], report[1][0][7:12]) + expected_data_after_credit_note = [100, 0, 0, 40, -40] + row = report[1][0] + self.assertEqual(expected_data_after_credit_note, + [row.invoice_grand_total, row.invoiced, row.paid, row.credit_note, row.outstanding]) def make_sales_invoice(): frappe.set_user("Administrator") @@ -64,7 +75,7 @@ def make_sales_invoice(): return si.name def make_payment(docname): - pe = get_payment_entry("Sales Invoice", docname, bank_account="Cash - _TC2", party_amount=30) + pe = get_payment_entry("Sales Invoice", docname, bank_account="Cash - _TC2", party_amount=40) pe.paid_from = "Debtors - _TC2" pe.insert() pe.submit() diff --git a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py index ec24aece5f..350e081957 100644 --- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py +++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py @@ -3,236 +3,11 @@ from __future__ import unicode_literals import frappe -from frappe import _, scrub -from frappe.utils import flt +from frappe import _ +from frappe.utils import flt, cint from erpnext.accounts.party import get_partywise_advanced_payment_amount from erpnext.accounts.report.accounts_receivable.accounts_receivable import ReceivablePayableReport - from six import iteritems -from six.moves import zip - -class AccountsReceivableSummary(ReceivablePayableReport): - def run(self, args): - party_naming_by = frappe.db.get_value(args.get("naming_by")[0], None, args.get("naming_by")[1]) - return self.get_columns(party_naming_by, args), self.get_data(party_naming_by, args) - - def get_columns(self, party_naming_by, args): - columns = [_(args.get("party_type")) + ":Link/" + args.get("party_type") + ":200"] - - if party_naming_by == "Naming Series": - columns += [ args.get("party_type") + " Name::140"] - - credit_debit_label = "Credit Note Amt" if args.get('party_type') == 'Customer' else "Debit Note Amt" - - columns += [{ - "label": _("Advance Amount"), - "fieldname": "advance_amount", - "fieldtype": "Currency", - "options": "currency", - "width": 100 - },{ - "label": _("Total Invoiced Amt"), - "fieldname": "total_invoiced_amt", - "fieldtype": "Currency", - "options": "currency", - "width": 100 - }, - { - "label": _("Total Paid Amt"), - "fieldname": "total_paid_amt", - "fieldtype": "Currency", - "options": "currency", - "width": 100 - }] - - columns += [ - { - "label": _(credit_debit_label), - "fieldname": scrub(credit_debit_label), - "fieldtype": "Currency", - "options": "currency", - "width": 140 - }, - { - "label": _("Total Outstanding Amt"), - "fieldname": "total_outstanding_amt", - "fieldtype": "Currency", - "options": "currency", - "width": 160 - }, - { - "label": _("0-" + str(self.filters.range1)), - "fieldname": scrub("0-" + str(self.filters.range1)), - "fieldtype": "Currency", - "options": "currency", - "width": 160 - }, - { - "label": _(str(self.filters.range1) + "-" + str(self.filters.range2)), - "fieldname": scrub(str(self.filters.range1) + "-" + str(self.filters.range2)), - "fieldtype": "Currency", - "options": "currency", - "width": 160 - }, - { - "label": _(str(self.filters.range2) + "-" + str(self.filters.range3)), - "fieldname": scrub(str(self.filters.range2) + "-" + str(self.filters.range3)), - "fieldtype": "Currency", - "options": "currency", - "width": 160 - }, - { - "label": _(str(self.filters.range3) + "-" + str(self.filters.range4)), - "fieldname": scrub(str(self.filters.range3) + "-" + str(self.filters.range4)), - "fieldtype": "Currency", - "options": "currency", - "width": 160 - }, - { - "label": _(str(self.filters.range4) + _("-Above")), - "fieldname": scrub(str(self.filters.range4) + _("-Above")), - "fieldtype": "Currency", - "options": "currency", - "width": 160 - } - ] - - if args.get("party_type") == "Customer": - columns += [{ - "label": _("Territory"), - "fieldname": "territory", - "fieldtype": "Link", - "options": "Territory", - "width": 80 - }, - { - "label": _("Customer Group"), - "fieldname": "customer_group", - "fieldtype": "Link", - "options": "Customer Group", - "width": 80 - }, - { - "label": _("Sales Person"), - "fieldtype": "Data", - "fieldname": "sales_person", - "width": 120, - }] - - if args.get("party_type") == "Supplier": - columns += [{ - "label": _("Supplier Group"), - "fieldname": "supplier_group", - "fieldtype": "Link", - "options": "Supplier Group", - "width": 80 - }] - - columns.append({ - "fieldname": "currency", - "label": _("Currency"), - "fieldtype": "Link", - "options": "Currency", - "width": 80 - }) - - return columns - - def get_data(self, party_naming_by, args): - data = [] - - partywise_total = self.get_partywise_total(party_naming_by, args) - - partywise_advance_amount = get_partywise_advanced_payment_amount(args.get("party_type"), - self.filters.get("report_date")) or {} - for party, party_dict in iteritems(partywise_total): - row = [party] - - if party_naming_by == "Naming Series": - row += [self.get_party_name(args.get("party_type"), party)] - - row += [partywise_advance_amount.get(party, 0)] - - paid_amt = 0 - if party_dict.paid_amt > 0: - paid_amt = flt(party_dict.paid_amt - partywise_advance_amount.get(party, 0)) - - row += [ - party_dict.invoiced_amt, paid_amt, party_dict.credit_amt, party_dict.outstanding_amt, - party_dict.range1, party_dict.range2, party_dict.range3, party_dict.range4, party_dict.range5 - ] - - if args.get("party_type") == "Customer": - row += [self.get_territory(party), self.get_customer_group(party), ", ".join(set(party_dict.sales_person))] - if args.get("party_type") == "Supplier": - row += [self.get_supplier_group(party)] - - row.append(party_dict.currency) - data.append(row) - - return data - - def get_partywise_total(self, party_naming_by, args): - party_total = frappe._dict() - for d in self.get_voucherwise_data(party_naming_by, args): - party_total.setdefault(d.party, - frappe._dict({ - "invoiced_amt": 0, - "paid_amt": 0, - "credit_amt": 0, - "outstanding_amt": 0, - "range1": 0, - "range2": 0, - "range3": 0, - "range4": 0, - "range5": 0, - "sales_person": [] - }) - ) - for k in list(party_total[d.party]): - if k not in ["currency", "sales_person"]: - party_total[d.party][k] += flt(d.get(k, 0)) - - party_total[d.party].currency = d.currency - - if d.sales_person: - party_total[d.party].sales_person.append(d.sales_person) - - return party_total - - def get_voucherwise_data(self, party_naming_by, args): - voucherwise_data = ReceivablePayableReport(self.filters).run(args)[1] - - cols = ["posting_date", "party"] - - if party_naming_by == "Naming Series": - cols += ["party_name"] - - if args.get("party_type") == 'Customer': - cols += ["contact"] - - cols += ["voucher_type", "voucher_no", "due_date"] - - if args.get("party_type") == "Supplier": - cols += ["bill_no", "bill_date"] - - cols += ["invoiced_amt", "paid_amt", "credit_amt", - "outstanding_amt", "age", "range1", "range2", "range3", "range4", "range5", "currency", "pdc/lc_date", "pdc/lc_ref", - "pdc/lc_amount"] - - if args.get("party_type") == "Supplier": - cols += ["supplier_group", "remarks"] - if args.get("party_type") == "Customer": - cols += ["po_no", "do_no", "territory", "customer_group", "sales_person", "remarks"] - - return self.make_data_dict(cols, voucherwise_data) - - def make_data_dict(self, cols, data): - data_dict = [] - for d in data: - data_dict.append(frappe._dict(zip(cols, d))) - - return data_dict def execute(filters=None): args = { @@ -241,3 +16,119 @@ def execute(filters=None): } return AccountsReceivableSummary(filters).run(args) + +class AccountsReceivableSummary(ReceivablePayableReport): + def run(self, args): + self.party_type = args.get('party_type') + self.party_naming_by = frappe.db.get_value(args.get("naming_by")[0], None, args.get("naming_by")[1]) + self.get_columns() + self.get_data(args) + return self.columns, self.data + + def get_data(self, args): + self.data = [] + + self.receivables = ReceivablePayableReport(self.filters).run(args)[1] + + self.get_party_total(args) + + party_advance_amount = get_partywise_advanced_payment_amount(self.party_type, + self.filters.report_date) or {} + + for party, party_dict in iteritems(self.party_total): + row = frappe._dict() + + row.party = party + if self.party_naming_by == "Naming Series": + row.party_name = frappe.get_cached_value(self.party_type, party, [self.party_type + "_name"]) + + row.update(party_dict) + + # Advance against party + row.advance = party_advance_amount.get(party, 0) + + # In AR/AP, advance shown in paid columns, + # but in summary report advance shown in separate column + row.paid -= row.advance + + self.data.append(row) + + def get_party_total(self, args): + self.party_total = frappe._dict() + + for d in self.receivables: + self.init_party_total(d) + + # Add all amount columns + for k in list(self.party_total[d.party]): + if k not in ["currency", "sales_person"]: + + self.party_total[d.party][k] += d.get(k, 0.0) + + # set territory, customer_group, sales person etc + self.set_party_details(d) + + def init_party_total(self, row): + self.party_total.setdefault(row.party, frappe._dict({ + "invoiced": 0.0, + "paid": 0.0, + "credit_note": 0.0, + "outstanding": 0.0, + "range1": 0.0, + "range2": 0.0, + "range3": 0.0, + "range4": 0.0, + "range5": 0.0, + "sales_person": [] + })) + + def set_party_details(self, row): + self.party_total[row.party].currency = row.currency + + for key in ('territory', 'customer_group', 'supplier_group'): + if row.get(key): + self.party_total[row.party][key] = row.get(key) + + if row.sales_person: + self.party_total[row.party].sales_person.append(row.sales_person) + + def get_columns(self): + self.columns = [] + self.add_column(label=_(self.party_type), fieldname='party', + fieldtype='Link', options=self.party_type, width=180) + + if self.party_naming_by == "Naming Series": + self.add_column(_('{0} Name').format(self.party_type), + fieldname = 'party_name', fieldtype='Data') + + credit_debit_label = "Credit Note" if self.party_type == 'Customer' else "Debit Note" + + self.add_column(_('Advance Amount'), fieldname='advance') + self.add_column(_('Invoiced Amount'), fieldname='invoiced') + self.add_column(_('Paid Amount'), fieldname='paid') + self.add_column(_(credit_debit_label), fieldname='credit_note') + self.add_column(_('Outstanding Amount'), fieldname='outstanding') + + self.setup_ageing_columns() + + if self.party_type == "Customer": + self.add_column(label=_('Territory'), fieldname='territory', fieldtype='Link', + options='Territory') + self.add_column(label=_('Customer Group'), fieldname='customer_group', fieldtype='Link', + options='Customer Group') + if self.filters.show_sales_person: + self.add_column(label=_('Sales Person'), fieldname='sales_person', fieldtype='Data') + else: + self.add_column(label=_('Supplier Group'), fieldname='supplier_group', fieldtype='Link', + options='Supplier Group') + + self.add_column(label=_('Currency'), fieldname='currency', fieldtype='Link', + options='Currency', width=80) + + def setup_ageing_columns(self): + for i, label in enumerate(["0-{range1}".format(range1=self.filters["range1"]), + "{range1}-{range2}".format(range1=cint(self.filters["range1"])+ 1, range2=self.filters["range2"]), + "{range2}-{range3}".format(range2=cint(self.filters["range2"])+ 1, range3=self.filters["range3"]), + "{range3}-{range4}".format(range3=cint(self.filters["range3"])+ 1, range4=self.filters["range4"]), + "{range4}-{above}".format(range4=cint(self.filters["range4"])+ 1, above=_("Above"))]): + self.add_column(label=label, fieldname='range' + str(i+1)) \ No newline at end of file diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.py b/erpnext/accounts/report/balance_sheet/balance_sheet.py index 7d9acf26ed..97ce4f21e8 100644 --- a/erpnext/accounts/report/balance_sheet/balance_sheet.py +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.py @@ -135,11 +135,11 @@ def get_chart_data(filters, columns, asset, liability, equity): datasets = [] if asset_data: - datasets.append({'name':'Assets', 'values': asset_data}) + datasets.append({'name': _('Assets'), 'values': asset_data}) if liability_data: - datasets.append({'name':'Liabilities', 'values': liability_data}) + datasets.append({'name': _('Liabilities'), 'values': liability_data}) if equity_data: - datasets.append({'name':'Equity', 'values': equity_data}) + datasets.append({'name': _('Equity'), 'values': equity_data}) chart = { "data": { diff --git a/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.js b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.js index ca243944e9..2343eaa846 100644 --- a/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.js +++ b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.js @@ -27,8 +27,8 @@ frappe.query_reports["Payment Period Based On Invoice Date"] = { fieldname:"payment_type", label: __("Payment Type"), fieldtype: "Select", - options: "Incoming\nOutgoing", - default: "Incoming" + options: __("Incoming") + "\n" + __("Outgoing"), + default: __("Incoming") }, { "fieldname":"party_type", diff --git a/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py index 89e0113fc8..24b5d87b5b 100644 --- a/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py +++ b/erpnext/accounts/report/payment_period_based_on_invoice_date/payment_period_based_on_invoice_date.py @@ -39,8 +39,8 @@ def execute(filters=None): return columns, data def validate_filters(filters): - if (filters.get("payment_type") == "Incoming" and filters.get("party_type") == "Supplier") or \ - (filters.get("payment_type") == "Outgoing" and filters.get("party_type") == "Customer"): + if (filters.get("payment_type") == _("Incoming") and filters.get("party_type") == "Supplier") or \ + (filters.get("payment_type") == _("Outgoing") and filters.get("party_type") == "Customer"): frappe.throw(_("{0} payment entries can not be filtered by {1}")\ .format(filters.payment_type, filters.party_type)) @@ -51,7 +51,7 @@ def get_columns(filters): _("Party Type") + "::100", _("Party") + ":Dynamic Link/Party Type:140", _("Posting Date") + ":Date:100", - _("Invoice") + (":Link/Purchase Invoice:130" if filters.get("payment_type") == "Outgoing" else ":Link/Sales Invoice:130"), + _("Invoice") + (":Link/Purchase Invoice:130" if filters.get("payment_type") == _("Outgoing") else ":Link/Sales Invoice:130"), _("Invoice Posting Date") + ":Date:130", _("Payment Due Date") + ":Date:130", _("Debit") + ":Currency:120", @@ -69,7 +69,7 @@ def get_conditions(filters): conditions = [] if not filters.party_type: - if filters.payment_type == "Outgoing": + if filters.payment_type == _("Outgoing"): filters.party_type = "Supplier" else: filters.party_type = "Customer" @@ -101,7 +101,7 @@ def get_entries(filters): def get_invoice_posting_date_map(filters): invoice_details = {} - dt = "Sales Invoice" if filters.get("payment_type") == "Incoming" else "Purchase Invoice" + dt = "Sales Invoice" if filters.get("payment_type") == _("Incoming") else "Purchase Invoice" for t in frappe.db.sql("select name, posting_date, due_date from `tab{0}`".format(dt), as_dict=1): invoice_details[t.name] = t diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py index ac11868cab..d500c8116e 100644 --- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py @@ -75,11 +75,11 @@ def get_chart_data(filters, columns, income, expense, net_profit_loss): datasets = [] if income_data: - datasets.append({'name': 'Income', 'values': income_data}) + datasets.append({'name': _('Income'), 'values': income_data}) if expense_data: - datasets.append({'name': 'Expense', 'values': expense_data}) + datasets.append({'name': _('Expense'), 'values': expense_data}) if net_profit: - datasets.append({'name': 'Net Profit/Loss', 'values': net_profit}) + datasets.append({'name': _('Net Profit/Loss'), 'values': net_profit}) chart = { "data": { diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 45d2ec2c51..ab37e91538 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -255,9 +255,15 @@ class Asset(AccountsController): precision = self.precision("gross_purchase_amount") if row.depreciation_method in ("Straight Line", "Manual"): + depreciation_left = (cint(row.total_number_of_depreciations) - cint(self.number_of_depreciations_booked)) + + if not depreciation_left: + frappe.msgprint(_("All the depreciations has been booked")) + depreciation_amount = flt(row.expected_value_after_useful_life) + return depreciation_amount + depreciation_amount = (flt(row.value_after_depreciation) - - flt(row.expected_value_after_useful_life)) / (cint(row.total_number_of_depreciations) - - cint(self.number_of_depreciations_booked)) + flt(row.expected_value_after_useful_life)) / depreciation_left else: depreciation_amount = flt(depreciable_value * (flt(row.rate_of_depreciation) / 100), precision) @@ -275,7 +281,7 @@ class Asset(AccountsController): flt(accumulated_depreciation_after_full_schedule), self.precision('gross_purchase_amount')) - if (row.expected_value_after_useful_life and + if (row.expected_value_after_useful_life and row.expected_value_after_useful_life < asset_value_after_full_schedule): frappe.throw(_("Depreciation Row {0}: Expected value after useful life must be greater than or equal to {1}") .format(row.idx, asset_value_after_full_schedule)) diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index 481ee7d9f4..c09b94fa8e 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -456,8 +456,6 @@ class TestAsset(unittest.TestCase): self.assertEqual(gle, expected_gle) si.cancel() - frappe.delete_doc("Sales Invoice", si.name) - self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Partially Depreciated") def test_asset_expected_value_after_useful_life(self): diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.js b/erpnext/buying/doctype/buying_settings/buying_settings.js new file mode 100644 index 0000000000..403b1c95e7 --- /dev/null +++ b/erpnext/buying/doctype/buying_settings/buying_settings.js @@ -0,0 +1,8 @@ +// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Buying Settings', { + // refresh: function(frm) { + + // } +}); diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.json b/erpnext/buying/doctype/buying_settings/buying_settings.json index f2eec4f722..a492519591 100644 --- a/erpnext/buying/doctype/buying_settings/buying_settings.json +++ b/erpnext/buying/doctype/buying_settings/buying_settings.json @@ -1,379 +1,111 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2013-06-25 11:04:03", - "custom": 0, - "description": "Settings for Buying Module", - "docstatus": 0, - "doctype": "DocType", - "document_type": "Other", - "editable_grid": 0, + "creation": "2013-06-25 11:04:03", + "description": "Settings for Buying Module", + "doctype": "DocType", + "document_type": "Other", + "field_order": [ + "supp_master_name", + "supplier_group", + "buying_price_list", + "column_break_3", + "po_required", + "pr_required", + "maintain_same_rate", + "allow_multiple_items", + "subcontract", + "backflush_raw_materials_of_subcontract_based_on", + "column_break_11", + "over_transfer_allowance" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Supplier Name", - "fieldname": "supp_master_name", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Supplier Naming By", - "length": 0, - "no_copy": 0, - "options": "Supplier Name\nNaming Series", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "supplier_group", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Default Supplier Group", - "length": 0, - "no_copy": 0, - "options": "Supplier Group", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "buying_price_list", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Default Buying Price List", - "length": 0, - "no_copy": 0, - "options": "Price List", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "po_required", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Purchase Order Required", - "length": 0, - "no_copy": 0, - "options": "No\nYes", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "pr_required", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Purchase Receipt Required", - "length": 0, - "no_copy": 0, - "options": "No\nYes", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "maintain_same_rate", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Maintain same rate throughout purchase cycle", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "allow_multiple_items", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Allow Item to be added multiple times in a transaction", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "default": "Supplier Name", + "fieldname": "supp_master_name", + "fieldtype": "Select", + "label": "Supplier Naming By", + "options": "Supplier Name\nNaming Series" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "subcontract", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Subcontract", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "supplier_group", + "fieldtype": "Link", + "label": "Default Supplier Group", + "options": "Supplier Group" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Material Transferred for Subcontract", - "fieldname": "backflush_raw_materials_of_subcontract_based_on", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Backflush Raw Materials of Subcontract Based On", - "length": 0, - "no_copy": 0, - "options": "BOM\nMaterial Transferred for Subcontract", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "buying_price_list", + "fieldtype": "Link", + "label": "Default Buying Price List", + "options": "Price List" + }, + { + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, + { + "fieldname": "po_required", + "fieldtype": "Select", + "label": "Purchase Order Required", + "options": "No\nYes" + }, + { + "fieldname": "pr_required", + "fieldtype": "Select", + "label": "Purchase Receipt Required", + "options": "No\nYes" + }, + { + "default": "0", + "fieldname": "maintain_same_rate", + "fieldtype": "Check", + "label": "Maintain same rate throughout purchase cycle" + }, + { + "default": "0", + "fieldname": "allow_multiple_items", + "fieldtype": "Check", + "label": "Allow Item to be added multiple times in a transaction" + }, + { + "fieldname": "subcontract", + "fieldtype": "Section Break", + "label": "Subcontract" + }, + { + "default": "Material Transferred for Subcontract", + "fieldname": "backflush_raw_materials_of_subcontract_based_on", + "fieldtype": "Select", + "label": "Backflush Raw Materials of Subcontract Based On", + "options": "BOM\nMaterial Transferred for Subcontract" + }, + { + "depends_on": "eval:doc.backflush_raw_materials_of_subcontract_based_on == \"BOM\"", + "description": "Percentage you are allowed to transfer more against the quantity ordered. For example: If you have ordered 100 units. and your Allowance is 10% then you are allowed to transfer 110 units.", + "fieldname": "over_transfer_allowance", + "fieldtype": "Float", + "label": "Over Transfer Allowance (%)" + }, + { + "fieldname": "column_break_11", + "fieldtype": "Column Break" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-cog", - "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 1, - "istable": 0, - "max_attachments": 0, - "modified": "2018-07-31 07:52:38.062488", - "modified_by": "Administrator", - "module": "Buying", - "name": "Buying Settings", - "owner": "Administrator", + ], + "icon": "fa fa-cog", + "idx": 1, + "issingle": 1, + "modified": "2019-08-20 13:13:09.055189", + "modified_by": "Administrator", + "module": "Buying", + "name": "Buying Settings", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 0, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 0, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "track_changes": 0, - "track_seen": 0 -} + ] +} \ No newline at end of file diff --git a/erpnext/buying/doctype/buying_settings/test_buying_settings.py b/erpnext/buying/doctype/buying_settings/test_buying_settings.py new file mode 100644 index 0000000000..bf6eec67d4 --- /dev/null +++ b/erpnext/buying/doctype/buying_settings/test_buying_settings.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +# import frappe +import unittest + +class TestBuyingSettings(unittest.TestCase): + pass diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index 6ed6766288..e3e2f1edde 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -477,6 +477,7 @@ def make_rm_stock_entry(purchase_order, rm_items): rm_item_code = rm_item_data["rm_item_code"] items_dict = { rm_item_code: { + "po_detail": rm_item_data.get("name"), "item_name": rm_item_data["item_name"], "description": item_wh.get(rm_item_code, {}).get('description', ""), 'qty': rm_item_data["qty"], diff --git a/erpnext/buying/doctype/purchase_order_item_supplied/purchase_order_item_supplied.json b/erpnext/buying/doctype/purchase_order_item_supplied/purchase_order_item_supplied.json index 7016853058..8435bbb06e 100644 --- a/erpnext/buying/doctype/purchase_order_item_supplied/purchase_order_item_supplied.json +++ b/erpnext/buying/doctype/purchase_order_item_supplied/purchase_order_item_supplied.json @@ -1,404 +1,134 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2013-02-22 01:27:42", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "editable_grid": 1, + "creation": "2013-02-22 01:27:42", + "doctype": "DocType", + "editable_grid": 1, + "field_order": [ + "main_item_code", + "rm_item_code", + "required_qty", + "supplied_qty", + "rate", + "amount", + "column_break_6", + "bom_detail_no", + "reference_name", + "conversion_factor", + "stock_uom", + "reserve_warehouse" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "main_item_code", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Item Code", - "length": 0, - "no_copy": 0, - "oldfieldname": "main_item_code", - "oldfieldtype": "Data", - "options": "Item", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "columns": 2, + "fieldname": "main_item_code", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Item Code", + "oldfieldname": "main_item_code", + "oldfieldtype": "Data", + "options": "Item", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "rm_item_code", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Raw Material Item Code", - "length": 0, - "no_copy": 0, - "oldfieldname": "rm_item_code", - "oldfieldtype": "Data", - "options": "Item", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "columns": 2, + "fieldname": "rm_item_code", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Raw Material Item Code", + "oldfieldname": "rm_item_code", + "oldfieldtype": "Data", + "options": "Item", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "required_qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Supplied Qty", - "length": 0, - "no_copy": 0, - "oldfieldname": "required_qty", - "oldfieldtype": "Currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "columns": 2, + "fieldname": "required_qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Required Qty", + "oldfieldname": "required_qty", + "oldfieldtype": "Currency", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "rate", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Rate", - "length": 0, - "no_copy": 0, - "oldfieldname": "rate", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "columns": 2, + "fieldname": "rate", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Rate", + "oldfieldname": "rate", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amount", - "length": 0, - "no_copy": 0, - "oldfieldname": "amount", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "amount", + "fieldtype": "Currency", + "label": "Amount", + "oldfieldname": "amount", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_6", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_6", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "bom_detail_no", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "BOM Detail No", - "length": 0, - "no_copy": 0, - "oldfieldname": "bom_detail_no", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "bom_detail_no", + "fieldtype": "Data", + "label": "BOM Detail No", + "oldfieldname": "bom_detail_no", + "oldfieldtype": "Data", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "reference_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Reference Name", - "length": 0, - "no_copy": 0, - "oldfieldname": "reference_name", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "reference_name", + "fieldtype": "Data", + "label": "Reference Name", + "oldfieldname": "reference_name", + "oldfieldtype": "Data", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "conversion_factor", - "fieldtype": "Float", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Conversion Factor", - "length": 0, - "no_copy": 0, - "oldfieldname": "conversion_factor", - "oldfieldtype": "Currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "conversion_factor", + "fieldtype": "Float", + "hidden": 1, + "label": "Conversion Factor", + "oldfieldname": "conversion_factor", + "oldfieldtype": "Currency", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "stock_uom", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Stock Uom", - "length": 0, - "no_copy": 0, - "oldfieldname": "stock_uom", - "oldfieldtype": "Data", - "options": "UOM", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "stock_uom", + "fieldtype": "Link", + "label": "Stock Uom", + "oldfieldname": "stock_uom", + "oldfieldtype": "Data", + "options": "UOM", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 2, - "fieldname": "reserve_warehouse", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Reserve Warehouse", - "length": 0, - "no_copy": 0, - "options": "Warehouse", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "columns": 2, + "fieldname": "reserve_warehouse", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Reserve Warehouse", + "options": "Warehouse" + }, + { + "fieldname": "supplied_qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Supplied Qty", + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 1, - "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2019-01-07 16:51:58.016007", - "modified_by": "Administrator", - "module": "Buying", - "name": "Purchase Order Item Supplied", - "owner": "dhanalekshmi@webnotestech.com", - "permissions": [], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + ], + "hide_toolbar": 1, + "idx": 1, + "istable": 1, + "modified": "2019-08-20 13:37:32.702068", + "modified_by": "Administrator", + "module": "Buying", + "name": "Purchase Order Item Supplied", + "owner": "dhanalekshmi@webnotestech.com", + "permissions": [] } \ No newline at end of file diff --git a/erpnext/change_log/v12/v12_1_0.md b/erpnext/change_log/v12/v12_1_0.md new file mode 100644 index 0000000000..aed09c7e39 --- /dev/null +++ b/erpnext/change_log/v12/v12_1_0.md @@ -0,0 +1,6 @@ +# Version 12.1.0 Release Notes + +### Stock + +1. [Pick List](https://erpnext.com/docs/user/manual/en/stock/pick-list) +2. [Refactored Accounts Receivable Reports](https://erpnext.com/docs/user/manual/en/accounts/accounting-reports#2-accounting-statements) diff --git a/erpnext/communication/doctype/call_log/call_log.py b/erpnext/communication/doctype/call_log/call_log.py index 2343632719..35c31a0bf8 100644 --- a/erpnext/communication/doctype/call_log/call_log.py +++ b/erpnext/communication/doctype/call_log/call_log.py @@ -73,6 +73,10 @@ def set_caller_information(doc, state): # contact_name or lead_name display_name_field = '{}_name'.format(fieldname) + # Contact now has all the nos saved in child table + if doc.doctype == 'Contact': + numbers = [d.phone for d in doc.phone_nos] + for number in numbers: number = strip_number(number) if not number: continue diff --git a/erpnext/config/buying.py b/erpnext/config/buying.py index f5d8da74c5..6f5ab32b63 100644 --- a/erpnext/config/buying.py +++ b/erpnext/config/buying.py @@ -14,6 +14,12 @@ def get_data(): "dependencies": ["Item", "Supplier"], "description": _("Purchase Orders given to Suppliers."), }, + { + "type": "doctype", + "name": "Purchase Invoice", + "onboard": 1, + "dependencies": ["Item", "Supplier"] + }, { "type": "doctype", "name": "Material Request", diff --git a/erpnext/config/crm.py b/erpnext/config/crm.py index 70784f3d5f..eba6c7a02a 100644 --- a/erpnext/config/crm.py +++ b/erpnext/config/crm.py @@ -41,6 +41,11 @@ def get_data(): "name": "Lead Source", "description": _("Track Leads by Lead Source.") }, + { + "type": "doctype", + "name": "Contract", + "description": _("Helps you keep tracks of Contracts based on Supplier, Customer and Employee"), + }, ] }, { diff --git a/erpnext/config/selling.py b/erpnext/config/selling.py index 844710d47c..928bd5fa32 100644 --- a/erpnext/config/selling.py +++ b/erpnext/config/selling.py @@ -304,12 +304,6 @@ def get_data(): "name": "Customers Without Any Sales Transactions", "doctype": "Customer" }, - { - "type": "report", - "is_query_report": True, - "name": "Sales Partners Commission", - "doctype": "Customer" - }, { "type": "report", "is_query_report": True, diff --git a/erpnext/config/stock.py b/erpnext/config/stock.py index 7d66df2360..441a3ab4ec 100644 --- a/erpnext/config/stock.py +++ b/erpnext/config/stock.py @@ -30,6 +30,12 @@ def get_data(): "onboard": 1, "dependencies": ["Item"], }, + { + "type": "doctype", + "name": "Pick List", + "onboard": 1, + "dependencies": ["Item"], + }, { "type": "doctype", "name": "Delivery Trip" @@ -329,5 +335,5 @@ def get_data(): } ] }, - + ] diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 14bf9d57a1..f6d4eee518 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -263,7 +263,7 @@ class AccountsController(TransactionBase): if self.get("is_subcontracted"): args["is_subcontracted"] = self.is_subcontracted - ret = get_item_details(args, self) + ret = get_item_details(args, self, overwrite_warehouse=False) for fieldname, value in ret.items(): if item.meta.get_field(fieldname) and value is not None: diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 2cbe596770..9dbd5be918 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -45,6 +45,7 @@ class SellingController(StockController): self.set_gross_profit() set_default_income_account_for_item(self) self.set_customer_address() + self.validate_for_duplicate_items() def set_missing_values(self, for_validate=False): @@ -381,6 +382,34 @@ class SellingController(StockController): if self.get(address_field): self.set(address_display_field, get_address_display(self.get(address_field))) + def validate_for_duplicate_items(self): + check_list, chk_dupl_itm = [], [] + if cint(frappe.db.get_single_value("Selling Settings", "allow_multiple_items")): + return + + for d in self.get('items'): + if self.doctype == "Sales Invoice": + e = [d.item_code, d.description, d.warehouse, d.sales_order or d.delivery_note, d.batch_no or ''] + f = [d.item_code, d.description, d.sales_order or d.delivery_note] + elif self.doctype == "Delivery Note": + e = [d.item_code, d.description, d.warehouse, d.against_sales_order or d.against_sales_invoice, d.batch_no or ''] + f = [d.item_code, d.description, d.against_sales_order or d.against_sales_invoice] + elif self.doctype in ["Sales Order", "Quotation"]: + e = [d.item_code, d.description, d.warehouse, ''] + f = [d.item_code, d.description] + + if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1: + if e in check_list: + frappe.throw(_("Note: Item {0} entered multiple times").format(d.item_code)) + else: + check_list.append(e) + else: + if f in chk_dupl_itm: + frappe.throw(_("Note: Item {0} entered multiple times").format(d.item_code)) + else: + chk_dupl_itm.append(f) + + def validate_items(self): # validate items to see if they have is_sales_item enabled from erpnext.controllers.buying_controller import validate_item_type diff --git a/erpnext/crm/doctype/contract/contract.py b/erpnext/crm/doctype/contract/contract.py index 64cc97b503..18444cc7e6 100644 --- a/erpnext/crm/doctype/contract/contract.py +++ b/erpnext/crm/doctype/contract/contract.py @@ -88,7 +88,7 @@ def get_status(start_date, end_date): end_date = getdate(end_date) now_date = getdate(nowdate()) - return "Active" if start_date < now_date < end_date else "Inactive" + return "Active" if start_date <= now_date <= end_date else "Inactive" def update_status_for_contracts(): diff --git a/erpnext/crm/doctype/opportunity/test_opportunity.py b/erpnext/crm/doctype/opportunity/test_opportunity.py index 1a9f66ad9a..8f61edf00e 100644 --- a/erpnext/crm/doctype/opportunity/test_opportunity.py +++ b/erpnext/crm/doctype/opportunity/test_opportunity.py @@ -45,15 +45,16 @@ class TestOpportunity(unittest.TestCase): # create new customer and create new contact against 'new.opportunity@example.com' customer = make_customer(opp_doc.party_name).insert(ignore_permissions=True) - frappe.get_doc({ + contact = frappe.get_doc({ "doctype": "Contact", - "email_id": new_lead_email_id, "first_name": "_Test Opportunity Customer", "links": [{ "link_doctype": "Customer", "link_name": customer.name }] - }).insert(ignore_permissions=True) + }) + contact.add_email(new_lead_email_id) + contact.insert(ignore_permissions=True) opp_doc = frappe.get_doc(args).insert(ignore_permissions=True) self.assertTrue(opp_doc.party_name) diff --git a/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py b/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py index 77de84ce5c..6a846efad7 100644 --- a/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py +++ b/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py @@ -3,7 +3,6 @@ # For license information, please see license.txt from __future__ import unicode_literals -# import frappe from frappe.model.document import Document import requests import frappe diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py index 7f4b7bab0a..4943053663 100644 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py +++ b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py @@ -58,7 +58,7 @@ class ShopifySettings(Document): d.raise_for_status() self.update_webhook_table(method, d.json()) except Exception as e: - make_shopify_log(status="Warning", message=e.message, exception=False) + make_shopify_log(status="Warning", message=e, exception=False) def unregister_webhooks(self): session = get_request_session() @@ -71,7 +71,7 @@ class ShopifySettings(Document): res.raise_for_status() deleted_webhooks.append(d) except Exception as e: - frappe.log_error(message=frappe.get_traceback(), title=e.message[:140]) + frappe.log_error(message=frappe.get_traceback(), title=e) for d in deleted_webhooks: self.remove(d) diff --git a/erpnext/hr/doctype/expense_claim/test_expense_claim.py b/erpnext/hr/doctype/expense_claim/test_expense_claim.py index 6618a4f7c5..b559dfd81d 100644 --- a/erpnext/hr/doctype/expense_claim/test_expense_claim.py +++ b/erpnext/hr/doctype/expense_claim/test_expense_claim.py @@ -45,7 +45,6 @@ class TestExpenseClaim(unittest.TestCase): self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 700) expense_claim2.cancel() - frappe.delete_doc("Expense Claim", expense_claim2.name) self.assertEqual(frappe.db.get_value("Task", task_name, "total_expense_claim"), 200) self.assertEqual(frappe.db.get_value("Project", "_Test Project 1", "total_expense_claim"), 200) diff --git a/erpnext/hr/doctype/leave_application/leave_application_list.js b/erpnext/hr/doctype/leave_application/leave_application_list.js index f69b182737..cbb4b73227 100644 --- a/erpnext/hr/doctype/leave_application/leave_application_list.js +++ b/erpnext/hr/doctype/leave_application/leave_application_list.js @@ -5,6 +5,8 @@ frappe.listview_settings['Leave Application'] = { return [__("Approved"), "green", "status,=,Approved"]; } else if (doc.status === "Rejected") { return [__("Rejected"), "red", "status,=,Rejected"]; + } else { + return [__("Open"), "red", "status,=,Open"]; } } }; diff --git a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py index c82114e6d5..bfc6d95d17 100644 --- a/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py +++ b/erpnext/hr/doctype/leave_ledger_entry/leave_ledger_entry.py @@ -95,26 +95,25 @@ def process_expired_allocation(): 'expire_carry_forwarded_leaves_after_days': (">", 0) }, fieldname=['name']) - if leave_type_records: - leave_type = [record[0] for record in leave_type_records] + leave_type = [record[0] for record in leave_type_records] - expired_allocation = frappe.db.sql_list("""SELECT name - FROM `tabLeave Ledger Entry` - WHERE - `transaction_type`='Leave Allocation' - AND `is_expired`=1""") + expired_allocation = frappe.db.sql_list("""SELECT name + FROM `tabLeave Ledger Entry` + WHERE + `transaction_type`='Leave Allocation' + AND `is_expired`=1""") - expire_allocation = frappe.get_all("Leave Ledger Entry", - fields=['leaves', 'to_date', 'employee', 'leave_type', 'is_carry_forward', 'transaction_name as name', 'transaction_type'], - filters={ - 'to_date': ("<", today()), - 'transaction_type': 'Leave Allocation', - 'transaction_name': ('not in', expired_allocation) - }, - or_filters={ - 'is_carry_forward': 0, - 'leave_type': ('in', leave_type) - }) + expire_allocation = frappe.get_all("Leave Ledger Entry", + fields=['leaves', 'to_date', 'employee', 'leave_type', 'is_carry_forward', 'transaction_name as name', 'transaction_type'], + filters={ + 'to_date': ("<", today()), + 'transaction_type': 'Leave Allocation', + 'transaction_name': ('not in', expired_allocation) + }, + or_filters={ + 'is_carry_forward': 0, + 'leave_type': ('in', leave_type) + }) if expire_allocation: create_expiry_ledger_entry(expire_allocation) diff --git a/erpnext/hr/doctype/loan_application/loan_application.py b/erpnext/hr/doctype/loan_application/loan_application.py index 28d9c43f8e..582bf48bf0 100644 --- a/erpnext/hr/doctype/loan_application/loan_application.py +++ b/erpnext/hr/doctype/loan_application/loan_application.py @@ -30,7 +30,7 @@ class LoanApplication(Document): monthly_interest_rate = flt(self.rate_of_interest) / (12 *100) if monthly_interest_rate: min_repayment_amount = self.loan_amount*monthly_interest_rate - if self.repayment_amount - min_repayment_amount <= 0: + if (self.repayment_amount - min_repayment_amount) <= 0: frappe.throw(_("Repayment Amount must be greater than " \ + str(flt(min_repayment_amount, 2)))) self.repayment_periods = math.ceil((math.log(self.repayment_amount) - @@ -58,10 +58,13 @@ def make_loan(source_name, target_doc = None): doclist = get_mapped_doc("Loan Application", source_name, { "Loan Application": { "doctype": "Loan", + "field_map": { + "repayment_amount": "monthly_repayment_amount" + }, "validation": { "docstatus": ["=", 1] } } }, target_doc) - return doclist \ No newline at end of file + return doclist diff --git a/erpnext/hr/report/vehicle_expenses/vehicle_expenses.py b/erpnext/hr/report/vehicle_expenses/vehicle_expenses.py index e2989e29c0..e5622b7ae1 100644 --- a/erpnext/hr/report/vehicle_expenses/vehicle_expenses.py +++ b/erpnext/hr/report/vehicle_expenses/vehicle_expenses.py @@ -23,7 +23,8 @@ def get_columns(): _("Model") + ":data:50", _("Location") + ":data:100", _("Log") + ":Link/Vehicle Log:100", _("Odometer") + ":Int:80", _("Date") + ":Date:100", _("Fuel Qty") + ":Float:80", - _("Fuel Price") + ":Float:100",_("Service Expense") + ":Float:100" + _("Fuel Price") + ":Float:100",_("Fuel Expense") + ":Float:100", + _("Service Expense") + ":Float:100" ] return columns @@ -32,7 +33,8 @@ def get_log_data(filters): data = frappe.db.sql("""select vhcl.license_plate as "License", vhcl.make as "Make", vhcl.model as "Model", vhcl.location as "Location", log.name as "Log", log.odometer as "Odometer", - log.date as "Date", log.fuel_qty as "Fuel Qty", log.price as "Fuel Price" + log.date as "Date", log.fuel_qty as "Fuel Qty", log.price as "Fuel Price", + log.fuel_qty * log.price as "Fuel Expense" from `tabVehicle` vhcl,`tabVehicle Log` log where @@ -58,7 +60,7 @@ def get_chart_data(data,period_list): total_ser_exp=0 for row in data: if row["Date"] <= period.to_date and row["Date"] >= period.from_date: - total_fuel_exp+=flt(row["Fuel Price"]) + total_fuel_exp+=flt(row["Fuel Expense"]) total_ser_exp+=flt(row["Service Expense"]) fueldata.append([period.key,total_fuel_exp]) servicedata.append([period.key,total_ser_exp]) @@ -84,4 +86,4 @@ def get_chart_data(data,period_list): } } chart["type"] = "line" - return chart \ No newline at end of file + return chart diff --git a/erpnext/hub_node/legacy.py b/erpnext/hub_node/legacy.py index 95ada76a6a..85eb1b2bb0 100644 --- a/erpnext/hub_node/legacy.py +++ b/erpnext/hub_node/legacy.py @@ -68,12 +68,13 @@ def make_contact(supplier): contact = frappe.get_doc({ 'doctype': 'Contact', 'first_name': supplier.supplier_name, - 'email_id': supplier.supplier_email, 'is_primary_contact': 1, 'links': [ {'link_doctype': 'Supplier', 'link_name': supplier.supplier_name} ] - }).insert() + }) + contact.add_email(supplier.supplier_email) + contact.insert() else: contact = frappe.get_doc('Contact', contact_name) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js index 22613cc8a4..ce7b4f9425 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.js +++ b/erpnext/manufacturing/doctype/work_order/work_order.js @@ -4,16 +4,17 @@ frappe.ui.form.on("Work Order", { setup: function(frm) { frm.custom_make_buttons = { - 'Stock Entry': 'Make Stock Entry', - } + 'Stock Entry': 'Start', + 'Pick List': 'Create Pick List', + }; // Set query for warehouses - frm.set_query("wip_warehouse", function(doc) { + frm.set_query("wip_warehouse", function() { return { filters: { 'company': frm.doc.company, } - } + }; }); frm.set_query("source_warehouse", function() { @@ -21,7 +22,7 @@ frappe.ui.form.on("Work Order", { filters: { 'company': frm.doc.company, } - } + }; }); frm.set_query("source_warehouse", "required_items", function() { @@ -29,7 +30,7 @@ frappe.ui.form.on("Work Order", { filters: { 'company': frm.doc.company, } - } + }; }); frm.set_query("sales_order", function() { @@ -37,7 +38,7 @@ frappe.ui.form.on("Work Order", { filters: { "status": ["not in", ["Closed", "On Hold"]] } - } + }; }); frm.set_query("fg_warehouse", function() { @@ -46,7 +47,7 @@ frappe.ui.form.on("Work Order", { 'company': frm.doc.company, 'is_group': 0 } - } + }; }); frm.set_query("scrap_warehouse", function() { @@ -55,17 +56,19 @@ frappe.ui.form.on("Work Order", { 'company': frm.doc.company, 'is_group': 0 } - } + }; }); // Set query for BOM frm.set_query("bom_no", function() { if (frm.doc.production_item) { - return{ + return { query: "erpnext.controllers.queries.bom", filters: {item: cstr(frm.doc.production_item)} - } - } else msgprint(__("Please enter Production Item first")); + }; + } else { + frappe.msgprint(__("Please enter Production Item first")); + } }); // Set query for FG Item @@ -76,7 +79,7 @@ frappe.ui.form.on("Work Order", { ['is_stock_item', '=',1], ['default_bom', '!=', ''] ] - } + }; }); // Set query for FG Item @@ -85,12 +88,12 @@ frappe.ui.form.on("Work Order", { filters:[ ['Project', 'status', 'not in', 'Completed, Cancelled'] ] - } + }; }); // formatter for work order operation frm.set_indicator_formatter('operation', - function(doc) { return (frm.doc.qty==doc.completed_qty) ? "green" : "orange" }); + function(doc) { return (frm.doc.qty==doc.completed_qty) ? "green" : "orange"; }); }, onload: function(frm) { @@ -133,7 +136,7 @@ frappe.ui.form.on("Work Order", { if(not_completed && not_completed.length) { frm.add_custom_button(__('Create Job Card'), () => { - frm.trigger("make_job_card") + frm.trigger("make_job_card"); }).addClass('btn-primary'); } } @@ -151,7 +154,7 @@ frappe.ui.form.on("Work Order", { condition: (d) => { if (d.allow_alternative_item) {return true;} } - }) + }); }); } } @@ -285,13 +288,13 @@ frappe.ui.form.on("Work Order", { if(!frm.doc.skip_transfer){ var pending_complete = frm.doc.material_transferred_for_manufacturing - frm.doc.produced_qty; if(pending_complete) { - var title = __('{0} items in progress', [pending_complete]); var width = ((pending_complete / frm.doc.qty * 100) - added_min); + title = __('{0} items in progress', [pending_complete]); bars.push({ 'title': title, 'width': (width > 100 ? "99.5" : width) + '%', 'progress_class': 'progress-bar-warning' - }) + }); message = message + '. ' + title; } } @@ -377,7 +380,7 @@ frappe.ui.form.on("Work Order", { filters: [ ["Sales Order","name", "in", r.message] ] - } + }; }); } }); @@ -401,10 +404,10 @@ frappe.ui.form.on("Work Order Item", { frappe.model.set_value(row.doctype, row.name, "available_qty_at_source_warehouse", r.message); } - }) + }); } } -}) +}); frappe.ui.form.on("Work Order Operation", { workstation: function(frm, cdt, cdn) { @@ -421,7 +424,7 @@ frappe.ui.form.on("Work Order Operation", { erpnext.work_order.calculate_cost(frm.doc); erpnext.work_order.calculate_total_cost(frm); } - }) + }); } }, time_in_mins: function(frm, cdt, cdn) { @@ -447,10 +450,13 @@ erpnext.work_order = { const show_start_btn = (frm.doc.skip_transfer || frm.doc.transfer_material_against == 'Job Card') ? 0 : 1; - if (show_start_btn){ + if (show_start_btn) { if ((flt(doc.material_transferred_for_manufacturing) < flt(doc.qty)) && frm.doc.status != 'Stopped') { frm.has_start_btn = true; + frm.add_custom_button(__('Create Pick List'), function() { + erpnext.work_order.create_pick_list(frm); + }); var start_btn = frm.add_custom_button(__('Start'), function() { erpnext.work_order.make_se(frm, 'Material Transfer for Manufacture'); }); @@ -519,8 +525,8 @@ erpnext.work_order = { calculate_total_cost: function(frm) { var variable_cost = frm.doc.actual_operating_cost ? - flt(frm.doc.actual_operating_cost) : flt(frm.doc.planned_operating_cost) - frm.set_value("total_operating_cost", (flt(frm.doc.additional_operating_cost) + variable_cost)) + flt(frm.doc.actual_operating_cost) : flt(frm.doc.planned_operating_cost); + frm.set_value("total_operating_cost", (flt(frm.doc.additional_operating_cost) + variable_cost)); }, set_default_warehouse: function(frm) { @@ -528,45 +534,72 @@ erpnext.work_order = { frappe.call({ method: "erpnext.manufacturing.doctype.work_order.work_order.get_default_warehouse", callback: function(r) { - if(!r.exe) { + if (!r.exe) { frm.set_value("wip_warehouse", r.message.wip_warehouse); - frm.set_value("fg_warehouse", r.message.fg_warehouse) + frm.set_value("fg_warehouse", r.message.fg_warehouse); } } }); } }, - make_se: function(frm, purpose) { - if(!frm.doc.skip_transfer){ - var max = (purpose === "Manufacture") ? - flt(frm.doc.material_transferred_for_manufacturing) - flt(frm.doc.produced_qty) : - flt(frm.doc.qty) - flt(frm.doc.material_transferred_for_manufacturing); + get_max_transferable_qty: (frm, purpose) => { + let max = 0; + if (frm.doc.skip_transfer) return max; + if (purpose === 'Manufacture') { + max = flt(frm.doc.material_transferred_for_manufacturing) - flt(frm.doc.produced_qty); } else { - var max = flt(frm.doc.qty) - flt(frm.doc.produced_qty); + max = flt(frm.doc.qty) - flt(frm.doc.material_transferred_for_manufacturing); } + return flt(max, precision('qty')); + }, - max = flt(max, precision("qty")); - frappe.prompt({fieldtype:"Float", label: __("Qty for {0}", [purpose]), fieldname:"qty", - description: __("Max: {0}", [max]), 'default': max }, function(data) - { - if(data.qty > max) { - frappe.msgprint(__("Quantity must not be more than {0}", [max])); - return; - } - frappe.call({ - method:"erpnext.manufacturing.doctype.work_order.work_order.make_stock_entry", - args: { - "work_order_id": frm.doc.name, - "purpose": purpose, - "qty": data.qty - }, - callback: function(r) { - var doclist = frappe.model.sync(r.message); - frappe.set_route("Form", doclist[0].doctype, doclist[0].name); + show_prompt_for_qty_input: function(frm, purpose) { + let max = this.get_max_transferable_qty(frm, purpose); + return new Promise((resolve, reject) => { + frappe.prompt({ + fieldtype: 'Float', + label: __('Qty for {0}', [purpose]), + fieldname: 'qty', + description: __('Max: {0}', [max]), + default: max + }, data => { + if (data.qty > max) { + frappe.msgprint(__('Quantity must not be more than {0}', [max])); + reject(); } + data.purpose = purpose; + resolve(data); + }, __('Select Quantity'), __('Create')); + }); + }, + + make_se: function(frm, purpose) { + this.show_prompt_for_qty_input(frm, purpose) + .then(data => { + return frappe.xcall('erpnext.manufacturing.doctype.work_order.work_order.make_stock_entry', { + 'work_order_id': frm.doc.name, + 'purpose': purpose, + 'qty': data.qty + }); + }).then(stock_entry => { + frappe.model.sync(stock_entry); + frappe.set_route('Form', stock_entry.doctype, stock_entry.name); + }); + + }, + + create_pick_list: function(frm, purpose='Material Transfer for Manufacture') { + this.show_prompt_for_qty_input(frm, purpose) + .then(data => { + return frappe.xcall('erpnext.manufacturing.doctype.work_order.work_order.create_pick_list', { + 'source_name': frm.doc.name, + 'for_qty': data.qty + }); + }).then(pick_list => { + frappe.model.sync(pick_list); + frappe.set_route('Form', pick_list.doctype, pick_list.name); }); - }, __("Select Quantity"), __('Create')); }, make_consumption_se: function(frm, backflush_raw_materials_based_on) { @@ -606,6 +639,6 @@ erpnext.work_order = { frm.reload_doc(); } } - }) + }); } -} +}; diff --git a/erpnext/manufacturing/doctype/work_order/work_order.json b/erpnext/manufacturing/doctype/work_order/work_order.json index 63c95e7c21..0d073a2312 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.json +++ b/erpnext/manufacturing/doctype/work_order/work_order.json @@ -72,6 +72,7 @@ "fieldname": "naming_series", "fieldtype": "Select", "label": "Series", + "no_copy": 1, "options": "MFG-WO-.YYYY.-", "print_hide": 1, "reqd": 1, @@ -467,7 +468,7 @@ "idx": 1, "image_field": "image", "is_submittable": 1, - "modified": "2019-07-31 00:13:38.218277", + "modified": "2019-08-28 12:29:35.315239", "modified_by": "Administrator", "module": "Manufacturing", "name": "Work Order", diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 2b70325d9f..a636b871e2 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -19,6 +19,7 @@ from erpnext.stock.stock_balance import get_planned_qty, update_bin_qty from frappe.utils.csvutils import getlink from erpnext.stock.utils import get_bin, validate_warehouse_company, get_latest_stock_qty from erpnext.utilities.transaction_base import validate_uom_is_integer +from frappe.model.mapper import get_mapped_doc class OverProductionError(frappe.ValidationError): pass class StockOverProductionError(frappe.ValidationError): pass @@ -707,3 +708,46 @@ def get_work_order_operation_data(work_order, operation, workstation): for d in work_order.operations: if d.operation == operation and d.workstation == workstation: return d + +@frappe.whitelist() +def create_pick_list(source_name, target_doc=None, for_qty=None): + for_qty = for_qty or json.loads(target_doc).get('for_qty') + max_finished_goods_qty = frappe.db.get_value('Work Order', source_name, 'qty') + def update_item_quantity(source, target, source_parent): + pending_to_issue = flt(source.required_qty) - flt(source.transferred_qty) + desire_to_transfer = flt(source.required_qty) / max_finished_goods_qty * flt(for_qty) + + qty = 0 + if desire_to_transfer <= pending_to_issue: + qty = desire_to_transfer + elif pending_to_issue > 0: + qty = pending_to_issue + + if qty: + target.qty = qty + target.stock_qty = qty + target.uom = frappe.get_value('Item', source.item_code, 'stock_uom') + target.stock_uom = target.uom + target.conversion_factor = 1 + else: + target.delete() + + doc = get_mapped_doc('Work Order', source_name, { + 'Work Order': { + 'doctype': 'Pick List', + 'validation': { + 'docstatus': ['=', 1] + } + }, + 'Work Order Item': { + 'doctype': 'Pick List Item', + 'postprocess': update_item_quantity, + 'condition': lambda doc: abs(doc.transferred_qty) < abs(doc.required_qty) + }, + }, target_doc) + + doc.for_qty = for_qty + + doc.set_item_locations() + + return doc \ No newline at end of file diff --git a/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py b/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py index 3fe5282582..0d3c30ea6e 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py +++ b/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py @@ -6,7 +6,7 @@ def get_data(): 'fieldname': 'work_order', 'transactions': [ { - 'items': ['Stock Entry', 'Job Card'] + 'items': ['Pick List', 'Stock Entry', 'Job Card'] } ] } \ No newline at end of file diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 7b8280975d..07b8ee6ab4 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -605,7 +605,6 @@ erpnext.patches.v11_1.rename_depends_on_lwp execute:frappe.delete_doc("Report", "Inactive Items") erpnext.patches.v11_1.delete_scheduling_tool erpnext.patches.v12_0.rename_tolerance_fields -erpnext.patches.v12_0.make_custom_fields_for_bank_remittance #14-06-2019 execute:frappe.delete_doc_if_exists("Page", "support-analytics") erpnext.patches.v12_0.remove_patient_medical_record_page erpnext.patches.v11_1.move_customer_lead_to_dynamic_column @@ -626,4 +625,10 @@ erpnext.patches.v12_0.add_default_buying_selling_terms_in_company erpnext.patches.v12_0.update_ewaybill_field_position erpnext.patches.v12_0.create_accounting_dimensions_in_missing_doctypes erpnext.patches.v11_1.set_status_for_material_request_type_manufacture -erpnext.patches.v12_0.generate_leave_ledger_entries \ No newline at end of file +execute:frappe.reload_doc('desk', 'doctype','dashboard_chart_link') +execute:frappe.reload_doc('desk', 'doctype','dashboard') +execute:frappe.reload_doc('desk', 'doctype','dashboard_chart_source') +execute:frappe.reload_doc('desk', 'doctype','dashboard_chart') +erpnext.patches.v12_0.add_default_dashboards +erpnext.patches.v12_0.remove_bank_remittance_custom_fields +erpnext.patches.v12_0.generate_leave_ledger_entries diff --git a/erpnext/patches/v12_0/add_default_dashboards.py b/erpnext/patches/v12_0/add_default_dashboards.py new file mode 100644 index 0000000000..ab92fbaa69 --- /dev/null +++ b/erpnext/patches/v12_0/add_default_dashboards.py @@ -0,0 +1,8 @@ +# Copyright (c) 2019, Frappe and Contributors +# License: GNU General Public License v3. See license.txt + +import frappe +from erpnext.setup.setup_wizard.operations.install_fixtures import add_dashboards + +def execute(): + add_dashboards() diff --git a/erpnext/patches/v12_0/make_custom_fields_for_bank_remittance.py b/erpnext/patches/v12_0/make_custom_fields_for_bank_remittance.py deleted file mode 100644 index 3d4a9952c7..0000000000 --- a/erpnext/patches/v12_0/make_custom_fields_for_bank_remittance.py +++ /dev/null @@ -1,12 +0,0 @@ -from __future__ import unicode_literals -import frappe -from erpnext.regional.india.setup import make_custom_fields - -def execute(): - frappe.reload_doc("accounts", "doctype", "tax_category") - frappe.reload_doc("stock", "doctype", "item_manufacturer") - company = frappe.get_all('Company', filters = {'country': 'India'}) - if not company: - return - - make_custom_fields() \ No newline at end of file diff --git a/erpnext/patches/v12_0/remove_bank_remittance_custom_fields.py b/erpnext/patches/v12_0/remove_bank_remittance_custom_fields.py new file mode 100644 index 0000000000..d1446b3227 --- /dev/null +++ b/erpnext/patches/v12_0/remove_bank_remittance_custom_fields.py @@ -0,0 +1,14 @@ +from __future__ import unicode_literals +import frappe +from erpnext.regional.india.setup import make_custom_fields + +def execute(): + frappe.reload_doc("accounts", "doctype", "tax_category") + frappe.reload_doc("stock", "doctype", "item_manufacturer") + company = frappe.get_all('Company', filters = {'country': 'India'}) + if not company: + return + if frappe.db.exists("Custom Field", "Company-bank_remittance_section"): + deprecated_fields = ['bank_remittance_section', 'client_code', 'remittance_column_break', 'product_code'] + for i in range(len(deprecated_fields)): + frappe.delete_doc("Custom Field", 'Company-'+deprecated_fields[i]) \ No newline at end of file diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py index df9a6baf38..3c719227bd 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.py +++ b/erpnext/projects/doctype/timesheet/timesheet.py @@ -96,7 +96,7 @@ class Timesheet(Document): for time in self.time_logs: if time.from_time and time.to_time: - if flt(std_working_hours) > 0: + if flt(std_working_hours) and date_diff(time.to_time, time.from_time): time.hours = flt(std_working_hours) * date_diff(time.to_time, time.from_time) else: if not time.hours: diff --git a/erpnext/public/js/templates/contact_list.html b/erpnext/public/js/templates/contact_list.html index 893b4e0ec2..50fbfd9f12 100644 --- a/erpnext/public/js/templates/contact_list.html +++ b/erpnext/public/js/templates/contact_list.html @@ -14,20 +14,33 @@ style="margin-top:-3px; margin-right: -5px;"> {%= __("Edit") %}

    - {% if (contact_list[i].phone || contact_list[i].mobile_no || - contact_list[i].email_id) { %} + {% if (contact_list[i].phones || contact_list[i].email_ids) { %}

    - {% if(contact_list[i].phone) { %} - {%= __("Phone") %}: {%= contact_list[i].phone %}
    - {% } %} - {% if(contact_list[i].mobile_no) { %} - {%= __("Mobile No.") %}: {%= contact_list[i].mobile_no %}
    - {% } %} - {% if(contact_list[i].email_id) { %} - {%= __("Email Address") %}: {%= contact_list[i].email_id %} - {% } %} + {% if(contact_list[i].phone) { %} + {%= __("Phone") %}: {%= contact_list[i].phone %} ({%= __("Primary") %})
    + {% endif %} + {% if(contact_list[i].phone_nos) { %} + {% for(var j=0, k=contact_list[i].phone_nos.length; j + {% } %} + {% endif %} +

    +

    + {% if(contact_list[i].email_id) { %} + {%= __("Email") %}: {%= contact_list[i].email_id %} ({%= __("Primary") %})
    + {% endif %} + {% if(contact_list[i].email_ids) { %} + {% for(var j=0, k=contact_list[i].email_ids.length; j + {% } %} + {% endif %}

    {% endif %} +

    + {% if (contact_list[i].address) { %} + {%= __("Address") %}: {%= contact_list[i].address %}
    + {% endif %} +

    {% } %} {% if(!contact_list.length) { %} diff --git a/erpnext/regional/india/bank_remittance.py b/erpnext/regional/india/bank_remittance.py deleted file mode 100644 index 85c9564722..0000000000 --- a/erpnext/regional/india/bank_remittance.py +++ /dev/null @@ -1,190 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2018, Frappe and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -from frappe.model.document import Document -from frappe.utils import cint,cstr, today -from frappe import _ -import re -import datetime -from collections import OrderedDict - -def create_bank_remittance_txt(name): - payment_order = frappe.get_cached_doc("Payment Order", name) - - no_of_records = len(payment_order.get("references")) - total_amount = sum(entry.get("amount") for entry in payment_order.get("references")) - - product_code, client_code, company_email = frappe.db.get_value("Company", - filters={'name' : payment_order.company}, - fieldname=['product_code', 'client_code', 'email']) - - header, file_name = get_header_row(payment_order, client_code) - batch = get_batch_row(payment_order, no_of_records, total_amount, product_code) - - detail = [] - for ref_doc in payment_order.get("references"): - detail += get_detail_row(ref_doc, payment_order, company_email) - - trailer = get_trailer_row(no_of_records, total_amount) - detail_records = "\n".join(detail) - - return "\n".join([header, batch, detail_records, trailer]), file_name - -@frappe.whitelist() -def generate_report(name): - data, file_name = create_bank_remittance_txt(name) - - f = frappe.get_doc({ - 'doctype': 'File', - 'file_name': file_name, - 'content': data, - "attached_to_doctype": 'Payment Order', - "attached_to_name": name, - 'is_private': True - }) - f.save() - return { - 'file_url': f.file_url, - 'file_name': file_name - } - -def generate_file_name(name, company_account, date): - ''' generate file name with format (account_code)_mmdd_(payment_order_no) ''' - bank, acc_no = frappe.db.get_value("Bank Account", {"name": company_account}, ['bank', 'bank_account_no']) - return bank[:1]+str(acc_no)[-4:]+'_'+date.strftime("%m%d")+sanitize_data(name, '')[4:]+'.txt' - -def get_header_row(doc, client_code): - ''' Returns header row and generated file name ''' - file_name = generate_file_name(doc.name, doc.company_bank_account, doc.posting_date) - header = ["H"] - header.append(validate_field_size(client_code, "Client Code", 20)) - header += [''] * 3 - header.append(validate_field_size(file_name, "File Name", 20)) - return "~".join(header), file_name - -def get_batch_row(doc, no_of_records, total_amount, product_code): - batch = ["B"] - batch.append(validate_field_size(no_of_records, "No Of Records", 5)) - batch.append(validate_amount(format(total_amount, '0.2f'), 17)) - batch.append(sanitize_data(doc.name, '_')[:20]) - batch.append(format_date(doc.posting_date)) - batch.append(validate_field_size(product_code,"Product Code", 20)) - return "~".join(batch) - -def get_detail_row(ref_doc, payment_entry, company_email): - - payment_date = format_date(payment_entry.posting_date) - payment_entry = frappe.get_cached_doc('Payment Entry', ref_doc.payment_entry) - supplier_bank_details = frappe.get_cached_doc('Bank Account', ref_doc.bank_account) - company_bank_acc_no = frappe.db.get_value("Bank Account", {'name': payment_entry.bank_account}, ['bank_account_no']) - - addr_link = frappe.db.get_value('Dynamic Link', - { - 'link_doctype': 'Supplier', - 'link_name': 'Sample Supplier', - 'parenttype':'Address', - 'parent': ('like', '%-Billing') - }, 'parent') - - supplier_billing_address = frappe.get_cached_doc('Address', addr_link) - email = ','.join(filter(None, [supplier_billing_address.email_id, company_email])) - - detail = OrderedDict( - record_identifier='D', - payment_ref_no=sanitize_data(ref_doc.payment_entry), - payment_type=cstr(payment_entry.mode_of_payment)[:10], - amount=str(validate_amount(format(ref_doc.amount, '.2f'),13)), - payment_date=payment_date, - instrument_date=payment_date, - instrument_number='', - dr_account_no_client=str(validate_field_size(company_bank_acc_no, "Company Bank Account", 20)), - dr_description='', - dr_ref_no='', - cr_ref_no='', - bank_code_indicator='M', - beneficiary_code='', - beneficiary_name=sanitize_data(validate_information(payment_entry, "party", 160), ' '), - beneficiary_bank=sanitize_data(validate_information(supplier_bank_details, "bank", 10)), - beneficiary_branch_code=cstr(validate_information(supplier_bank_details, "branch_code", 11)), - beneficiary_acc_no=validate_information(supplier_bank_details, "bank_account_no", 20), - location='', - print_location='', - beneficiary_address_1=validate_field_size(sanitize_data(cstr(supplier_billing_address.address_line1), ' '), " Beneficiary Address 1", 50), - beneficiary_address_2=validate_field_size(sanitize_data(cstr(supplier_billing_address.address_line2), ' '), " Beneficiary Address 2", 50), - beneficiary_address_3='', - beneficiary_address_4='', - beneficiary_address_5='', - beneficiary_city=validate_field_size(cstr(supplier_billing_address.city), "Beneficiary City", 20), - beneficiary_zipcode=validate_field_size(cstr(supplier_billing_address.pincode), "Pin Code", 6), - beneficiary_state=validate_field_size(cstr(supplier_billing_address.state), "Beneficiary State", 20), - beneficiary_email=cstr(email)[:255], - beneficiary_mobile=validate_field_size(cstr(supplier_billing_address.phone), "Beneficiary Mobile", 10), - payment_details_1='', - payment_details_2='', - payment_details_3='', - payment_details_4='', - delivery_mode='' - ) - detail_record = ["~".join(list(detail.values()))] - - detail_record += get_advice_rows(payment_entry) - return detail_record - -def get_advice_rows(payment_entry): - ''' Returns multiple advice rows for a single detail entry ''' - payment_entry_date = payment_entry.posting_date.strftime("%b%y%d%m").upper() - mode_of_payment = payment_entry.mode_of_payment - advice_rows = [] - for record in payment_entry.references: - advice = ['E'] - advice.append(cstr(mode_of_payment)) - advice.append(cstr(record.total_amount)) - advice.append('') - advice.append(cstr(record.outstanding_amount)) - advice.append(record.reference_name) - advice.append(format_date(record.due_date)) - advice.append(payment_entry_date) - advice_rows.append("~".join(advice)) - return advice_rows - -def get_trailer_row(no_of_records, total_amount): - ''' Returns trailer row ''' - trailer = ["T"] - trailer.append(validate_field_size(no_of_records, "No of Records", 5)) - trailer.append(validate_amount(format(total_amount, "0.2f"), 17)) - return "~".join(trailer) - -def sanitize_data(val, replace_str=''): - ''' Remove all the non-alphanumeric characters from string ''' - pattern = re.compile('[\W_]+') - return pattern.sub(replace_str, val) - -def format_date(val): - ''' Convert a datetime object to DD/MM/YYYY format ''' - return val.strftime("%d/%m/%Y") - -def validate_amount(val, max_int_size): - ''' Validate amount to be within the allowed limits ''' - int_size = len(str(val).split('.')[0]) - - if int_size > max_int_size: - frappe.throw(_("Amount for a single transaction exceeds maximum allowed amount, create a separate payment order by splitting the transactions")) - - return val - -def validate_information(obj, attr, max_size): - ''' Checks if the information is not set in the system and is within the size ''' - if hasattr(obj, attr): - return validate_field_size(getattr(obj, attr), frappe.unscrub(attr), max_size) - - else: - frappe.throw(_("{0} is mandatory for generating remittance payments, set the field and try again".format(frappe.unscrub(attr)))) - -def validate_field_size(val, label, max_size): - ''' check the size of the val ''' - if len(cstr(val)) > max_size: - frappe.throw(_("{0} field is limited to size {1}".format(label, max_size))) - return cstr(val) \ No newline at end of file diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py index 40b98ed19a..756c17dc3b 100644 --- a/erpnext/regional/india/setup.py +++ b/erpnext/regional/india/setup.py @@ -407,14 +407,6 @@ def make_custom_fields(update=True): fieldtype='Link', options='Salary Component', insert_after='basic_component'), dict(fieldname='arrear_component', label='Arrear Component', fieldtype='Link', options='Salary Component', insert_after='hra_component'), - dict(fieldname='bank_remittance_section', label='Bank Remittance Settings', - fieldtype='Section Break', collapsible=1, insert_after='arrear_component'), - dict(fieldname='client_code', label='Client Code', fieldtype='Data', - insert_after='bank_remittance_section'), - dict(fieldname='remittance_column_break', fieldtype='Column Break', - insert_after='client_code'), - dict(fieldname='product_code', label='Product Code', fieldtype='Data', - insert_after='remittance_column_break'), ], 'Employee Tax Exemption Declaration':[ dict(fieldname='hra_section', label='HRA Exemption', diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index c946c47c59..3c9c3cca03 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -337,14 +337,15 @@ def make_contact(args, is_primary_contact=1): contact = frappe.get_doc({ 'doctype': 'Contact', 'first_name': args.get('name'), - 'mobile_no': args.get('mobile_no'), - 'email_id': args.get('email_id'), 'is_primary_contact': is_primary_contact, 'links': [{ 'link_doctype': args.get('doctype'), 'link_name': args.get('name') }] - }).insert() + }) + contact.add_email(args.get('email_id')) + contact.add_phone(args.get('mobile_no')) + contact.insert() return contact @@ -371,7 +372,7 @@ def get_customer_primary_contact(doctype, txt, searchfield, start, page_len, fil return frappe.db.sql(""" select `tabContact`.name from `tabContact`, `tabDynamic Link` where `tabContact`.name = `tabDynamic Link`.parent and `tabDynamic Link`.link_name = %(customer)s - and `tabDynamic Link`.link_doctype = 'Customer' and `tabContact`.is_primary_contact = 1 + and `tabDynamic Link`.link_doctype = 'Customer' and `tabContact`.name like %(txt)s """, { 'customer': customer, @@ -383,7 +384,7 @@ def get_customer_primary_address(doctype, txt, searchfield, start, page_len, fil return frappe.db.sql(""" select `tabAddress`.name from `tabAddress`, `tabDynamic Link` where `tabAddress`.name = `tabDynamic Link`.parent and `tabDynamic Link`.link_name = %(customer)s - and `tabDynamic Link`.link_doctype = 'Customer' and `tabAddress`.is_primary_address = 1 + and `tabDynamic Link`.link_doctype = 'Customer' and `tabAddress`.name like %(txt)s """, { 'customer': customer, diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js index 2e5f255a90..e86caddb87 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.js +++ b/erpnext/selling/doctype/sales_order/sales_order.js @@ -7,6 +7,7 @@ frappe.ui.form.on("Sales Order", { setup: function(frm) { frm.custom_make_buttons = { 'Delivery Note': 'Delivery', + 'Pick List': 'Pick List', 'Sales Invoice': 'Invoice', 'Material Request': 'Material Request', 'Purchase Order': 'Purchase Order', @@ -109,7 +110,9 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( this._super(); let allow_delivery = false; - if(doc.docstatus==1) { + if (doc.docstatus==1) { + this.frm.add_custom_button(__('Pick List'), () => this.create_pick_list(), __('Create')); + if(this.frm.has_perm("submit")) { if(doc.status === 'On Hold') { // un-hold @@ -233,6 +236,13 @@ erpnext.selling.SalesOrderController = erpnext.selling.SellingController.extend( this.order_type(doc); }, + create_pick_list() { + frappe.model.open_mapped_doc({ + method: "erpnext.selling.doctype.sales_order.sales_order.create_pick_list", + frm: this.frm + }) + }, + make_work_order() { var me = this; this.frm.call({ diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 09dc9a9932..c9aaab4b21 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -72,9 +72,7 @@ class SalesOrder(SellingController): frappe.msgprint(_("Warning: Sales Order {0} already exists against Customer's Purchase Order {1}").format(so[0][0], self.po_no)) def validate_for_items(self): - check_list = [] for d in self.get('items'): - check_list.append(cstr(d.item_code)) # used for production plan d.transaction_date = self.transaction_date @@ -83,13 +81,6 @@ class SalesOrder(SellingController): where item_code = %s and warehouse = %s", (d.item_code, d.warehouse)) d.projected_qty = tot_avail_qty and flt(tot_avail_qty[0][0]) or 0 - # check for same entry multiple times - unique_chk_list = set(check_list) - if len(unique_chk_list) != len(check_list) and \ - not cint(frappe.db.get_single_value("Selling Settings", "allow_multiple_items")): - frappe.msgprint(_("Same item has been entered multiple times"), - title=_("Warning"), indicator='orange') - def product_bundle_has_stock_item(self, product_bundle): """Returns true if product bundle has stock item""" ret = len(frappe.db.sql("""select i.name from tabItem i, `tabProduct Bundle Item` pbi @@ -568,7 +559,7 @@ def make_project(source_name, target_doc=None): return doc @frappe.whitelist() -def make_delivery_note(source_name, target_doc=None): +def make_delivery_note(source_name, target_doc=None, skip_item_mapping=False): def set_missing_values(source, target): target.ignore_pricing_rule = 1 target.run_method("set_missing_values") @@ -593,23 +584,13 @@ def make_delivery_note(source_name, target_doc=None): or item.get("buying_cost_center") \ or item_group.get("buying_cost_center") - target_doc = get_mapped_doc("Sales Order", source_name, { + mapper = { "Sales Order": { "doctype": "Delivery Note", "validation": { "docstatus": ["=", 1] } }, - "Sales Order Item": { - "doctype": "Delivery Note Item", - "field_map": { - "rate": "rate", - "name": "so_detail", - "parent": "against_sales_order", - }, - "postprocess": update_item, - "condition": lambda doc: abs(doc.delivered_qty) < abs(doc.qty) and doc.delivered_by_supplier!=1 - }, "Sales Taxes and Charges": { "doctype": "Sales Taxes and Charges", "add_if_empty": True @@ -618,7 +599,21 @@ def make_delivery_note(source_name, target_doc=None): "doctype": "Sales Team", "add_if_empty": True } - }, target_doc, set_missing_values) + } + + if not skip_item_mapping: + mapper["Sales Order Item"] = { + "doctype": "Delivery Note Item", + "field_map": { + "rate": "rate", + "name": "so_detail", + "parent": "against_sales_order", + }, + "postprocess": update_item, + "condition": lambda doc: abs(doc.delivered_qty) < abs(doc.qty) and doc.delivered_by_supplier!=1 + } + + target_doc = get_mapped_doc("Sales Order", source_name, mapper, target_doc, set_missing_values) return target_doc @@ -996,3 +991,33 @@ def make_raw_material_request(items, company, sales_order, project=None): def make_inter_company_purchase_order(source_name, target_doc=None): from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_inter_company_transaction return make_inter_company_transaction("Sales Order", source_name, target_doc) + +@frappe.whitelist() +def create_pick_list(source_name, target_doc=None): + def update_item_quantity(source, target, source_parent): + target.qty = flt(source.qty) - flt(source.delivered_qty) + target.stock_qty = (flt(source.qty) - flt(source.delivered_qty)) * flt(source.conversion_factor) + + doc = get_mapped_doc('Sales Order', source_name, { + 'Sales Order': { + 'doctype': 'Pick List', + 'validation': { + 'docstatus': ['=', 1] + } + }, + 'Sales Order Item': { + 'doctype': 'Pick List Item', + 'field_map': { + 'parent': 'sales_order', + 'name': 'sales_order_item' + }, + 'postprocess': update_item_quantity, + 'condition': lambda doc: abs(doc.delivered_qty) < abs(doc.qty) and doc.delivered_by_supplier!=1 + }, + }, target_doc) + + doc.purpose = 'Delivery against Sales Order' + + doc.set_item_locations() + + return doc diff --git a/erpnext/selling/doctype/sales_order/sales_order_dashboard.py b/erpnext/selling/doctype/sales_order/sales_order_dashboard.py index aab6db2584..4126bc6a70 100644 --- a/erpnext/selling/doctype/sales_order/sales_order_dashboard.py +++ b/erpnext/selling/doctype/sales_order/sales_order_dashboard.py @@ -17,7 +17,7 @@ def get_data(): 'transactions': [ { 'label': _('Fulfillment'), - 'items': ['Sales Invoice', 'Delivery Note'] + 'items': ['Sales Invoice', 'Pick List', 'Delivery Note'] }, { 'label': _('Purchasing'), diff --git a/erpnext/selling/doctype/sms_center/sms_center.py b/erpnext/selling/doctype/sms_center/sms_center.py index bb6ba1ffce..289b045e1c 100644 --- a/erpnext/selling/doctype/sms_center/sms_center.py +++ b/erpnext/selling/doctype/sms_center/sms_center.py @@ -31,7 +31,7 @@ class SMSCenter(Document): self.sales_partner.replace("'", "\'") or " and ifnull(dl.link_name, '') != ''" if self.send_to in ['All Contact', 'All Customer Contact', 'All Supplier Contact', 'All Sales Partner Contact']: rec = frappe.db.sql("""select CONCAT(ifnull(c.first_name,''), ' ', ifnull(c.last_name,'')), - c.mobile_no from `tabContact` c, `tabDynamic Link` dl where ifnull(c.mobile_no,'')!='' and + c.phone from `tabContact` c, `tabDynamic Link` dl where ifnull(c.phone,'')!='' and c.docstatus != 2 and dl.parent = c.name%s""" % where_clause) elif self.send_to == 'All Lead (Open)': diff --git a/erpnext/selling/page/point_of_sale/point_of_sale.js b/erpnext/selling/page/point_of_sale/point_of_sale.js index d233b41787..d2c2d70dbe 100644 --- a/erpnext/selling/page/point_of_sale/point_of_sale.js +++ b/erpnext/selling/page/point_of_sale/point_of_sale.js @@ -4,7 +4,7 @@ frappe.provide('erpnext.pos'); frappe.pages['point-of-sale'].on_page_load = function(wrapper) { frappe.ui.make_app_page({ parent: wrapper, - title: 'Point of Sale', + title: __('Point of Sale'), single_column: true }); diff --git a/erpnext/setup/doctype/global_defaults/global_defaults.py b/erpnext/setup/doctype/global_defaults/global_defaults.py index a39e246c68..fa7bc504b6 100644 --- a/erpnext/setup/doctype/global_defaults/global_defaults.py +++ b/erpnext/setup/doctype/global_defaults/global_defaults.py @@ -58,7 +58,7 @@ class GlobalDefaults(Document): # Make property setters to hide rounded total fields for doctype in ("Quotation", "Sales Order", "Sales Invoice", "Delivery Note", - "Supplier Quotation", "Purchase Order"): + "Supplier Quotation", "Purchase Order", "Purchase Invoice"): make_property_setter(doctype, "base_rounded_total", "hidden", self.disable_rounded_total, "Check") make_property_setter(doctype, "base_rounded_total", "print_hide", 1, "Check") diff --git a/erpnext/setup/doctype/item_group/item_group.py b/erpnext/setup/doctype/item_group/item_group.py index 33ab992568..bb357d8bf8 100644 --- a/erpnext/setup/doctype/item_group/item_group.py +++ b/erpnext/setup/doctype/item_group/item_group.py @@ -26,6 +26,10 @@ class ItemGroup(NestedSet, WebsiteGenerator): def validate(self): super(ItemGroup, self).validate() + + if not self.parent_item_group and not frappe.flags.in_test: + self.parent_item_group = 'All Item Groups' + self.make_route() def on_update(self): diff --git a/erpnext/setup/setup_wizard/data/dashboard_charts.py b/erpnext/setup/setup_wizard/data/dashboard_charts.py new file mode 100644 index 0000000000..41cfcb529e --- /dev/null +++ b/erpnext/setup/setup_wizard/data/dashboard_charts.py @@ -0,0 +1,107 @@ +from __future__ import unicode_literals +from frappe import _ +import frappe +import json + +def get_default_dashboards(): + company = frappe.get_doc("Company", frappe.defaults.get_defaults().company) + income_account = company.default_income_account or get_account("Income Account", company.name) + expense_account = company.default_expense_account or get_account("Expense Account", company.name) + bank_account = company.default_bank_account or get_account("Bank", company.name) + + return { + "Dashboards": [ + { + "doctype": "Dashboard", + "dashboard_name": "Accounts", + "charts": [ + { "chart": "Outgoing Bills (Sales Invoice)" }, + { "chart": "Incoming Bills (Purchase Invoice)" }, + { "chart": "Bank Balance" }, + { "chart": "Income" }, + { "chart": "Expenses" } + ] + } + ], + "Charts": [ + { + "doctype": "Dashboard Chart", + "time_interval": "Quarterly", + "chart_name": "Income", + "timespan": "Last Year", + "color": None, + "filters_json": json.dumps({"company": company.name, "account": income_account}), + "source": "Account Balance Timeline", + "chart_type": "Custom", + "timeseries": 1, + "owner": "Administrator", + "type": "Line", + "width": "Half" + }, + { + "doctype": "Dashboard Chart", + "time_interval": "Quarterly", + "chart_name": "Expenses", + "timespan": "Last Year", + "color": None, + "filters_json": json.dumps({"company": company.name, "account": expense_account}), + "source": "Account Balance Timeline", + "chart_type": "Custom", + "timeseries": 1, + "owner": "Administrator", + "type": "Line", + "width": "Half" + }, + { + "doctype": "Dashboard Chart", + "time_interval": "Quarterly", + "chart_name": "Bank Balance", + "timespan": "Last Year", + "color": "#ffb868", + "filters_json": json.dumps({"company": company.name, "account": bank_account}), + "source": "Account Balance Timeline", + "chart_type": "Custom", + "timeseries": 1, + "owner": "Administrator", + "type": "Line", + "width": "Half" + }, + { + "doctype": "Dashboard Chart", + "time_interval": "Monthly", + "chart_name": "Incoming Bills (Purchase Invoice)", + "timespan": "Last Year", + "color": "#a83333", + "value_based_on": "base_grand_total", + "filters_json": json.dumps({}), + "chart_type": "Sum", + "timeseries": 1, + "based_on": "posting_date", + "owner": "Administrator", + "document_type": "Purchase Invoice", + "type": "Bar", + "width": "Half" + }, + { + "doctype": "Dashboard Chart", + "time_interval": "Monthly", + "chart_name": "Outgoing Bills (Sales Invoice)", + "timespan": "Last Year", + "color": "#7b933d", + "value_based_on": "base_grand_total", + "filters_json": json.dumps({}), + "chart_type": "Sum", + "timeseries": 1, + "based_on": "posting_date", + "owner": "Administrator", + "document_type": "Sales Invoice", + "type": "Bar", + "width": "Half" + } + ] + } + +def get_account(account_type, company): + accounts = frappe.get_list("Account", filters={"account_type": account_type, "company": company}) + if accounts: + return accounts[0].name \ No newline at end of file diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py index 4b734df61d..7123021d1e 100644 --- a/erpnext/setup/setup_wizard/operations/install_fixtures.py +++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py @@ -475,13 +475,14 @@ def install_defaults(args=None): frappe.db.set_value("Company", args.company_name, "default_bank_account", bank_account.name, update_modified=False) - return doc except RootNotEditable: frappe.throw(_("Bank account cannot be named as {0}").format(args.bank_account)) except frappe.DuplicateEntryError: # bank account same as a CoA entry pass + add_dashboards() + # Now, with fixtures out of the way, onto concrete stuff records = [ @@ -499,6 +500,13 @@ def install_defaults(args=None): make_records(records) +def add_dashboards(): + from erpnext.setup.setup_wizard.data.dashboard_charts import get_default_dashboards + dashboard_data = get_default_dashboards() + + make_records(dashboard_data["Charts"]) + make_records(dashboard_data["Dashboards"]) + def get_fy_details(fy_start_date, fy_end_date): start_year = getdate(fy_start_date).year diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index b5f2c3404e..1116273ace 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -1,4514 +1,1308 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 0, - "autoname": "naming_series:", - "beta": 0, - "creation": "2013-05-24 19:29:09", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Document", - "editable_grid": 0, + "allow_import": 1, + "autoname": "naming_series:", + "creation": "2013-05-24 19:29:09", + "doctype": "DocType", + "document_type": "Document", + "field_order": [ + "delivery_to_section", + "column_break0", + "title", + "naming_series", + "customer", + "customer_name", + "column_break1", + "amended_from", + "company", + "posting_date", + "posting_time", + "set_posting_time", + "is_return", + "issue_credit_note", + "return_against", + "customer_po_details", + "po_no", + "section_break_18", + "pick_list", + "column_break_17", + "po_date", + "contact_info", + "shipping_address_name", + "shipping_address", + "contact_person", + "contact_display", + "contact_mobile", + "contact_email", + "col_break21", + "customer_address", + "tax_id", + "address_display", + "company_address", + "company_address_display", + "currency_and_price_list", + "currency", + "conversion_rate", + "col_break23", + "selling_price_list", + "price_list_currency", + "plc_conversion_rate", + "ignore_pricing_rule", + "sec_warehouse", + "set_warehouse", + "col_break_warehouse", + "to_warehouse", + "items_section", + "scan_barcode", + "items", + "pricing_rule_details", + "pricing_rules", + "packing_list", + "packed_items", + "product_bundle_help", + "section_break_31", + "total_qty", + "base_total", + "base_net_total", + "column_break_33", + "total", + "net_total", + "total_net_weight", + "taxes_section", + "tax_category", + "column_break_39", + "shipping_rule", + "section_break_41", + "taxes_and_charges", + "taxes", + "sec_tax_breakup", + "other_charges_calculation", + "section_break_44", + "base_total_taxes_and_charges", + "column_break_47", + "total_taxes_and_charges", + "section_break_49", + "apply_discount_on", + "base_discount_amount", + "column_break_51", + "additional_discount_percentage", + "discount_amount", + "totals", + "base_grand_total", + "base_rounding_adjustment", + "base_rounded_total", + "base_in_words", + "column_break3", + "grand_total", + "rounding_adjustment", + "rounded_total", + "in_words", + "terms_section_break", + "tc_name", + "terms", + "transporter_info", + "transporter", + "driver", + "lr_no", + "vehicle_no", + "col_break34", + "transporter_name", + "driver_name", + "lr_date", + "more_info", + "project", + "campaign", + "source", + "column_break5", + "per_billed", + "customer_group", + "territory", + "printing_details", + "letter_head", + "select_print_heading", + "language", + "column_break_88", + "print_without_amount", + "group_same_items", + "section_break_83", + "status", + "per_installed", + "installation_status", + "column_break_89", + "excise_page", + "instructions", + "subscription_section", + "auto_repeat", + "sales_team_section_break", + "sales_partner", + "column_break7", + "commission_rate", + "total_commission", + "section_break1", + "sales_team" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "delivery_to_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Delivery To", - "length": 0, - "no_copy": 0, - "options": "fa fa-user", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "delivery_to_section", + "fieldtype": "Section Break", + "label": "Delivery To", + "options": "fa fa-user" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break0", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "oldfieldtype": "Column Break", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "50%", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "column_break0", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break", + "print_width": "50%", "width": "50%" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "{customer_name}", - "fieldname": "title", - "fieldtype": "Data", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Title", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "default": "{customer_name}", + "fieldname": "title", + "fieldtype": "Data", + "hidden": 1, + "label": "Title", + "no_copy": 1, + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "naming_series", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Series", - "length": 0, - "no_copy": 1, - "oldfieldname": "naming_series", - "oldfieldtype": "Select", - "options": "MAT-DN-.YYYY.-", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 1, - "translatable": 0, - "unique": 0 - }, + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "no_copy": 1, + "oldfieldname": "naming_series", + "oldfieldtype": "Select", + "options": "MAT-DN-.YYYY.-", + "print_hide": 1, + "reqd": 1, + "set_only_once": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "customer", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Customer", - "length": 0, - "no_copy": 0, - "oldfieldname": "customer", - "oldfieldtype": "Link", - "options": "Customer", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "customer", + "fieldtype": "Link", + "in_global_search": 1, + "in_standard_filter": 1, + "label": "Customer", + "oldfieldname": "customer", + "oldfieldtype": "Link", + "options": "Customer", + "print_hide": 1, + "reqd": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 1, - "collapsible": 0, - "columns": 0, - "depends_on": "customer", - "fetch_from": "customer.customer_name", - "fieldname": "customer_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Customer Name", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "bold": 1, + "depends_on": "customer", + "fetch_from": "customer.customer_name", + "fieldname": "customer_name", + "fieldtype": "Data", + "in_global_search": 1, + "label": "Customer Name", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break1", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "oldfieldtype": "Column Break", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break1", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "amended_from", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Amended From", - "length": 0, - "no_copy": 1, - "oldfieldname": "amended_from", - "oldfieldtype": "Data", - "options": "Delivery Note", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "150px", - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "amended_from", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Amended From", + "no_copy": 1, + "oldfieldname": "amended_from", + "oldfieldtype": "Data", + "options": "Delivery Note", + "print_hide": 1, + "print_width": "150px", + "read_only": 1, "width": "150px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Company", - "length": 0, - "no_copy": 0, - "oldfieldname": "company", - "oldfieldtype": "Link", - "options": "Company", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "150px", - "read_only": 0, - "remember_last_selected_value": 1, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "company", + "fieldtype": "Link", + "in_standard_filter": 1, + "label": "Company", + "oldfieldname": "company", + "oldfieldtype": "Link", + "options": "Company", + "print_hide": 1, + "print_width": "150px", + "remember_last_selected_value": 1, + "reqd": 1, "width": "150px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Today", - "depends_on": "", - "fieldname": "posting_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Date", - "length": 0, - "no_copy": 1, - "oldfieldname": "posting_date", - "oldfieldtype": "Date", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "100px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "default": "Today", + "fieldname": "posting_date", + "fieldtype": "Date", + "label": "Date", + "no_copy": 1, + "oldfieldname": "posting_date", + "oldfieldtype": "Date", + "print_width": "100px", + "reqd": 1, + "search_index": 1, "width": "100px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "description": "", - "fieldname": "posting_time", - "fieldtype": "Time", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Posting Time", - "length": 0, - "no_copy": 0, - "oldfieldname": "posting_time", - "oldfieldtype": "Time", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "100px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "posting_time", + "fieldtype": "Time", + "label": "Posting Time", + "oldfieldname": "posting_time", + "oldfieldtype": "Time", + "print_hide": 1, + "print_width": "100px", + "reqd": 1, "width": "100px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.docstatus==0", - "fieldname": "set_posting_time", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Edit Posting Date and Time", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "depends_on": "eval:doc.docstatus==0", + "fieldname": "set_posting_time", + "fieldtype": "Check", + "label": "Edit Posting Date and Time", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "is_return", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Is Return", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "is_return", + "fieldtype": "Check", + "label": "Is Return", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "is_return", - "fieldname": "issue_credit_note", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Issue Credit Note", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "depends_on": "is_return", + "fieldname": "issue_credit_note", + "fieldtype": "Check", + "label": "Issue Credit Note" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "is_return", - "fieldname": "return_against", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Return Against Delivery Note", - "length": 0, - "no_copy": 1, - "options": "Delivery Note", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "is_return", + "fieldname": "return_against", + "fieldtype": "Link", + "label": "Return Against Delivery Note", + "no_copy": 1, + "options": "Delivery Note", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "po_no", - "columns": 0, - "fieldname": "customer_po_details", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Customer PO Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "collapsible_depends_on": "po_no", + "fieldname": "customer_po_details", + "fieldtype": "Section Break", + "label": "Customer PO Details" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "po_no", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Customer's Purchase Order No", - "length": 0, - "no_copy": 1, - "oldfieldname": "po_no", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "100px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "allow_on_submit": 1, + "fieldname": "po_no", + "fieldtype": "Data", + "label": "Customer's Purchase Order No", + "no_copy": 1, + "oldfieldname": "po_no", + "oldfieldtype": "Data", + "print_hide": 1, + "print_width": "100px", "width": "100px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_17", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_17", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:doc.po_no", - "fieldname": "po_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Customer's Purchase Order Date", - "length": 0, - "no_copy": 0, - "oldfieldname": "po_date", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "100px", - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "depends_on": "eval:doc.po_no", + "fieldname": "po_date", + "fieldtype": "Date", + "label": "Customer's Purchase Order Date", + "oldfieldname": "po_date", + "oldfieldtype": "Data", + "print_hide": 1, + "print_width": "100px", + "read_only": 1, "width": "100px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "depends_on": "customer", - "fieldname": "contact_info", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Address and Contact", - "length": 0, - "no_copy": 0, - "options": "fa fa-bullhorn", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "depends_on": "customer", + "fieldname": "contact_info", + "fieldtype": "Section Break", + "label": "Address and Contact", + "options": "fa fa-bullhorn" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "shipping_address_name", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Shipping Address", - "length": 0, - "no_copy": 0, - "options": "Address", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "shipping_address_name", + "fieldtype": "Link", + "label": "Shipping Address", + "options": "Address", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "shipping_address", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Shipping Address", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "shipping_address", + "fieldtype": "Small Text", + "label": "Shipping Address", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "contact_person", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Contact Person", - "length": 0, - "no_copy": 0, - "options": "Contact", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "contact_person", + "fieldtype": "Link", + "label": "Contact Person", + "options": "Contact", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "contact_display", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 1, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Contact", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "contact_display", + "fieldtype": "Small Text", + "in_global_search": 1, + "label": "Contact", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "contact_mobile", - "fieldtype": "Small Text", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Mobile No", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "contact_mobile", + "fieldtype": "Small Text", + "hidden": 1, + "label": "Mobile No", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "contact_email", - "fieldtype": "Data", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Contact Email", - "length": 0, - "no_copy": 0, - "options": "Email", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "contact_email", + "fieldtype": "Data", + "hidden": 1, + "label": "Contact Email", + "options": "Email", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "col_break21", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "50%", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "col_break21", + "fieldtype": "Column Break", + "print_width": "50%", "width": "50%" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "customer", - "fieldname": "customer_address", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Billing Address Name", - "length": 0, - "no_copy": 0, - "options": "Address", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "customer", + "fieldname": "customer_address", + "fieldtype": "Link", + "label": "Billing Address Name", + "options": "Address", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "tax_id", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Tax Id", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "tax_id", + "fieldtype": "Data", + "label": "Tax Id", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "address_display", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Billing Address", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "address_display", + "fieldtype": "Small Text", + "label": "Billing Address", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "company_address", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Company Address Name", - "length": 0, - "no_copy": 0, - "options": "Address", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "company_address", + "fieldtype": "Link", + "label": "Company Address Name", + "options": "Address" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "company_address_display", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Company Address", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "company_address_display", + "fieldtype": "Small Text", + "label": "Company Address" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "currency_and_price_list", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Currency and Price List", - "length": 0, - "no_copy": 0, - "options": "fa fa-tag", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "currency_and_price_list", + "fieldtype": "Section Break", + "label": "Currency and Price List", + "options": "fa fa-tag" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "currency", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Currency", - "length": 0, - "no_copy": 0, - "oldfieldname": "currency", - "oldfieldtype": "Select", - "options": "Currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "currency", + "fieldtype": "Link", + "label": "Currency", + "oldfieldname": "currency", + "oldfieldtype": "Select", + "options": "Currency", + "print_hide": 1, + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Rate at which customer's currency is converted to company's base currency", - "fieldname": "conversion_rate", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Exchange Rate", - "length": 0, - "no_copy": 0, - "oldfieldname": "conversion_rate", - "oldfieldtype": "Currency", - "permlevel": 0, - "precision": "9", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "description": "Rate at which customer's currency is converted to company's base currency", + "fieldname": "conversion_rate", + "fieldtype": "Float", + "label": "Exchange Rate", + "oldfieldname": "conversion_rate", + "oldfieldtype": "Currency", + "precision": "9", + "print_hide": 1, + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "col_break23", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "col_break23", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "selling_price_list", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Price List", - "length": 0, - "no_copy": 0, - "oldfieldname": "price_list_name", - "oldfieldtype": "Select", - "options": "Price List", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "selling_price_list", + "fieldtype": "Link", + "label": "Price List", + "oldfieldname": "price_list_name", + "oldfieldtype": "Select", + "options": "Price List", + "print_hide": 1, + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "price_list_currency", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Price List Currency", - "length": 0, - "no_copy": 0, - "options": "Currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "price_list_currency", + "fieldtype": "Link", + "label": "Price List Currency", + "options": "Currency", + "print_hide": 1, + "read_only": 1, + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Rate at which Price list currency is converted to company's base currency", - "fieldname": "plc_conversion_rate", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Price List Exchange Rate", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "9", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "description": "Rate at which Price list currency is converted to company's base currency", + "fieldname": "plc_conversion_rate", + "fieldtype": "Float", + "label": "Price List Exchange Rate", + "precision": "9", + "print_hide": 1, + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "ignore_pricing_rule", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Ignore Pricing Rule", - "length": 0, - "no_copy": 1, - "permlevel": 1, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "fieldname": "ignore_pricing_rule", + "fieldtype": "Check", + "label": "Ignore Pricing Rule", + "no_copy": 1, + "permlevel": 1, + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sec_warehouse", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "sec_warehouse", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "set_warehouse", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Set Source Warehouse", - "length": 0, - "no_copy": 0, - "options": "Warehouse", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "set_warehouse", + "fieldtype": "Link", + "label": "Set Source Warehouse", + "options": "Warehouse", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "col_break_warehouse", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "col_break_warehouse", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Required only for sample item.", - "fieldname": "to_warehouse", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "To Warehouse", - "length": 0, - "no_copy": 1, - "oldfieldname": "to_warehouse", - "oldfieldtype": "Link", - "options": "Warehouse", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "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 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "items_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Section Break", - "options": "fa fa-shopping-cart", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "items_section", + "fieldtype": "Section Break", + "oldfieldtype": "Section Break", + "options": "fa fa-shopping-cart" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "scan_barcode", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Scan Barcode", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "scan_barcode", + "fieldtype": "Data", + "label": "Scan Barcode" + }, { - "allow_bulk_edit": 1, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "items", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Items", - "length": 0, - "no_copy": 0, - "oldfieldname": "delivery_note_details", - "oldfieldtype": "Table", - "options": "Delivery Note Item", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_bulk_edit": 1, + "fieldname": "items", + "fieldtype": "Table", + "label": "Items", + "oldfieldname": "delivery_note_details", + "oldfieldtype": "Table", + "options": "Delivery Note Item", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "collapsible_depends_on": "", - "columns": 0, - "fieldname": "pricing_rule_details", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Pricing Rules", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "pricing_rule_details", + "fieldtype": "Section Break", + "label": "Pricing Rules" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "collapsible_depends_on": "", - "columns": 0, - "fieldname": "pricing_rules", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Pricing Rule Detail", - "length": 0, - "no_copy": 0, - "options": "Pricing Rule Detail", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "pricing_rules", + "fieldtype": "Table", + "label": "Pricing Rule Detail", + "options": "Pricing Rule Detail", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "packed_items", - "columns": 0, - "fieldname": "packing_list", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Packing List", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Section Break", - "options": "fa fa-suitcase", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "collapsible_depends_on": "packed_items", + "fieldname": "packing_list", + "fieldtype": "Section Break", + "label": "Packing List", + "oldfieldtype": "Section Break", + "options": "fa fa-suitcase", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "packed_items", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Packed Items", - "length": 0, - "no_copy": 0, - "oldfieldname": "packing_details", - "oldfieldtype": "Table", - "options": "Packed Item", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "packed_items", + "fieldtype": "Table", + "label": "Packed Items", + "oldfieldname": "packing_details", + "oldfieldtype": "Table", + "options": "Packed Item", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "product_bundle_help", - "fieldtype": "HTML", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Product Bundle Help", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "product_bundle_help", + "fieldtype": "HTML", + "label": "Product Bundle Help", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_31", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_31", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "total_qty", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Quantity", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "total_qty", + "fieldtype": "Float", + "label": "Total Quantity", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "base_total", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total (Company Currency)", - "length": 0, - "no_copy": 0, - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "base_total", + "fieldtype": "Currency", + "label": "Total (Company Currency)", + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "base_net_total", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Net Total (Company Currency)", - "length": 0, - "no_copy": 0, - "oldfieldname": "net_total", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "150px", - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "base_net_total", + "fieldtype": "Currency", + "label": "Net Total (Company Currency)", + "oldfieldname": "net_total", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "print_hide": 1, + "print_width": "150px", + "read_only": 1, "width": "150px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "column_break_33", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_33", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "total", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total", - "length": 0, - "no_copy": 0, - "options": "currency", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "total", + "fieldtype": "Currency", + "label": "Total", + "options": "currency", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "net_total", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Net Total", - "length": 0, - "no_copy": 0, - "options": "currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "net_total", + "fieldtype": "Currency", + "label": "Net Total", + "options": "currency", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "total_net_weight", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Net Weight", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "total_net_weight", + "fieldtype": "Float", + "label": "Total Net Weight", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "taxes_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Taxes and Charges", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Section Break", - "options": "fa fa-money", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "taxes_section", + "fieldtype": "Section Break", + "label": "Taxes and Charges", + "oldfieldtype": "Section Break", + "options": "fa fa-money" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "tax_category", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Tax Category", - "length": 0, - "no_copy": 0, - "options": "Tax Category", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "tax_category", + "fieldtype": "Link", + "label": "Tax Category", + "options": "Tax Category", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_39", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_39", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "shipping_rule", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Shipping Rule", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Button", - "options": "Shipping Rule", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "shipping_rule", + "fieldtype": "Link", + "label": "Shipping Rule", + "oldfieldtype": "Button", + "options": "Shipping Rule", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_41", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_41", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "If you have created a standard template in Sales Taxes and Charges Template, select one and click on the button below.", - "fieldname": "taxes_and_charges", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Sales Taxes and Charges Template", - "length": 0, - "no_copy": 0, - "oldfieldname": "charge", - "oldfieldtype": "Link", - "options": "Sales Taxes and Charges Template", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "description": "If you have created a standard template in Sales Taxes and Charges Template, select one and click on the button below.", + "fieldname": "taxes_and_charges", + "fieldtype": "Link", + "label": "Sales Taxes and Charges Template", + "oldfieldname": "charge", + "oldfieldtype": "Link", + "options": "Sales Taxes and Charges Template", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "taxes", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Sales Taxes and Charges", - "length": 0, - "no_copy": 0, - "oldfieldname": "other_charges", - "oldfieldtype": "Table", - "options": "Sales Taxes and Charges", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "taxes", + "fieldtype": "Table", + "label": "Sales Taxes and Charges", + "oldfieldname": "other_charges", + "oldfieldtype": "Table", + "options": "Sales Taxes and Charges" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "sec_tax_breakup", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Tax Breakup", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "sec_tax_breakup", + "fieldtype": "Section Break", + "label": "Tax Breakup" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "other_charges_calculation", - "fieldtype": "Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Taxes and Charges Calculation", - "length": 0, - "no_copy": 1, - "oldfieldtype": "HTML", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "other_charges_calculation", + "fieldtype": "Text", + "label": "Taxes and Charges Calculation", + "no_copy": 1, + "oldfieldtype": "HTML", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_44", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "section_break_44", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "base_total_taxes_and_charges", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Taxes and Charges (Company Currency)", - "length": 0, - "no_copy": 0, - "oldfieldname": "other_charges_total", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "150px", - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "base_total_taxes_and_charges", + "fieldtype": "Currency", + "label": "Total Taxes and Charges (Company Currency)", + "oldfieldname": "other_charges_total", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "print_hide": 1, + "print_width": "150px", + "read_only": 1, "width": "150px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_47", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_47", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "total_taxes_and_charges", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Taxes and Charges", - "length": 0, - "no_copy": 0, - "options": "currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "total_taxes_and_charges", + "fieldtype": "Currency", + "label": "Total Taxes and Charges", + "options": "currency", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "discount_amount", - "columns": 0, - "fieldname": "section_break_49", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Additional Discount", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "collapsible_depends_on": "discount_amount", + "fieldname": "section_break_49", + "fieldtype": "Section Break", + "label": "Additional Discount" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Grand Total", - "fieldname": "apply_discount_on", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Apply Additional Discount On", - "length": 0, - "no_copy": 0, - "options": "\nGrand Total\nNet Total", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "Grand Total", + "fieldname": "apply_discount_on", + "fieldtype": "Select", + "label": "Apply Additional Discount On", + "options": "\nGrand Total\nNet Total", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "base_discount_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Additional Discount Amount (Company Currency)", - "length": 0, - "no_copy": 0, - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "base_discount_amount", + "fieldtype": "Currency", + "label": "Additional Discount Amount (Company Currency)", + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_51", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_51", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "additional_discount_percentage", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Additional Discount Percentage", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "additional_discount_percentage", + "fieldtype": "Float", + "label": "Additional Discount Percentage", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "discount_amount", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Additional Discount Amount", - "length": 0, - "no_copy": 0, - "options": "currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "discount_amount", + "fieldtype": "Currency", + "label": "Additional Discount Amount", + "options": "currency", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "totals", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Section Break", - "options": "fa fa-money", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "totals", + "fieldtype": "Section Break", + "oldfieldtype": "Section Break", + "options": "fa fa-money" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "base_grand_total", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Grand Total (Company Currency)", - "length": 0, - "no_copy": 0, - "oldfieldname": "grand_total", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "150px", - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "base_grand_total", + "fieldtype": "Currency", + "label": "Grand Total (Company Currency)", + "oldfieldname": "grand_total", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "print_hide": 1, + "print_width": "150px", + "read_only": 1, "width": "150px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "base_rounding_adjustment", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Rounding Adjustment (Company Currency)", - "length": 0, - "no_copy": 1, - "options": "Company:company:default_currency", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "base_rounding_adjustment", + "fieldtype": "Currency", + "label": "Rounding Adjustment (Company Currency)", + "no_copy": 1, + "options": "Company:company:default_currency", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "base_rounded_total", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Rounded Total (Company Currency)", - "length": 0, - "no_copy": 0, - "oldfieldname": "rounded_total", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "150px", - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "base_rounded_total", + "fieldtype": "Currency", + "label": "Rounded Total (Company Currency)", + "oldfieldname": "rounded_total", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "print_hide": 1, + "print_width": "150px", + "read_only": 1, "width": "150px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "In Words will be visible once you save the Delivery Note.", - "fieldname": "base_in_words", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "In Words (Company Currency)", - "length": 0, - "no_copy": 0, - "oldfieldname": "in_words", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "200px", - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "description": "In Words will be visible once you save the Delivery Note.", + "fieldname": "base_in_words", + "fieldtype": "Data", + "label": "In Words (Company Currency)", + "oldfieldname": "in_words", + "oldfieldtype": "Data", + "print_hide": 1, + "print_width": "200px", + "read_only": 1, "width": "200px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "oldfieldtype": "Column Break", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break3", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "grand_total", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Grand Total", - "length": 0, - "no_copy": 0, - "oldfieldname": "grand_total_export", - "oldfieldtype": "Currency", - "options": "currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "150px", - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "grand_total", + "fieldtype": "Currency", + "in_list_view": 1, + "label": "Grand Total", + "oldfieldname": "grand_total_export", + "oldfieldtype": "Currency", + "options": "currency", + "print_width": "150px", + "read_only": 1, "width": "150px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "rounding_adjustment", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Rounding Adjustment", - "length": 0, - "no_copy": 1, - "options": "currency", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "rounding_adjustment", + "fieldtype": "Currency", + "label": "Rounding Adjustment", + "no_copy": 1, + "options": "currency", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 1, - "collapsible": 0, - "columns": 0, - "fieldname": "rounded_total", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Rounded Total", - "length": 0, - "no_copy": 0, - "oldfieldname": "rounded_total_export", - "oldfieldtype": "Currency", - "options": "currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "150px", - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "bold": 1, + "fieldname": "rounded_total", + "fieldtype": "Currency", + "label": "Rounded Total", + "oldfieldname": "rounded_total_export", + "oldfieldtype": "Currency", + "options": "currency", + "print_width": "150px", + "read_only": 1, "width": "150px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "In Words (Export) will be visible once you save the Delivery Note.", - "fieldname": "in_words", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "In Words", - "length": 0, - "no_copy": 0, - "oldfieldname": "in_words_export", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "150px", - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "description": "In Words (Export) will be visible once you save the Delivery Note.", + "fieldname": "in_words", + "fieldtype": "Data", + "label": "In Words", + "oldfieldname": "in_words_export", + "oldfieldtype": "Data", + "print_hide": 1, + "print_width": "150px", + "read_only": 1, "width": "150px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "terms", - "columns": 0, - "fieldname": "terms_section_break", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Terms and Conditions", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Section Break", - "options": "fa fa-legal", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "collapsible_depends_on": "terms", + "fieldname": "terms_section_break", + "fieldtype": "Section Break", + "label": "Terms and Conditions", + "oldfieldtype": "Section Break", + "options": "fa fa-legal" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "tc_name", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Terms", - "length": 0, - "no_copy": 0, - "oldfieldname": "tc_name", - "oldfieldtype": "Link", - "options": "Terms and Conditions", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "tc_name", + "fieldtype": "Link", + "label": "Terms", + "oldfieldname": "tc_name", + "oldfieldtype": "Link", + "options": "Terms and Conditions", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "terms", - "fieldtype": "Text Editor", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Terms and Conditions Details", - "length": 0, - "no_copy": 0, - "oldfieldname": "terms", - "oldfieldtype": "Text Editor", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "terms", + "fieldtype": "Text Editor", + "label": "Terms and Conditions Details", + "oldfieldname": "terms", + "oldfieldtype": "Text Editor" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "transporter", - "columns": 0, - "fieldname": "transporter_info", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Transporter Info", - "length": 0, - "no_copy": 0, - "options": "fa fa-truck", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "collapsible_depends_on": "transporter", + "fieldname": "transporter_info", + "fieldtype": "Section Break", + "label": "Transporter Info", + "options": "fa fa-truck", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "transporter", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Transporter", - "length": 0, - "no_copy": 0, - "options": "Supplier", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "150px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "transporter", + "fieldtype": "Link", + "label": "Transporter", + "options": "Supplier", + "print_hide": 1, + "print_width": "150px", "width": "150px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "driver", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Driver", - "length": 0, - "no_copy": 0, - "options": "Driver", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "driver", + "fieldtype": "Link", + "label": "Driver", + "options": "Driver", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "lr_no", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Transport Receipt No", - "length": 0, - "no_copy": 0, - "oldfieldname": "lr_no", - "oldfieldtype": "Data", - "options": "", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "100px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "lr_no", + "fieldtype": "Data", + "label": "Transport Receipt No", + "oldfieldname": "lr_no", + "oldfieldtype": "Data", + "print_hide": 1, + "print_width": "100px", "width": "100px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "vehicle_no", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Vehicle No", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "vehicle_no", + "fieldtype": "Data", + "label": "Vehicle No", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "col_break34", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": "50%", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "col_break34", + "fieldtype": "Column Break", + "print_width": "50%", "width": "50%" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "transporter.name", - "fieldname": "transporter_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Transporter Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "transporter.name", + "fieldname": "transporter_name", + "fieldtype": "Data", + "label": "Transporter Name", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fetch_from": "driver.full_name", - "fieldname": "driver_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Driver Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fetch_from": "driver.full_name", + "fieldname": "driver_name", + "fieldtype": "Data", + "label": "Driver Name", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Today", - "description": "", - "fieldname": "lr_date", - "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Transport Receipt Date", - "length": 0, - "no_copy": 0, - "oldfieldname": "lr_date", - "oldfieldtype": "Date", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "100px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "default": "Today", + "fieldname": "lr_date", + "fieldtype": "Date", + "label": "Transport Receipt Date", + "oldfieldname": "lr_date", + "oldfieldtype": "Date", + "print_hide": 1, + "print_width": "100px", "width": "100px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "more_info", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "More Information", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Section Break", - "options": "fa fa-file-text", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "more_info", + "fieldtype": "Section Break", + "label": "More Information", + "oldfieldtype": "Section Break", + "options": "fa fa-file-text", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Track this Delivery Note against any Project", - "fieldname": "project", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Project", - "length": 0, - "no_copy": 0, - "oldfieldname": "project", - "oldfieldtype": "Link", - "options": "Project", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "description": "Track this Delivery Note against any Project", + "fieldname": "project", + "fieldtype": "Link", + "label": "Project", + "oldfieldname": "project", + "oldfieldtype": "Link", + "options": "Project" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "campaign", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Campaign", - "length": 0, - "no_copy": 0, - "oldfieldname": "campaign", - "oldfieldtype": "Link", - "options": "Campaign", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "campaign", + "fieldtype": "Link", + "label": "Campaign", + "oldfieldname": "campaign", + "oldfieldtype": "Link", + "options": "Campaign", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "source", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Source", - "length": 0, - "no_copy": 0, - "oldfieldname": "source", - "oldfieldtype": "Select", - "options": "Lead Source", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "source", + "fieldtype": "Link", + "label": "Source", + "oldfieldname": "source", + "oldfieldtype": "Select", + "options": "Lead Source", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break5", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "oldfieldtype": "Column Break", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "50%", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "column_break5", + "fieldtype": "Column Break", + "oldfieldtype": "Column Break", + "print_hide": 1, + "print_width": "50%", "width": "50%" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "per_billed", - "fieldtype": "Percent", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "% Amount Billed", - "length": 0, - "no_copy": 1, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "per_billed", + "fieldtype": "Percent", + "label": "% Amount Billed", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "customer_group", - "fieldtype": "Link", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Customer Group", - "length": 0, - "no_copy": 0, - "options": "Customer Group", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "customer_group", + "fieldtype": "Link", + "hidden": 1, + "label": "Customer Group", + "options": "Customer Group", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "", - "fieldname": "territory", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Territory", - "length": 0, - "no_copy": 0, - "options": "Territory", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "territory", + "fieldtype": "Link", + "label": "Territory", + "options": "Territory", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "printing_details", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Printing Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "printing_details", + "fieldtype": "Section Break", + "label": "Printing Details" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "letter_head", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Letter Head", - "length": 0, - "no_copy": 0, - "oldfieldname": "letter_head", - "oldfieldtype": "Link", - "options": "Letter Head", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "fieldname": "letter_head", + "fieldtype": "Link", + "label": "Letter Head", + "oldfieldname": "letter_head", + "oldfieldtype": "Link", + "options": "Letter Head", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "select_print_heading", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Print Heading", - "length": 0, - "no_copy": 1, - "oldfieldname": "select_print_heading", - "oldfieldtype": "Link", - "options": "Print Heading", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "fieldname": "select_print_heading", + "fieldtype": "Link", + "label": "Print Heading", + "no_copy": 1, + "oldfieldname": "select_print_heading", + "oldfieldtype": "Link", + "options": "Print Heading", + "print_hide": 1, + "report_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "language", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Print Language", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "language", + "fieldtype": "Data", + "label": "Print Language", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_88", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_88", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "print_without_amount", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Print Without Amount", - "length": 0, - "no_copy": 0, - "oldfieldname": "print_without_amount", - "oldfieldtype": "Check", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "default": "0", + "fieldname": "print_without_amount", + "fieldtype": "Check", + "label": "Print Without Amount", + "oldfieldname": "print_without_amount", + "oldfieldtype": "Check", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "group_same_items", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Group same items", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "allow_on_submit": 1, + "default": "0", + "fieldname": "group_same_items", + "fieldtype": "Check", + "label": "Group same items", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "columns": 0, - "fieldname": "section_break_83", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Status", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "fieldname": "section_break_83", + "fieldtype": "Section Break", + "label": "Status" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Draft", - "fieldname": "status", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 1, - "label": "Status", - "length": 0, - "no_copy": 1, - "oldfieldname": "status", - "oldfieldtype": "Select", - "options": "\nDraft\nTo Bill\nCompleted\nCancelled\nClosed", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "150px", - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 1, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "default": "Draft", + "fieldname": "status", + "fieldtype": "Select", + "in_standard_filter": 1, + "label": "Status", + "no_copy": 1, + "oldfieldname": "status", + "oldfieldtype": "Select", + "options": "\nDraft\nTo Bill\nCompleted\nCancelled\nClosed", + "print_hide": 1, + "print_width": "150px", + "read_only": 1, + "reqd": 1, + "search_index": 1, "width": "150px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "eval:!doc.__islocal", - "description": "% of materials delivered against this Delivery Note", - "fieldname": "per_installed", - "fieldtype": "Percent", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "% Installed", - "length": 0, - "no_copy": 1, - "oldfieldname": "per_installed", - "oldfieldtype": "Currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "eval:!doc.__islocal", + "description": "% of materials delivered against this Delivery Note", + "fieldname": "per_installed", + "fieldtype": "Percent", + "in_list_view": 1, + "label": "% Installed", + "no_copy": 1, + "oldfieldname": "per_installed", + "oldfieldtype": "Currency", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "installation_status", - "fieldtype": "Select", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Installation Status", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "installation_status", + "fieldtype": "Select", + "hidden": 1, + "label": "Installation Status", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_89", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_89", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "excise_page", - "fieldtype": "Data", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Excise Page Number", - "length": 0, - "no_copy": 0, - "oldfieldname": "excise_page", - "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "excise_page", + "fieldtype": "Data", + "hidden": 1, + "label": "Excise Page Number", + "oldfieldname": "excise_page", + "oldfieldtype": "Data", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "instructions", - "fieldtype": "Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Instructions", - "length": 0, - "no_copy": 0, - "oldfieldname": "instructions", - "oldfieldtype": "Text", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "instructions", + "fieldtype": "Text", + "label": "Instructions", + "oldfieldname": "instructions", + "oldfieldtype": "Text" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "subscription_section", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Subscription Section", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "subscription_section", + "fieldtype": "Section Break", + "label": "Subscription Section" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "auto_repeat", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Auto Repeat", - "length": 0, - "no_copy": 1, - "options": "Auto Repeat", - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "auto_repeat", + "fieldtype": "Link", + "label": "Auto Repeat", + "no_copy": 1, + "options": "Auto Repeat", + "print_hide": 1, + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "total_commission", - "columns": 0, - "fieldname": "sales_team_section_break", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Commission", - "length": 0, - "no_copy": 0, - "oldfieldtype": "Section Break", - "options": "fa fa-group", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "collapsible_depends_on": "total_commission", + "fieldname": "sales_team_section_break", + "fieldtype": "Section Break", + "label": "Commission", + "oldfieldtype": "Section Break", + "options": "fa fa-group", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sales_partner", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Sales Partner", - "length": 0, - "no_copy": 0, - "oldfieldname": "sales_partner", - "oldfieldtype": "Link", - "options": "Sales Partner", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "150px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "sales_partner", + "fieldtype": "Link", + "label": "Sales Partner", + "oldfieldname": "sales_partner", + "oldfieldtype": "Link", + "options": "Sales Partner", + "print_hide": 1, + "print_width": "150px", "width": "150px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break7", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "50%", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "column_break7", + "fieldtype": "Column Break", + "print_hide": 1, + "print_width": "50%", "width": "50%" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "commission_rate", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Commission Rate (%)", - "length": 0, - "no_copy": 0, - "oldfieldname": "commission_rate", - "oldfieldtype": "Currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": "100px", - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0, + "fieldname": "commission_rate", + "fieldtype": "Float", + "label": "Commission Rate (%)", + "oldfieldname": "commission_rate", + "oldfieldtype": "Currency", + "print_hide": 1, + "print_width": "100px", "width": "100px" - }, + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "total_commission", - "fieldtype": "Currency", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Total Commission", - "length": 0, - "no_copy": 0, - "oldfieldname": "total_commission", - "oldfieldtype": "Currency", - "options": "Company:company:default_currency", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "total_commission", + "fieldtype": "Currency", + "label": "Total Commission", + "oldfieldname": "total_commission", + "oldfieldtype": "Currency", + "options": "Company:company:default_currency", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 1, - "collapsible_depends_on": "sales_team", - "columns": 0, - "fieldname": "section_break1", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Sales Team", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "collapsible": 1, + "collapsible_depends_on": "sales_team", + "fieldname": "section_break1", + "fieldtype": "Section Break", + "label": "Sales Team", + "print_hide": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 1, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "sales_team", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Sales Team1", - "length": 0, - "no_copy": 0, - "oldfieldname": "sales_team", - "oldfieldtype": "Table", - "options": "Sales Team", - "permlevel": 0, - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "allow_on_submit": 1, + "fieldname": "sales_team", + "fieldtype": "Table", + "label": "Sales Team", + "oldfieldname": "sales_team", + "oldfieldtype": "Table", + "options": "Sales Team", + "print_hide": 1 + }, + { + "fieldname": "pick_list", + "fieldtype": "Link", + "hidden": 1, + "label": "Pick List", + "options": "Pick List", + "read_only": 1 + }, + { + "fieldname": "section_break_18", + "fieldtype": "Section Break" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-truck", - "idx": 146, - "image_view": 0, - "in_create": 0, - "is_submittable": 1, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "menu_index": 0, - "modified": "2019-02-13 01:06:29.783590", - "modified_by": "Administrator", - "module": "Stock", - "name": "Delivery Note", - "owner": "Administrator", + ], + "icon": "fa fa-truck", + "idx": 146, + "is_submittable": 1, + "modified": "2019-08-26 07:37:39.766014", + "modified_by": "Administrator", + "module": "Stock", + "name": "Delivery Note", + "owner": "Administrator", "permissions": [ { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Stock User", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Stock User", + "share": 1, + "submit": 1, "write": 1 - }, + }, { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Stock Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Stock Manager", + "share": 1, + "submit": 1, "write": 1 - }, + }, { - "amend": 1, - "cancel": 1, - "create": 1, - "delete": 1, - "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Sales User", - "set_user_permissions": 0, - "share": 1, - "submit": 1, + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Sales User", + "share": 1, + "submit": 1, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, - "read": 1, - "report": 1, - "role": "Accounts User", - "set_user_permissions": 0, - "share": 0, - "submit": 0, - "write": 0 - }, + "read": 1, + "report": 1, + "role": "Accounts User" + }, { - "amend": 0, - "cancel": 0, - "create": 0, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 1, - "print": 0, - "read": 1, - "report": 0, - "role": "Stock Manager", - "set_user_permissions": 0, - "share": 0, - "submit": 0, + "permlevel": 1, + "read": 1, + "role": "Stock Manager", "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 1, - "search_fields": "status,customer,customer_name, territory,base_grand_total", - "show_name_in_global_search": 1, - "sort_field": "modified", - "sort_order": "DESC", - "timeline_field": "customer", - "title_field": "title", - "track_changes": 1, - "track_seen": 1, - "track_views": 0 -} + ], + "search_fields": "status,customer,customer_name, territory,base_grand_total", + "show_name_in_global_search": 1, + "sort_field": "modified", + "sort_order": "DESC", + "timeline_field": "customer", + "title_field": "title", + "track_changes": 1, + "track_seen": 1 +} \ No newline at end of file diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index 2de9b975da..f79d127984 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -166,24 +166,7 @@ class DeliveryNote(SellingController): frappe.throw(_("Customer {0} does not belong to project {1}").format(self.customer, self.project)) def validate_for_items(self): - check_list, chk_dupl_itm = [], [] - if cint(frappe.db.get_single_value("Selling Settings", "allow_multiple_items")): - return - for d in self.get('items'): - e = [d.item_code, d.description, d.warehouse, d.against_sales_order or d.against_sales_invoice, d.batch_no or ''] - f = [d.item_code, d.description, d.against_sales_order or d.against_sales_invoice] - - if frappe.db.get_value("Item", d.item_code, "is_stock_item") == 1: - if e in check_list: - frappe.msgprint(_("Note: Item {0} entered multiple times").format(d.item_code)) - else: - check_list.append(e) - else: - if f in chk_dupl_itm: - frappe.msgprint(_("Note: Item {0} entered multiple times").format(d.item_code)) - else: - chk_dupl_itm.append(f) #Customer Provided parts will have zero valuation rate if frappe.db.get_value('Item', d.item_code, 'is_customer_provided_item'): d.allow_zero_valuation_rate = 1 diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 1568729344..518fe74889 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -124,6 +124,7 @@ class Item(WebsiteGenerator): self.update_defaults_from_item_group() self.validate_auto_reorder_enabled_in_stock_settings() self.cant_change() + self.update_show_in_website() if not self.get("__islocal"): self.old_item_group = frappe.db.get_value(self.doctype, self.name, "item_group") @@ -476,6 +477,10 @@ class Item(WebsiteGenerator): [self.remove(d) for d in to_remove] + def update_show_in_website(self): + if self.disabled: + self.show_in_website = False + def update_template_tables(self): template = frappe.get_doc("Item", self.variant_of) diff --git a/erpnext/stock/doctype/material_request/material_request.js b/erpnext/stock/doctype/material_request/material_request.js index 96e31ff6ff..99195c300d 100644 --- a/erpnext/stock/doctype/material_request/material_request.js +++ b/erpnext/stock/doctype/material_request/material_request.js @@ -8,6 +8,7 @@ frappe.ui.form.on('Material Request', { setup: function(frm) { frm.custom_make_buttons = { 'Stock Entry': 'Issue Material', + 'Pick List': 'Pick List', 'Purchase Order': 'Purchase Order', 'Request for Quotation': 'Request for Quotation', 'Supplier Quotation': 'Supplier Quotation', @@ -55,8 +56,13 @@ frappe.ui.form.on('Material Request', { if (frm.doc.docstatus == 1 && frm.doc.status != 'Stopped') { if (flt(frm.doc.per_ordered, 2) < 100) { - // make + let add_create_pick_list_button = () => { + frm.add_custom_button(__('Pick List'), + () => frm.events.create_pick_list(frm), __('Create')); + } + if (frm.doc.material_request_type === "Material Transfer") { + add_create_pick_list_button(); frm.add_custom_button(__("Transfer Material"), () => frm.events.make_stock_entry(frm), __('Create')); } @@ -258,6 +264,13 @@ frappe.ui.form.on('Material Request', { }); }, + create_pick_list: (frm) => { + frappe.model.open_mapped_doc({ + method: "erpnext.stock.doctype.material_request.material_request.create_pick_list", + frm: frm + }); + }, + raise_work_orders: function(frm) { frappe.call({ method:"erpnext.stock.doctype.material_request.material_request.raise_work_orders", diff --git a/erpnext/stock/doctype/material_request/material_request.py b/erpnext/stock/doctype/material_request/material_request.py index f2fe44879d..44e890cc50 100644 --- a/erpnext/stock/doctype/material_request/material_request.py +++ b/erpnext/stock/doctype/material_request/material_request.py @@ -502,3 +502,28 @@ def raise_work_orders(material_request): frappe.throw(_("Productions Orders cannot be raised for:") + '\n' + new_line_sep(errors)) return work_orders + +@frappe.whitelist() +def create_pick_list(source_name, target_doc=None): + doc = get_mapped_doc('Material Request', source_name, { + 'Material Request': { + 'doctype': 'Pick List', + 'field_map': { + 'material_request_type': 'purpose' + }, + 'validation': { + 'docstatus': ['=', 1] + } + }, + 'Material Request Item': { + 'doctype': 'Pick List Item', + 'field_map': { + 'name': 'material_request_item', + 'qty': 'stock_qty' + }, + }, + }, target_doc) + + doc.set_item_locations() + + return doc \ No newline at end of file diff --git a/erpnext/stock/doctype/material_request/material_request_dashboard.py b/erpnext/stock/doctype/material_request/material_request_dashboard.py index adfab86cc4..cbd64784c6 100644 --- a/erpnext/stock/doctype/material_request/material_request_dashboard.py +++ b/erpnext/stock/doctype/material_request/material_request_dashboard.py @@ -8,7 +8,7 @@ def get_data(): 'transactions': [ { 'label': _('Related'), - 'items': ['Request for Quotation', 'Supplier Quotation', 'Purchase Order', "Stock Entry"] + 'items': ['Request for Quotation', 'Supplier Quotation', 'Purchase Order', 'Stock Entry', 'Pick List'] }, { 'label': _('Manufacturing'), diff --git a/erpnext/stock/doctype/pick_list/__init__.py b/erpnext/stock/doctype/pick_list/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/stock/doctype/pick_list/pick_list.js b/erpnext/stock/doctype/pick_list/pick_list.js new file mode 100644 index 0000000000..3f66743f07 --- /dev/null +++ b/erpnext/stock/doctype/pick_list/pick_list.js @@ -0,0 +1,180 @@ +// Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Pick List', { + setup: (frm) => { + frm.custom_make_buttons = { + 'Delivery Note': 'Delivery Note', + 'Stock Entry': 'Stock Entry', + }; + frm.set_query('parent_warehouse', () => { + return { + filters: { + 'is_group': 1, + 'company': frm.doc.company + } + }; + }); + frm.set_query('work_order', () => { + return { + query: 'erpnext.stock.doctype.pick_list.pick_list.get_pending_work_orders', + filters: { + 'company': frm.doc.company + } + }; + }); + frm.set_query('material_request', () => { + return { + filters: { + 'material_request_type': ['=', frm.doc.purpose] + } + }; + }); + frm.set_query('item_code', 'locations', () => { + return { + filters: { + is_stock_item: 1 + } + }; + }); + }, + get_item_locations: (frm) => { + if (!frm.doc.locations || !frm.doc.locations.length) { + frappe.msgprint(__('First add items in the Item Locations table')); + } else { + frm.call('set_item_locations'); + } + }, + refresh: (frm) => { + frm.trigger('add_get_items_button'); + if (frm.doc.docstatus === 1) { + frappe.xcall('erpnext.stock.doctype.pick_list.pick_list.target_document_exists', { + 'pick_list_name': frm.doc.name, + 'purpose': frm.doc.purpose + }).then(target_document_exists => { + if (target_document_exists) return; + if (frm.doc.purpose === 'Delivery against Sales Order') { + frm.add_custom_button(__('Delivery Note'), () => frm.trigger('create_delivery_note'), __('Create')); + } else { + frm.add_custom_button(__('Stock Entry'), () => frm.trigger('create_stock_entry'), __('Create')); + } + }); + } + }, + work_order: (frm) => { + frappe.db.get_value('Work Order', + frm.doc.work_order, + ['qty', 'material_transferred_for_manufacturing'] + ).then(data => { + let qty_data = data.message; + let max = qty_data.qty - qty_data.material_transferred_for_manufacturing; + frappe.prompt({ + fieldtype: 'Float', + label: __('Qty of Finished Goods Item'), + fieldname: 'qty', + description: __('Max: {0}', [max]), + default: max + }, (data) => { + frm.set_value('for_qty', data.qty); + if (data.qty > max) { + frappe.msgprint(__('Quantity must not be more than {0}', [max])); + return; + } + frm.clear_table('locations'); + erpnext.utils.map_current_doc({ + method: 'erpnext.manufacturing.doctype.work_order.work_order.create_pick_list', + target: frm, + source_name: frm.doc.work_order + }); + }, __('Select Quantity'), __('Get Items')); + }); + }, + material_request: (frm) => { + erpnext.utils.map_current_doc({ + method: 'erpnext.stock.doctype.material_request.material_request.create_pick_list', + target: frm, + source_name: frm.doc.material_request + }); + }, + purpose: (frm) => { + frm.clear_table('locations'); + frm.trigger('add_get_items_button'); + }, + create_delivery_note: (frm) => { + frappe.model.open_mapped_doc({ + method: 'erpnext.stock.doctype.pick_list.pick_list.create_delivery_note', + frm: frm + }); + }, + create_stock_entry: (frm) => { + frappe.xcall('erpnext.stock.doctype.pick_list.pick_list.create_stock_entry', { + 'pick_list': frm.doc, + }).then(stock_entry => { + frappe.model.sync(stock_entry); + frappe.set_route("Form", 'Stock Entry', stock_entry.name); + }); + }, + add_get_items_button: (frm) => { + let purpose = frm.doc.purpose; + if (purpose != 'Delivery against Sales Order' || frm.doc.docstatus !== 0) return; + let get_query_filters = { + docstatus: 1, + per_delivered: ['<', 100], + status: ['!=', ''], + customer: frm.doc.customer + }; + frm.get_items_btn = frm.add_custom_button(__('Get Items'), () => { + if (!frm.doc.customer) { + frappe.msgprint(__('Please select Customer first')); + return; + } + erpnext.utils.map_current_doc({ + method: 'erpnext.selling.doctype.sales_order.sales_order.create_pick_list', + source_doctype: 'Sales Order', + target: frm, + setters: { + company: frm.doc.company, + customer: frm.doc.customer + }, + date_field: 'transaction_date', + get_query_filters: get_query_filters + }); + }); + } +}); + +frappe.ui.form.on('Pick List Item', { + item_code: (frm, cdt, cdn) => { + let row = frappe.get_doc(cdt, cdn); + if (row.item_code) { + get_item_details(row.item_code).then(data => { + frappe.model.set_value(cdt, cdn, 'uom', data.stock_uom); + frappe.model.set_value(cdt, cdn, 'stock_uom', data.stock_uom); + frappe.model.set_value(cdt, cdn, 'conversion_factor', 1); + }); + } + }, + uom: (frm, cdt, cdn) => { + let row = frappe.get_doc(cdt, cdn); + if (row.uom) { + get_item_details(row.item_code, row.uom).then(data => { + frappe.model.set_value(cdt, cdn, 'conversion_factor', data.conversion_factor); + }); + } + }, + qty: (frm, cdt, cdn) => { + let row = frappe.get_doc(cdt, cdn); + frappe.model.set_value(cdt, cdn, 'stock_qty', row.qty * row.conversion_factor); + }, + conversion_factor: (frm, cdt, cdn) => { + let row = frappe.get_doc(cdt, cdn); + frappe.model.set_value(cdt, cdn, 'stock_qty', row.qty * row.conversion_factor); + } +}); + +function get_item_details(item_code, uom=null) { + return frappe.xcall('erpnext.stock.doctype.pick_list.pick_list.get_item_details', { + item_code, + uom + }); +} \ No newline at end of file diff --git a/erpnext/stock/doctype/pick_list/pick_list.json b/erpnext/stock/doctype/pick_list/pick_list.json new file mode 100644 index 0000000000..8d5ef3d12a --- /dev/null +++ b/erpnext/stock/doctype/pick_list/pick_list.json @@ -0,0 +1,184 @@ +{ + "autoname": "naming_series:", + "creation": "2019-07-11 16:03:13.681045", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "naming_series", + "company", + "purpose", + "customer", + "work_order", + "material_request", + "for_qty", + "column_break_4", + "parent_warehouse", + "get_item_locations", + "section_break_6", + "locations", + "amended_from" + ], + "fields": [ + { + "fieldname": "company", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Company", + "options": "Company", + "reqd": 1 + }, + { + "fieldname": "column_break_4", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_6", + "fieldtype": "Section Break" + }, + { + "description": "Items under this warehouse will be suggested", + "fieldname": "parent_warehouse", + "fieldtype": "Link", + "label": "Parent Warehouse", + "options": "Warehouse" + }, + { + "depends_on": "eval:doc.purpose==='Delivery against Sales Order'", + "fieldname": "customer", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Customer", + "options": "Customer" + }, + { + "depends_on": "eval:doc.purpose==='Material Transfer for Manufacture'", + "fieldname": "work_order", + "fieldtype": "Link", + "label": "Work Order", + "options": "Work Order" + }, + { + "fieldname": "locations", + "fieldtype": "Table", + "label": "Item Locations", + "options": "Pick List Item" + }, + { + "depends_on": "eval:doc.purpose==='Material Transfer for Manufacture'", + "description": "Qty of raw materials will be decided based on the qty of the Finished Goods Item", + "fieldname": "for_qty", + "fieldtype": "Float", + "label": "Qty of Finished Goods Item", + "read_only": 1 + }, + { + "fieldname": "amended_from", + "fieldtype": "Link", + "label": "Amended From", + "no_copy": 1, + "options": "Pick List", + "print_hide": 1, + "read_only": 1 + }, + { + "default": "Material Transfer for Manufacture", + "fieldname": "purpose", + "fieldtype": "Select", + "label": "Purpose", + "options": "Material Transfer for Manufacture\nMaterial Transfer\nDelivery against Sales Order" + }, + { + "depends_on": "eval:['Material Transfer', 'Material Issue'].includes(doc.purpose)", + "fieldname": "material_request", + "fieldtype": "Link", + "label": "Material Request", + "options": "Material Request" + }, + { + "depends_on": "eval:doc.docstatus===0", + "fieldname": "get_item_locations", + "fieldtype": "Button", + "label": "Get Item Locations" + }, + { + "fieldname": "naming_series", + "fieldtype": "Select", + "label": "Series", + "options": "STO-PICK-.YYYY.-", + "reqd": 1, + "set_only_once": 1 + } + ], + "is_submittable": 1, + "modified": "2019-08-29 21:10:11.572387", + "modified_by": "Administrator", + "module": "Stock", + "name": "Pick List", + "owner": "Administrator", + "permissions": [ + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Stock Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Stock User", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Manufacturing Manager", + "share": 1, + "submit": 1, + "write": 1 + }, + { + "amend": 1, + "cancel": 1, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Manufacturing User", + "share": 1, + "submit": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py new file mode 100644 index 0000000000..c4d8c41ebb --- /dev/null +++ b/erpnext/stock/doctype/pick_list/pick_list.py @@ -0,0 +1,432 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +import frappe +import json +from six import iteritems +from frappe.model.document import Document +from frappe import _ +from collections import OrderedDict +from frappe.utils import floor, flt, today, cint +from frappe.model.mapper import get_mapped_doc, map_child_doc +from erpnext.stock.get_item_details import get_conversion_factor +from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note as create_delivery_note_from_sales_order + +# TODO: Prioritize SO or WO group warehouse + +class PickList(Document): + def before_save(self): + self.set_item_locations() + + def before_submit(self): + for item in self.locations: + if not frappe.get_cached_value('Item', item.item_code, 'has_serial_no'): + continue + if len(item.serial_no.split('\n')) == item.picked_qty: + continue + frappe.throw(_('For item {0} at row {1}, count of serial numbers does not match with the picked quantity') + .format(frappe.bold(item.item_code), frappe.bold(item.idx))) + + def set_item_locations(self): + items = self.aggregate_item_qty() + self.item_location_map = frappe._dict() + + from_warehouses = None + if self.parent_warehouse: + from_warehouses = frappe.db.get_descendants('Warehouse', self.parent_warehouse) + + # reset + self.delete_key('locations') + for item_doc in items: + item_code = item_doc.item_code + + self.item_location_map.setdefault(item_code, + get_available_item_locations(item_code, from_warehouses, self.item_count_map.get(item_code))) + + locations = get_items_with_location_and_quantity(item_doc, self.item_location_map) + + item_doc.idx = None + item_doc.name = None + + for row in locations: + row.update({ + 'picked_qty': row.stock_qty + }) + + location = item_doc.as_dict() + location.update(row) + self.append('locations', location) + + def aggregate_item_qty(self): + locations = self.get('locations') + self.item_count_map = {} + # aggregate qty for same item + item_map = OrderedDict() + for item in locations: + item_code = item.item_code + reference = item.sales_order_item or item.material_request_item + key = (item_code, item.uom, reference) + + item.idx = None + item.name = None + + if item_map.get(key): + item_map[key].qty += item.qty + item_map[key].stock_qty += item.stock_qty + else: + item_map[key] = item + + # maintain count of each item (useful to limit get query) + self.item_count_map.setdefault(item_code, 0) + self.item_count_map[item_code] += item.stock_qty + + return item_map.values() + + +def get_items_with_location_and_quantity(item_doc, item_location_map): + available_locations = item_location_map.get(item_doc.item_code) + locations = [] + + remaining_stock_qty = item_doc.stock_qty + while remaining_stock_qty > 0 and available_locations: + item_location = available_locations.pop(0) + item_location = frappe._dict(item_location) + + stock_qty = remaining_stock_qty if item_location.qty >= remaining_stock_qty else item_location.qty + qty = stock_qty / (item_doc.conversion_factor or 1) + + uom_must_be_whole_number = frappe.db.get_value('UOM', item_doc.uom, 'must_be_whole_number') + if uom_must_be_whole_number: + qty = floor(qty) + stock_qty = qty * item_doc.conversion_factor + if not stock_qty: break + + serial_nos = None + if item_location.serial_no: + serial_nos = '\n'.join(item_location.serial_no[0: cint(stock_qty)]) + + locations.append(frappe._dict({ + 'qty': qty, + 'stock_qty': stock_qty, + 'warehouse': item_location.warehouse, + 'serial_no': serial_nos, + 'batch_no': item_location.batch_no + })) + + remaining_stock_qty -= stock_qty + + qty_diff = item_location.qty - stock_qty + # if extra quantity is available push current warehouse to available locations + if qty_diff > 0: + item_location.qty = qty_diff + if item_location.serial_no: + # set remaining serial numbers + item_location.serial_no = item_location.serial_no[-qty_diff:] + available_locations = [item_location] + available_locations + + # update available locations for the item + item_location_map[item_doc.item_code] = available_locations + return locations + +def get_available_item_locations(item_code, from_warehouses, required_qty): + locations = [] + if frappe.get_cached_value('Item', item_code, 'has_serial_no'): + locations = get_available_item_locations_for_serialized_item(item_code, from_warehouses, required_qty) + elif frappe.get_cached_value('Item', item_code, 'has_batch_no'): + locations = get_available_item_locations_for_batched_item(item_code, from_warehouses, required_qty) + else: + locations = get_available_item_locations_for_other_item(item_code, from_warehouses, required_qty) + + total_qty_available = sum(location.get('qty') for location in locations) + + remaining_qty = required_qty - total_qty_available + + if remaining_qty > 0: + frappe.msgprint(_('{0} units of {1} is not available.') + .format(remaining_qty, frappe.get_desk_link('Item', item_code))) + + return locations + + +def get_available_item_locations_for_serialized_item(item_code, from_warehouses, required_qty): + filters = frappe._dict({ + 'item_code': item_code, + 'warehouse': ['!=', ''] + }) + + if from_warehouses: + filters.warehouse = ['in', from_warehouses] + + serial_nos = frappe.get_all('Serial No', + fields=['name', 'warehouse'], + filters=filters, + limit=required_qty, + order_by='purchase_date', + as_list=1) + + warehouse_serial_nos_map = frappe._dict() + for serial_no, warehouse in serial_nos: + warehouse_serial_nos_map.setdefault(warehouse, []).append(serial_no) + + locations = [] + for warehouse, serial_nos in iteritems(warehouse_serial_nos_map): + locations.append({ + 'qty': len(serial_nos), + 'warehouse': warehouse, + 'serial_no': serial_nos + }) + + return locations + +def get_available_item_locations_for_batched_item(item_code, from_warehouses, required_qty): + warehouse_condition = 'and warehouse in %(warehouses)s' if from_warehouses else '' + batch_locations = frappe.db.sql(""" + SELECT + sle.`warehouse`, + sle.`batch_no`, + SUM(sle.`actual_qty`) AS `qty` + FROM + `tabStock Ledger Entry` sle, `tabBatch` batch + WHERE + sle.batch_no = batch.name + and sle.`item_code`=%(item_code)s + and IFNULL(batch.`expiry_date`, '2200-01-01') > %(today)s + {warehouse_condition} + GROUP BY + `warehouse`, + `batch_no`, + `item_code` + HAVING `qty` > 0 + ORDER BY IFNULL(batch.`expiry_date`, '2200-01-01'), batch.`creation` + """.format(warehouse_condition=warehouse_condition), { #nosec + 'item_code': item_code, + 'today': today(), + 'warehouses': from_warehouses + }, as_dict=1) + + return batch_locations + +def get_available_item_locations_for_other_item(item_code, from_warehouses, required_qty): + # gets all items available in different warehouses + filters = frappe._dict({ + 'item_code': item_code, + 'actual_qty': ['>', 0] + }) + + if from_warehouses: + filters.warehouse = ['in', from_warehouses] + + item_locations = frappe.get_all('Bin', + fields=['warehouse', 'actual_qty as qty'], + filters=filters, + limit=required_qty, + order_by='creation') + + return item_locations + + +@frappe.whitelist() +def create_delivery_note(source_name, target_doc=None): + pick_list = frappe.get_doc('Pick List', source_name) + sales_orders = [d.sales_order for d in pick_list.locations] + sales_orders = set(sales_orders) + + delivery_note = None + for sales_order in sales_orders: + delivery_note = create_delivery_note_from_sales_order(sales_order, + delivery_note, skip_item_mapping=True) + + item_table_mapper = { + 'doctype': 'Delivery Note Item', + 'field_map': { + 'rate': 'rate', + 'name': 'so_detail', + 'parent': 'against_sales_order', + }, + 'condition': lambda doc: abs(doc.delivered_qty) < abs(doc.qty) and doc.delivered_by_supplier!=1 + } + + for location in pick_list.locations: + sales_order_item = frappe.get_cached_doc('Sales Order Item', location.sales_order_item) + dn_item = map_child_doc(sales_order_item, delivery_note, item_table_mapper) + + if dn_item: + dn_item.warehouse = location.warehouse + dn_item.qty = location.picked_qty + dn_item.batch_no = location.batch_no + dn_item.serial_no = location.serial_no + + update_delivery_note_item(sales_order_item, dn_item, delivery_note) + + set_delivery_note_missing_values(delivery_note) + + delivery_note.pick_list = pick_list.name + + return delivery_note + +@frappe.whitelist() +def create_stock_entry(pick_list): + pick_list = frappe.get_doc(json.loads(pick_list)) + + if stock_entry_exists(pick_list.get('name')): + return frappe.msgprint(_('Stock Entry has been already created against this Pick List')) + + stock_entry = frappe.new_doc('Stock Entry') + stock_entry.pick_list = pick_list.get('name') + stock_entry.purpose = pick_list.get('purpose') + stock_entry.set_stock_entry_type() + + if pick_list.get('work_order'): + stock_entry = update_stock_entry_based_on_work_order(pick_list, stock_entry) + elif pick_list.get('material_request'): + stock_entry = update_stock_entry_based_on_material_request(pick_list, stock_entry) + else: + stock_entry = update_stock_entry_items_with_no_reference(pick_list, stock_entry) + + stock_entry.set_incoming_rate() + stock_entry.set_actual_qty() + stock_entry.calculate_rate_and_amount(update_finished_item_rate=False) + + return stock_entry.as_dict() + +@frappe.whitelist() +def get_pending_work_orders(doctype, txt, searchfield, start, page_length, filters, as_dict): + return frappe.db.sql(""" + SELECT + `name`, `company`, `planned_start_date` + FROM + `tabWork Order` + WHERE + `status` not in ('Completed', 'Stopped') + AND `qty` > `material_transferred_for_manufacturing` + AND `docstatus` = 1 + AND `company` = %(company)s + AND `name` like %(txt)s + ORDER BY + if(locate(%(_txt)s, name), locate(%(_txt)s, name), 99999), name + LIMIT + %(start)s, %(page_length)s""", + { + 'txt': "%%%s%%" % txt, + '_txt': txt.replace('%', ''), + 'start': start, + 'page_length': frappe.utils.cint(page_length), + 'company': filters.get('company') + }, as_dict=as_dict) + +@frappe.whitelist() +def target_document_exists(pick_list_name, purpose): + if purpose == 'Delivery against Sales Order': + return frappe.db.exists('Delivery Note', { + 'pick_list': pick_list_name + }) + + return stock_entry_exists(pick_list_name) + +@frappe.whitelist() +def get_item_details(item_code, uom=None): + details = frappe.db.get_value('Item', item_code, ['stock_uom', 'name'], as_dict=1) + details.uom = uom or details.stock_uom + if uom: + details.update(get_conversion_factor(item_code, uom)) + + return details + + +def update_delivery_note_item(source, target, delivery_note): + cost_center = frappe.db.get_value('Project', delivery_note.project, 'cost_center') + if not cost_center: + cost_center = get_cost_center(source.item_code, 'Item', delivery_note.company) + + if not cost_center: + cost_center = get_cost_center(source.item_group, 'Item Group', delivery_note.company) + + target.cost_center = cost_center + +def get_cost_center(for_item, from_doctype, company): + '''Returns Cost Center for Item or Item Group''' + return frappe.db.get_value('Item Default', + fieldname=['buying_cost_center'], + filters={ + 'parent': for_item, + 'parenttype': from_doctype, + 'company': company + }) + +def set_delivery_note_missing_values(target): + target.run_method('set_missing_values') + target.run_method('set_po_nos') + target.run_method('calculate_taxes_and_totals') + +def stock_entry_exists(pick_list_name): + return frappe.db.exists('Stock Entry', { + 'pick_list': pick_list_name + }) + +def update_stock_entry_based_on_work_order(pick_list, stock_entry): + work_order = frappe.get_doc("Work Order", pick_list.get('work_order')) + + stock_entry.work_order = work_order.name + stock_entry.company = work_order.company + stock_entry.from_bom = 1 + stock_entry.bom_no = work_order.bom_no + stock_entry.use_multi_level_bom = work_order.use_multi_level_bom + stock_entry.fg_completed_qty = pick_list.for_qty + if work_order.bom_no: + stock_entry.inspection_required = frappe.db.get_value('BOM', + work_order.bom_no, 'inspection_required') + + is_wip_warehouse_group = frappe.db.get_value('Warehouse', work_order.wip_warehouse, 'is_group') + if not (is_wip_warehouse_group and work_order.skip_transfer): + wip_warehouse = work_order.wip_warehouse + else: + wip_warehouse = None + stock_entry.to_warehouse = wip_warehouse + + stock_entry.project = work_order.project + + for location in pick_list.locations: + item = frappe._dict() + update_common_item_properties(item, location) + item.t_warehouse = wip_warehouse + + stock_entry.append('items', item) + + return stock_entry + +def update_stock_entry_based_on_material_request(pick_list, stock_entry): + for location in pick_list.locations: + target_warehouse = None + if location.material_request_item: + target_warehouse = frappe.get_value('Material Request Item', + location.material_request_item, 'warehouse') + item = frappe._dict() + update_common_item_properties(item, location) + item.t_warehouse = target_warehouse + stock_entry.append('items', item) + + return stock_entry + +def update_stock_entry_items_with_no_reference(pick_list, stock_entry): + for location in pick_list.locations: + item = frappe._dict() + update_common_item_properties(item, location) + + stock_entry.append('items', item) + + return stock_entry + +def update_common_item_properties(item, location): + item.item_code = location.item_code + item.s_warehouse = location.warehouse + item.qty = location.picked_qty * location.conversion_factor + item.transfer_qty = location.picked_qty + item.uom = location.uom + item.conversion_factor = location.conversion_factor + item.stock_uom = location.stock_uom + item.material_request = location.material_request + item.serial_no = location.serial_no + item.batch_no = location.batch_no + item.material_request_item = location.material_request_item \ No newline at end of file diff --git a/erpnext/stock/doctype/pick_list/pick_list_dashboard.py b/erpnext/stock/doctype/pick_list/pick_list_dashboard.py new file mode 100644 index 0000000000..6e007df5e6 --- /dev/null +++ b/erpnext/stock/doctype/pick_list/pick_list_dashboard.py @@ -0,0 +1,12 @@ +from __future__ import unicode_literals +from frappe import _ + +def get_data(): + return { + 'fieldname': 'pick_list', + 'transactions': [ + { + 'items': ['Stock Entry', 'Delivery Note'] + }, + ] + } \ No newline at end of file diff --git a/erpnext/stock/doctype/pick_list/test_pick_list.py b/erpnext/stock/doctype/pick_list/test_pick_list.py new file mode 100644 index 0000000000..6b4f73b140 --- /dev/null +++ b/erpnext/stock/doctype/pick_list/test_pick_list.py @@ -0,0 +1,220 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt +from __future__ import unicode_literals + +import frappe +import unittest +test_dependencies = ['Item', 'Sales Invoice', 'Stock Entry', 'Batch'] + +from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation \ + import EmptyStockReconciliationItemsError + +class TestPickList(unittest.TestCase): + + def test_pick_list_picks_warehouse_for_each_item(self): + try: + frappe.get_doc({ + 'doctype': 'Stock Reconciliation', + 'company': '_Test Company', + 'purpose': 'Opening Stock', + 'expense_account': 'Temporary Opening - _TC', + 'items': [{ + 'item_code': '_Test Item Home Desktop 100', + 'warehouse': '_Test Warehouse - _TC', + 'valuation_rate': 100, + 'qty': 5 + }] + }).submit() + except EmptyStockReconciliationItemsError: + pass + + pick_list = frappe.get_doc({ + 'doctype': 'Pick List', + 'company': '_Test Company', + 'customer': '_Test Customer', + 'items_based_on': 'Sales Order', + 'locations': [{ + 'item_code': '_Test Item Home Desktop 100', + 'qty': 5, + 'stock_qty': 5, + 'conversion_factor': 1, + 'sales_order': '_T-Sales Order-1', + 'sales_order_item': '_T-Sales Order-1_item', + }] + }) + pick_list.set_item_locations() + + self.assertEqual(pick_list.locations[0].item_code, '_Test Item Home Desktop 100') + self.assertEqual(pick_list.locations[0].warehouse, '_Test Warehouse - _TC') + self.assertEqual(pick_list.locations[0].qty, 5) + + def test_pick_list_splits_row_according_to_warhouse_availability(self): + try: + frappe.get_doc({ + 'doctype': 'Stock Reconciliation', + 'company': '_Test Company', + 'purpose': 'Opening Stock', + 'expense_account': 'Temporary Opening - _TC', + 'items': [{ + 'item_code': '_Test Item Warehouse Group Wise Reorder', + 'warehouse': '_Test Warehouse Group-C1 - _TC', + 'valuation_rate': 100, + 'qty': 5 + }] + }).submit() + except EmptyStockReconciliationItemsError: + pass + + try: + frappe.get_doc({ + 'doctype': 'Stock Reconciliation', + 'company': '_Test Company', + 'purpose': 'Opening Stock', + 'expense_account': 'Temporary Opening - _TC', + 'items': [{ + 'item_code': '_Test Item Warehouse Group Wise Reorder', + 'warehouse': '_Test Warehouse 2 - _TC', + 'valuation_rate': 400, + 'qty': 10 + }] + }).submit() + except EmptyStockReconciliationItemsError: + pass + + pick_list = frappe.get_doc({ + 'doctype': 'Pick List', + 'company': '_Test Company', + 'customer': '_Test Customer', + 'items_based_on': 'Sales Order', + 'locations': [{ + 'item_code': '_Test Item Warehouse Group Wise Reorder', + 'qty': 1000, + 'stock_qty': 1000, + 'conversion_factor': 1, + 'sales_order': '_T-Sales Order-1', + 'sales_order_item': '_T-Sales Order-1_item', + }] + }) + + pick_list.set_item_locations() + + self.assertEqual(pick_list.locations[0].item_code, '_Test Item Warehouse Group Wise Reorder') + self.assertEqual(pick_list.locations[0].warehouse, '_Test Warehouse Group-C1 - _TC') + self.assertEqual(pick_list.locations[0].qty, 5) + + self.assertEqual(pick_list.locations[1].item_code, '_Test Item Warehouse Group Wise Reorder') + self.assertEqual(pick_list.locations[1].warehouse, '_Test Warehouse 2 - _TC') + self.assertEqual(pick_list.locations[1].qty, 10) + + def test_pick_list_shows_serial_no_for_serialized_item(self): + + stock_reconciliation = frappe.get_doc({ + 'doctype': 'Stock Reconciliation', + 'company': '_Test Company', + 'items': [{ + 'item_code': '_Test Serialized Item', + 'warehouse': '_Test Warehouse - _TC', + 'valuation_rate': 100, + 'qty': 5, + 'serial_no': '123450\n123451\n123452\n123453\n123454' + }] + }) + + stock_reconciliation.submit() + + pick_list = frappe.get_doc({ + 'doctype': 'Pick List', + 'company': '_Test Company', + 'customer': '_Test Customer', + 'items_based_on': 'Sales Order', + 'locations': [{ + 'item_code': '_Test Serialized Item', + 'qty': 1000, + 'stock_qty': 1000, + 'conversion_factor': 1, + 'sales_order': '_T-Sales Order-1', + 'sales_order_item': '_T-Sales Order-1_item', + }] + }) + + pick_list.set_item_locations() + self.assertEqual(pick_list.locations[0].item_code, '_Test Serialized Item') + self.assertEqual(pick_list.locations[0].warehouse, '_Test Warehouse - _TC') + self.assertEqual(pick_list.locations[0].qty, 5) + self.assertEqual(pick_list.locations[0].serial_no, '123450\n123451\n123452\n123453\n123454') + + def test_pick_list_for_items_from_multiple_sales_orders(self): + try: + frappe.get_doc({ + 'doctype': 'Stock Reconciliation', + 'company': '_Test Company', + 'purpose': 'Opening Stock', + 'expense_account': 'Temporary Opening - _TC', + 'items': [{ + 'item_code': '_Test Item Home Desktop 100', + 'warehouse': '_Test Warehouse - _TC', + 'valuation_rate': 100, + 'qty': 10 + }] + }).submit() + except EmptyStockReconciliationItemsError: + pass + + sales_order = frappe.get_doc({ + 'doctype': "Sales Order", + 'customer': '_Test Customer', + 'company': '_Test Company', + 'items': [{ + 'item_code': '_Test Item Home Desktop 100', + 'qty': 10, + 'delivery_date': frappe.utils.today() + }], + }) + sales_order.submit() + + pick_list = frappe.get_doc({ + 'doctype': 'Pick List', + 'company': '_Test Company', + 'customer': '_Test Customer', + 'items_based_on': 'Sales Order', + 'locations': [{ + 'item_code': '_Test Item Home Desktop 100', + 'qty': 5, + 'stock_qty': 5, + 'conversion_factor': 1, + 'sales_order': '_T-Sales Order-1', + 'sales_order_item': '_T-Sales Order-1_item', + }, { + 'item_code': '_Test Item Home Desktop 100', + 'qty': 5, + 'stock_qty': 5, + 'conversion_factor': 1, + 'sales_order': sales_order.name, + 'sales_order_item': sales_order.items[0].name, + }] + }) + pick_list.set_item_locations() + + self.assertEqual(pick_list.locations[0].item_code, '_Test Item Home Desktop 100') + self.assertEqual(pick_list.locations[0].warehouse, '_Test Warehouse - _TC') + self.assertEqual(pick_list.locations[0].qty, 5) + self.assertEqual(pick_list.locations[0].sales_order_item, '_T-Sales Order-1_item') + + self.assertEqual(pick_list.locations[1].item_code, '_Test Item Home Desktop 100') + self.assertEqual(pick_list.locations[1].warehouse, '_Test Warehouse - _TC') + self.assertEqual(pick_list.locations[1].qty, 5) + self.assertEqual(pick_list.locations[1].sales_order_item, sales_order.items[0].name) + + + # def test_pick_list_skips_items_in_expired_batch(self): + # pass + + # def test_pick_list_from_sales_order(self): + # pass + + # def test_pick_list_from_work_order(self): + # pass + + # def test_pick_list_from_material_request(self): + # pass \ No newline at end of file diff --git a/erpnext/stock/doctype/pick_list_item/__init__.py b/erpnext/stock/doctype/pick_list_item/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/stock/doctype/pick_list_item/pick_list_item.json b/erpnext/stock/doctype/pick_list_item/pick_list_item.json new file mode 100644 index 0000000000..c7a35df51f --- /dev/null +++ b/erpnext/stock/doctype/pick_list_item/pick_list_item.json @@ -0,0 +1,182 @@ +{ + "creation": "2019-07-11 16:01:22.832885", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "item_code", + "item_name", + "column_break_2", + "description", + "section_break_5", + "warehouse", + "quantity_section", + "qty", + "stock_qty", + "picked_qty", + "column_break_11", + "uom", + "conversion_factor", + "stock_uom", + "serial_no_and_batch_section", + "serial_no", + "column_break_20", + "batch_no", + "column_break_15", + "sales_order", + "sales_order_item", + "material_request", + "material_request_item" + ], + "fields": [ + { + "default": "1", + "fieldname": "qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Qty" + }, + { + "fieldname": "picked_qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Picked Qty" + }, + { + "fieldname": "warehouse", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Warehouse", + "options": "Warehouse", + "read_only": 1 + }, + { + "fetch_from": "item_code.item_name", + "fieldname": "item_name", + "fieldtype": "Data", + "label": "Item Name", + "read_only": 1 + }, + { + "fetch_from": "item_code.description", + "fieldname": "description", + "fieldtype": "Text", + "label": "Description", + "read_only": 1 + }, + { + "depends_on": "serial_no", + "fieldname": "serial_no", + "fieldtype": "Small Text", + "label": "Serial No" + }, + { + "depends_on": "batch_no", + "fieldname": "batch_no", + "fieldtype": "Link", + "label": "Batch No", + "options": "Batch" + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_5", + "fieldtype": "Section Break" + }, + { + "fieldname": "stock_uom", + "fieldtype": "Link", + "label": "Stock UOM", + "options": "UOM", + "read_only": 1 + }, + { + "fieldname": "column_break_11", + "fieldtype": "Column Break" + }, + { + "fieldname": "uom", + "fieldtype": "Link", + "label": "UOM", + "options": "UOM" + }, + { + "fieldname": "conversion_factor", + "fieldtype": "Float", + "label": "UOM Conversion Factor", + "read_only": 1 + }, + { + "fieldname": "stock_qty", + "fieldtype": "Float", + "in_list_view": 1, + "label": "Stock Qty", + "read_only": 1 + }, + { + "fieldname": "item_code", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Item", + "options": "Item" + }, + { + "fieldname": "quantity_section", + "fieldtype": "Section Break", + "label": "Quantity" + }, + { + "fieldname": "column_break_15", + "fieldtype": "Section Break", + "label": "Reference" + }, + { + "fieldname": "sales_order", + "fieldtype": "Link", + "label": "Sales Order", + "options": "Sales Order", + "read_only": 1 + }, + { + "fieldname": "sales_order_item", + "fieldtype": "Data", + "label": "Sales Order Item", + "read_only": 1 + }, + { + "fieldname": "serial_no_and_batch_section", + "fieldtype": "Section Break", + "label": "Serial No and Batch" + }, + { + "fieldname": "column_break_20", + "fieldtype": "Column Break" + }, + { + "fieldname": "material_request", + "fieldtype": "Link", + "label": "Material Request", + "options": "Material Request", + "read_only": 1 + }, + { + "fieldname": "material_request_item", + "fieldtype": "Data", + "label": "Material Request Item", + "read_only": 1 + } + ], + "istable": 1, + "modified": "2019-08-29 21:28:39.539007", + "modified_by": "Administrator", + "module": "Stock", + "name": "Pick List Item", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/stock/doctype/pick_list_item/pick_list_item.py b/erpnext/stock/doctype/pick_list_item/pick_list_item.py new file mode 100644 index 0000000000..8797b8dc21 --- /dev/null +++ b/erpnext/stock/doctype/pick_list_item/pick_list_item.py @@ -0,0 +1,10 @@ +# -*- coding: utf-8 -*- +# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +from __future__ import unicode_literals +# import frappe +from frappe.model.document import Document + +class PickListItem(Document): + pass diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index d124ae4747..ab9311b480 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -329,6 +329,11 @@ class TestPurchaseReceipt(unittest.TestCase): location = frappe.db.get_value('Serial No', serial_nos[0].name, 'location') self.assertEquals(location, "Test Location") + frappe.db.set_value("Asset", asset, "purchase_receipt", "") + frappe.db.set_value("Purchase Receipt Item", pr.items[0].name, "asset", "") + + pr.load_from_db() + pr.cancel() serial_nos = frappe.get_all('Serial No', {'asset': asset}, 'name') or [] self.assertEquals(len(serial_nos), 0) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json index ee563cb4a0..f9e6d29104 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.json +++ b/erpnext/stock/doctype/stock_entry/stock_entry.json @@ -17,6 +17,7 @@ "purchase_order", "delivery_note_no", "sales_invoice_no", + "pick_list", "purchase_receipt_no", "col2", "posting_date", @@ -613,12 +614,19 @@ { "fieldname": "dimension_col_break", "fieldtype": "Column Break" + }, + { + "fieldname": "pick_list", + "fieldtype": "Link", + "label": "Pick List", + "options": "Pick List", + "read_only": 1 } ], "icon": "fa fa-file-text", "idx": 1, "is_submittable": 1, - "modified": "2019-07-14 17:41:39.257508", + "modified": "2019-08-22 17:11:42.074154", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry", diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index 6e81dd0ae2..e5a2102e44 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -525,15 +525,21 @@ class StockEntry(StockController): backflush_raw_materials_based_on = frappe.db.get_single_value("Buying Settings", "backflush_raw_materials_of_subcontract_based_on") + qty_allowance = flt(frappe.db.get_single_value("Buying Settings", + "over_transfer_allowance")) + if (self.purpose == "Send to Subcontractor" and self.purchase_order and backflush_raw_materials_based_on == 'BOM'): purchase_order = frappe.get_doc("Purchase Order", self.purchase_order) for se_item in self.items: item_code = se_item.original_item or se_item.item_code precision = cint(frappe.db.get_default("float_precision")) or 3 - total_allowed = sum([flt(d.required_qty) for d in purchase_order.supplied_items \ + required_qty = sum([flt(d.required_qty) for d in purchase_order.supplied_items \ if d.rm_item_code == item_code]) - if not total_allowed: + + total_allowed = required_qty + (required_qty * (qty_allowance/100)) + + if not required_qty: frappe.throw(_("Item {0} not found in 'Raw Materials Supplied' table in Purchase Order {1}") .format(se_item.item_code, self.purchase_order)) total_supplied = frappe.db.sql("""select sum(transfer_qty) @@ -1110,6 +1116,7 @@ class StockEntry(StockController): se_child.allow_alternative_item = item_dict[d].get("allow_alternative_item", 0) se_child.subcontracted_item = item_dict[d].get("main_item_code") se_child.original_item = item_dict[d].get("original_item") + se_child.po_detail = item_dict[d].get("po_detail") if item_dict[d].get("idx"): se_child.idx = item_dict[d].get("idx") @@ -1161,7 +1168,14 @@ class StockEntry(StockController): where po.name = poitemsup.parent and po.name = %s""", self.purchase_order)) - #Update reserved sub contracted quantity in bin based on Supplied Item Details + #Update Supplied Qty in PO Supplied Items + + frappe.db.sql("""UPDATE `tabPurchase Order Item Supplied` pos + SET pos.supplied_qty = (SELECT ifnull(sum(transfer_qty), 0) FROM `tabStock Entry Detail` sed + WHERE pos.name = sed.po_detail and sed.docstatus = 1) + WHERE pos.docstatus = 1""") + + #Update reserved sub contracted quantity in bin based on Supplied Item Details and for d in self.get("items"): item_code = d.get('original_item') or d.get('item_code') reserve_warehouse = item_wh.get(item_code) diff --git a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json index 912a4651b3..d86e68b722 100644 --- a/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json +++ b/erpnext/stock/doctype/stock_entry_detail/stock_entry_detail.json @@ -59,6 +59,7 @@ "reference_section", "against_stock_entry", "ste_detail", + "po_detail", "column_break_51", "transferred_qty", "reference_purchase_receipt", @@ -480,11 +481,20 @@ "label": "Project", "options": "Project", "read_only": 1 + }, + { + "fieldname": "po_detail", + "fieldtype": "Data", + "hidden": 1, + "label": "PO Supplied Item", + "no_copy": 1, + "print_hide": 1, + "read_only": 1 } ], "idx": 1, "istable": 1, - "modified": "2019-07-12 11:34:53.190749", + "modified": "2019-08-20 14:01:02.319754", "modified_by": "Administrator", "module": "Stock", "name": "Stock Entry Detail", diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py index ededc4d8b4..cd05929743 100644 --- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py @@ -152,7 +152,6 @@ class TestStockReconciliation(unittest.TestCase): for d in to_delete_records: stock_doc = frappe.get_doc("Stock Reconciliation", d) stock_doc.cancel() - frappe.delete_doc("Stock Reconciliation", stock_doc.name) for d in serial_nos + serial_nos1: if frappe.db.exists("Serial No", d): @@ -203,9 +202,6 @@ class TestStockReconciliation(unittest.TestCase): stock_doc = frappe.get_doc("Stock Reconciliation", d) stock_doc.cancel() - frappe.delete_doc("Batch", sr.items[0].batch_no) - for d in to_delete_records: - frappe.delete_doc("Stock Reconciliation", d) def insert_existing_sle(): from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index f1d784c8d9..bf5a8180bd 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -22,7 +22,7 @@ sales_doctypes = ['Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice'] purchase_doctypes = ['Material Request', 'Supplier Quotation', 'Purchase Order', 'Purchase Receipt', 'Purchase Invoice'] @frappe.whitelist() -def get_item_details(args, doc=None): +def get_item_details(args, doc=None, overwrite_warehouse=True): """ args = { "item_code": "", @@ -44,11 +44,12 @@ def get_item_details(args, doc=None): "set_warehouse": "" } """ + args = process_args(args) item = frappe.get_cached_doc("Item", args.item_code) validate_item_details(args, item) - out = get_basic_details(args, item) + out = get_basic_details(args, item, overwrite_warehouse) get_item_tax_template(args, item, out) out["item_tax_rate"] = get_item_tax_map(args.company, args.get("item_tax_template") if out.get("item_tax_template") is None \ @@ -178,7 +179,7 @@ def validate_item_details(args, item): throw(_("Item {0} must be a Sub-contracted Item").format(item.name)) -def get_basic_details(args, item): +def get_basic_details(args, item, overwrite_warehouse=True): """ :param args: { "item_code": "", @@ -225,14 +226,26 @@ def get_basic_details(args, item): item_group_defaults = get_item_group_defaults(item.name, args.company) brand_defaults = get_brand_defaults(item.name, args.company) - warehouse = (args.get("set_warehouse") or item_defaults.get("default_warehouse") or - item_group_defaults.get("default_warehouse") or brand_defaults.get("default_warehouse") or args.warehouse) + if overwrite_warehouse or not args.warehouse: + warehouse = ( + args.get("set_warehouse") or + item_defaults.get("default_warehouse") or + item_group_defaults.get("default_warehouse") or + brand_defaults.get("default_warehouse") or + args.warehouse + ) - if not warehouse: - defaults = frappe.defaults.get_defaults() or {} - if defaults.get("default_warehouse") and frappe.db.exists("Warehouse", - {'name': defaults.default_warehouse, 'company': args.company}): - warehouse = defaults.default_warehouse + if not warehouse: + defaults = frappe.defaults.get_defaults() or {} + warehouse_exists = frappe.db.exists("Warehouse", { + 'name': defaults.default_warehouse, + 'company': args.company + }) + if defaults.get("default_warehouse") and warehouse_exists: + warehouse = defaults.default_warehouse + + else: + warehouse = args.warehouse if args.get('doctype') == "Material Request" and not args.get('material_request_type'): args['material_request_type'] = frappe.db.get_value('Material Request', diff --git a/erpnext/stock/print_format/__init__.py b/erpnext/stock/print_format/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/stock/print_format/pick_list/__init__.py b/erpnext/stock/print_format/pick_list/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/stock/print_format/pick_list/pick_list.json b/erpnext/stock/print_format/pick_list/pick_list.json new file mode 100644 index 0000000000..0e88d24c5a --- /dev/null +++ b/erpnext/stock/print_format/pick_list/pick_list.json @@ -0,0 +1,23 @@ +{ + "align_labels_right": 1, + "creation": "2019-08-02 07:27:42.533305", + "custom_format": 0, + "disabled": 0, + "doc_type": "Pick List", + "docstatus": 0, + "doctype": "Print Format", + "font": "Default", + "format_data": "[{\"fieldname\": \"print_heading_template\", \"fieldtype\": \"Custom HTML\", \"options\": \"
    \\t\\t\\t\\t

    Pick List
    {{ doc.name }}\\t\\t\\t\\t

    \"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"company\", \"label\": \"Company\"}, {\"print_hide\": 0, \"fieldname\": \"customer\", \"label\": \"Customer\"}, {\"fieldtype\": \"Column Break\"}, {\"print_hide\": 0, \"fieldname\": \"purpose\", \"label\": \"Purpose\"}, {\"fieldtype\": \"Section Break\", \"label\": \"\"}, {\"fieldtype\": \"Column Break\"}, {\"visible_columns\": [{\"print_hide\": 0, \"fieldname\": \"item_name\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"warehouse\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"qty\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"stock_qty\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"serial_no\", \"print_width\": \"\"}, {\"print_hide\": 0, \"fieldname\": \"batch_no\", \"print_width\": \"\"}], \"print_hide\": 0, \"fieldname\": \"locations\", \"label\": \"Item Locations\"}]", + "idx": 0, + "line_breaks": 1, + "modified": "2019-08-30 15:58:27.807219", + "modified_by": "Administrator", + "module": "Stock", + "name": "Pick List", + "owner": "Administrator", + "print_format_builder": 1, + "print_format_type": "Jinja", + "raw_printing": 0, + "show_section_headings": 1, + "standard": "Yes" +} \ No newline at end of file diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 920fc272f7..f7deac3591 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -227,9 +227,9 @@ class update_entries_after(object): elif actual_qty < 0: # In case of delivery/stock issue, get average purchase rate # of serial nos of current entry - stock_value_change = -1 * flt(frappe.db.sql("""select sum(purchase_rate) - from `tabSerial No` where name in (%s)""" % (", ".join(["%s"]*len(serial_no))), - tuple(serial_no))[0][0]) + stock_value_change = -1 * flt(frappe.get_all("Serial No", + fields=["sum(purchase_rate)"], + filters = {'name': ('in', serial_no)}, as_list=1)[0][0]) new_stock_qty = self.qty_after_transaction + actual_qty diff --git a/erpnext/templates/includes/footer/footer_powered.html b/erpnext/templates/includes/footer/footer_powered.html index faf5e9278c..8d5523ff97 100644 --- a/erpnext/templates/includes/footer/footer_powered.html +++ b/erpnext/templates/includes/footer/footer_powered.html @@ -1 +1,3 @@ -Powered by ERPNext +{% set domains = frappe.get_doc("Domain Settings").active_domains %} + +Powered by ERPNext - {{ domains[0].domain if domains else 'Open Source' }} ERP Software