From fb237739358930b4057528203be4275aa3dd730a Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 12 Aug 2019 14:56:57 +0530 Subject: [PATCH 01/23] fix: reconciled entry has not clearance date set --- erpnext/accounts/doctype/bank_transaction/bank_transaction.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py index f943b34581..716ec1f552 100644 --- a/erpnext/accounts/doctype/bank_transaction/bank_transaction.py +++ b/erpnext/accounts/doctype/bank_transaction/bank_transaction.py @@ -50,7 +50,7 @@ class BankTransaction(StatusUpdater): if paid_amount and allocated_amount: if flt(allocated_amount[0]["allocated_amount"]) > flt(paid_amount): frappe.throw(_("The total allocated amount ({0}) is greated than the paid amount ({1}).".format(flt(allocated_amount[0]["allocated_amount"]), flt(paid_amount)))) - elif flt(allocated_amount[0]["allocated_amount"]) == flt(paid_amount): + else: if payment_entry.payment_document in ["Payment Entry", "Journal Entry", "Purchase Invoice", "Expense Claim"]: self.clear_simple_entry(payment_entry) From 1008e6e45022217131503af9b826580bb0f5b18c Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 19 Aug 2019 20:21:36 +0530 Subject: [PATCH 02/23] fix: debit note not reconciled with another purchase invoice using payment reconciliation --- .../payment_reconciliation.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index b74eed5841..4665d75510 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -93,7 +93,7 @@ class PaymentReconciliation(Document): and `tab{doc}`.is_return = 1 and `tabGL Entry`.against_voucher_type = %(voucher_type)s and `tab{doc}`.docstatus = 1 and `tabGL Entry`.party = %(party)s and `tabGL Entry`.party_type = %(party_type)s and `tabGL Entry`.account = %(account)s - GROUP BY `tabSales Invoice`.name + GROUP BY `tab{doc}`.name Having amount > 0 """.format(doc=voucher_type, dr_or_cr=dr_or_cr, reconciled_dr_or_cr=reconciled_dr_or_cr), { @@ -257,11 +257,8 @@ def reconcile_dr_cr_note(dr_cr_notes): voucher_type = ('Credit Note' if d.voucher_type == 'Sales Invoice' else 'Debit Note') - dr_or_cr = ('credit_in_account_currency' - if d.reference_type == 'Sales Invoice' else 'debit_in_account_currency') - reconcile_dr_or_cr = ('debit_in_account_currency' - if dr_or_cr == 'credit_in_account_currency' else 'credit_in_account_currency') + if d.dr_or_cr == 'credit_in_account_currency' else 'credit_in_account_currency') jv = frappe.get_doc({ "doctype": "Journal Entry", @@ -272,8 +269,7 @@ def reconcile_dr_cr_note(dr_cr_notes): 'account': d.account, 'party': d.party, 'party_type': d.party_type, - reconcile_dr_or_cr: (abs(d.allocated_amount) - if abs(d.unadjusted_amount) > abs(d.allocated_amount) else abs(d.unadjusted_amount)), + d.dr_or_cr: abs(d.allocated_amount), 'reference_type': d.against_voucher_type, 'reference_name': d.against_voucher }, @@ -281,7 +277,8 @@ def reconcile_dr_cr_note(dr_cr_notes): 'account': d.account, 'party': d.party, 'party_type': d.party_type, - dr_or_cr: abs(d.allocated_amount), + reconcile_dr_or_cr: (abs(d.allocated_amount) + if abs(d.unadjusted_amount) > abs(d.allocated_amount) else abs(d.unadjusted_amount)), 'reference_type': d.voucher_type, 'reference_name': d.voucher_no } From 6affeaa9c1a80b1ad5024435bcd814b38412873d Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Wed, 21 Aug 2019 09:14:56 +0530 Subject: [PATCH 03/23] fix: check if number exists after striping '0' - Absract number striping logic to separate method - Rename a confusing variable name - Remove leftout print statement --- .../doctype/call_log/call_log.py | 31 +++++++++++-------- erpnext/crm/doctype/utils.py | 9 ++++++ 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/erpnext/communication/doctype/call_log/call_log.py b/erpnext/communication/doctype/call_log/call_log.py index c9fdfbe447..00464170f3 100644 --- a/erpnext/communication/doctype/call_log/call_log.py +++ b/erpnext/communication/doctype/call_log/call_log.py @@ -6,15 +6,13 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.model.document import Document -from erpnext.crm.doctype.utils import get_scheduled_employees_for_popup +from erpnext.crm.doctype.utils import get_scheduled_employees_for_popup, strip_number from frappe.contacts.doctype.contact.contact import get_contact_with_phone_number from erpnext.crm.doctype.lead.lead import get_lead_with_phone_number class CallLog(Document): def before_insert(self): - # strip 0 from the start of the number for proper number comparisions - # eg. 07888383332 should match with 7888383332 - number = self.get('from').lstrip('0') + number = strip_number(self.get('from')) self.contact = get_contact_with_phone_number(number) self.lead = get_lead_with_phone_number(number) @@ -48,13 +46,14 @@ def add_call_summary(call_log, summary): doc.add_comment('Comment', frappe.bold(_('Call Summary')) + '

' + summary) def get_employees_with_number(number): + number = strip_number(number) if not number: return [] employee_emails = frappe.cache().hget('employees_with_number', number) if employee_emails: return employee_emails employees = frappe.get_all('Employee', filters={ - 'cell_number': ['like', '%{}'.format(number.lstrip('0'))], + 'cell_number': ['like', '%{}'.format(number)], 'user_id': ['!=', ''] }, fields=['user_id']) @@ -64,23 +63,29 @@ def get_employees_with_number(number): return employee def set_caller_information(doc, state): - '''Called from hoooks on creation of Lead or Contact''' + '''Called from hooks on creation of Lead or Contact''' if doc.doctype not in ['Lead', 'Contact']: return numbers = [doc.get('phone'), doc.get('mobile_no')] - for_doc = doc.doctype.lower() + # contact for Contact and lead for Lead + fieldname = doc.doctype.lower() + + # contact_name or lead_name + display_name_field = '{}_name'.format(fieldname) for number in numbers: + number = strip_number(number) if not number: continue - print(number) + filters = frappe._dict({ - 'from': ['like', '%{}'.format(number.lstrip('0'))], - for_doc: '' + 'from': ['like', '%{}'.format(number)], + fieldname: '' }) logs = frappe.get_all('Call Log', filters=filters) for log in logs: - call_log = frappe.get_doc('Call Log', log.name) - call_log.set(for_doc, doc.name) - call_log.save(ignore_permissions=True) + frappe.db.set_value('Call Log', log.name, { + fieldname: doc.name, + display_name_field: doc.get_title() + }, update_modified=False) diff --git a/erpnext/crm/doctype/utils.py b/erpnext/crm/doctype/utils.py index 55532761c2..535458af21 100644 --- a/erpnext/crm/doctype/utils.py +++ b/erpnext/crm/doctype/utils.py @@ -54,6 +54,8 @@ def get_last_issue_from_customer(customer_name): def get_scheduled_employees_for_popup(communication_medium): + if not communication_medium: return [] + now_time = frappe.utils.nowtime() weekday = frappe.utils.get_weekday() @@ -73,3 +75,10 @@ def get_scheduled_employees_for_popup(communication_medium): employee_emails = set([employee.user_id for employee in employees]) return employee_emails + +def strip_number(number): + if not number: return + # strip 0 from the start of the number for proper number comparisions + # eg. 07888383332 should match with 7888383332 + number = number.lstrip('0') + return number \ No newline at end of file From e9e0c0e7d4b8eb40172bbf74e70fdef4bc02304d Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Wed, 21 Aug 2019 14:40:35 +0530 Subject: [PATCH 04/23] fix: Minor fix in GSTR-1 report (#18797) --- erpnext/regional/report/gstr_1/gstr_1.py | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/erpnext/regional/report/gstr_1/gstr_1.py b/erpnext/regional/report/gstr_1/gstr_1.py index 2da1085732..c397133f33 100644 --- a/erpnext/regional/report/gstr_1/gstr_1.py +++ b/erpnext/regional/report/gstr_1/gstr_1.py @@ -156,33 +156,21 @@ class Gstr1Report(object): if self.filters.get("type_of_business") == "B2B": - customers = frappe.get_all("Customer", - filters={ - "gst_category": ["in", ["Registered Regular", "Deemed Export", "SEZ"]] - }) - - if customers: - conditions += """ and ifnull(gst_category, '') != 'Overseas' and is_return != 1 - and customer in ({0})""".format(", ".join([frappe.db.escape(c.name) for c in customers])) + conditions += "and ifnull(gst_category, '') in ('Registered Regular', 'Deemed Export', 'SEZ') and is_return != 1" if self.filters.get("type_of_business") in ("B2C Large", "B2C Small"): b2c_limit = frappe.db.get_single_value('GST Settings', 'b2c_limit') if not b2c_limit: frappe.throw(_("Please set B2C Limit in GST Settings.")) - customers = frappe.get_all("Customer", - filters={ - "gst_category": ["in", ["Unregistered"]] - }) - if self.filters.get("type_of_business") == "B2C Large" and customers: conditions += """ and SUBSTR(place_of_supply, 1, 2) != SUBSTR(company_gstin, 1, 2) - and grand_total > {0} and is_return != 1 and customer in ({1})""".\ + and grand_total > {0} and is_return != 1 and gst_category ='Unregistered' """.\ format(flt(b2c_limit), ", ".join([frappe.db.escape(c.name) for c in customers])) elif self.filters.get("type_of_business") == "B2C Small" and customers: conditions += """ and ( SUBSTR(place_of_supply, 1, 2) = SUBSTR(company_gstin, 1, 2) - or grand_total <= {0}) and is_return != 1 and customer in ({1})""".\ + or grand_total <= {0}) and is_return != 1 and gst_category ='Unregistered' """.\ format(flt(b2c_limit), ", ".join([frappe.db.escape(c.name) for c in customers])) elif self.filters.get("type_of_business") == "CDNR": From bcb0f6038e0742d73888720c2fafc4609ac3aa6a Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Wed, 21 Aug 2019 14:47:33 +0530 Subject: [PATCH 05/23] fix: Single gl entry should only be considered once either in opening or closing entry (#18792) --- .../report/accounts_receivable/accounts_receivable.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 97e710450c..0e4ee12548 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -197,8 +197,10 @@ class ReceivablePayableReport(object): if self.filters.based_on_payment_terms and gl_entries_data: self.payment_term_map = self.get_payment_term_detail(voucher_nos) + 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 @@ -409,7 +411,9 @@ class ReceivablePayableReport(object): 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 From 5619db28cba2b1e5dcf31e23ecfa193f09d5f732 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Wed, 21 Aug 2019 14:49:24 +0530 Subject: [PATCH 06/23] fix: fetch capital work in progress as expense account (#18780) --- erpnext/controllers/queries.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 57c063a72a..19ec053e74 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -371,7 +371,7 @@ def get_expense_account(doctype, txt, searchfield, start, page_len, filters): return frappe.db.sql("""select tabAccount.name from `tabAccount` where (tabAccount.report_type = "Profit and Loss" - or tabAccount.account_type in ("Expense Account", "Fixed Asset", "Temporary", "Asset Received But Not Billed")) + or tabAccount.account_type in ("Expense Account", "Fixed Asset", "Temporary", "Asset Received But Not Billed", "Capital Work in Progress")) and tabAccount.is_group=0 and tabAccount.docstatus!=2 and tabAccount.{key} LIKE %(txt)s From 109a07b8344df9c2420c2cea7f9bd6419284c920 Mon Sep 17 00:00:00 2001 From: Suraj Shetty <13928957+surajshetty3416@users.noreply.github.com> Date: Wed, 21 Aug 2019 15:19:08 +0530 Subject: [PATCH 07/23] fix: Fix get_employees_with_number query if the passed number is 7039392929 then it should match with the employee cell_number set as `7039392929, 0288382222` --- erpnext/communication/doctype/call_log/call_log.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/communication/doctype/call_log/call_log.py b/erpnext/communication/doctype/call_log/call_log.py index 00464170f3..2343632719 100644 --- a/erpnext/communication/doctype/call_log/call_log.py +++ b/erpnext/communication/doctype/call_log/call_log.py @@ -53,7 +53,7 @@ def get_employees_with_number(number): if employee_emails: return employee_emails employees = frappe.get_all('Employee', filters={ - 'cell_number': ['like', '%{}'.format(number)], + 'cell_number': ['like', '%{}%'.format(number)], 'user_id': ['!=', ''] }, fields=['user_id']) From a6c6e02c492568b65fe910a1f15966ae29d1e3cf Mon Sep 17 00:00:00 2001 From: Anastes Mp Date: Wed, 21 Aug 2019 16:53:06 +0530 Subject: [PATCH 08/23] Incorrect database table (#18558) Fixed Unknow Column tax_type error on offline pos --- erpnext/accounts/doctype/sales_invoice/pos.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/pos.py b/erpnext/accounts/doctype/sales_invoice/pos.py index e2f99d6ea3..479548c6e3 100755 --- a/erpnext/accounts/doctype/sales_invoice/pos.py +++ b/erpnext/accounts/doctype/sales_invoice/pos.py @@ -307,7 +307,7 @@ def get_item_tax_data(): # example: {'Consulting Services': {'Excise 12 - TS': '12.000'}} itemwise_tax = {} - taxes = frappe.db.sql(""" select parent, tax_type, tax_rate from `tabItem Tax`""", as_dict=1) + taxes = frappe.db.sql(""" select parent, tax_type, tax_rate from `tabItem Tax Template Detail`""", as_dict=1) for tax in taxes: if tax.parent not in itemwise_tax: From cd25d6dc7fb456602a9613db7c8b4b7f09a7df0d Mon Sep 17 00:00:00 2001 From: Sahil Khan Date: Thu, 22 Aug 2019 13:30:17 +0550 Subject: [PATCH 09/23] bumped to version 12.0.8 --- erpnext/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/__init__.py b/erpnext/__init__.py index 8c47111e01..2490f3b42e 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.7' +__version__ = '12.0.8' def get_default_company(user=None): '''Get default company for user''' From 210317c0b5e8cb50d7226fc9a796adfe9ec3be5e Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Fri, 23 Aug 2019 00:21:26 +0530 Subject: [PATCH 10/23] fix: division by zero in asset Depereciation (#18637) * fix: division by zero in asset Depereciation * fix: requested changes --- erpnext/assets/doctype/asset/asset.py | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) 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)) From 88ee7d8dbeb62ff080247089219e2783d26c41d3 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Fri, 23 Aug 2019 11:13:58 +0530 Subject: [PATCH 11/23] fix: POS Sync Issue (#18807) --- erpnext/accounts/doctype/sales_invoice/pos.py | 15 ++++----------- erpnext/accounts/page/pos/pos.js | 11 ++--------- 2 files changed, 6 insertions(+), 20 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/pos.py b/erpnext/accounts/doctype/sales_invoice/pos.py index 479548c6e3..e290b235a9 100755 --- a/erpnext/accounts/doctype/sales_invoice/pos.py +++ b/erpnext/accounts/doctype/sales_invoice/pos.py @@ -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/page/pos/pos.js b/erpnext/accounts/page/pos/pos.js index 3834c96bfb..b5a02d0e49 100755 --- a/erpnext/accounts/page/pos/pos.js +++ b/erpnext/accounts/page/pos/pos.js @@ -1762,18 +1762,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, From db88476f8b48965565bb0c45dd9a040e3d76bac2 Mon Sep 17 00:00:00 2001 From: Anurag Mishra <32095923+Anurag810@users.noreply.github.com> Date: Fri, 23 Aug 2019 11:27:00 +0530 Subject: [PATCH 12/23] feat: refactor invoice_discounting (#18629) * feat: refactor invoice_discountig * Update invoice_discounting.py * Update invoice_discounting.js --- .../discounted_invoice.json | 13 ++++- .../invoice_discounting.js | 53 +++++++++++++------ .../invoice_discounting.py | 13 ++++- 3 files changed, 59 insertions(+), 20 deletions(-) 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 From 680e098b872f51c9c2b1e899b5548c492b8d7873 Mon Sep 17 00:00:00 2001 From: Michelle Alva <50285544+michellealva@users.noreply.github.com> Date: Fri, 23 Aug 2019 11:32:43 +0530 Subject: [PATCH 13/23] fix(dash): Added Tax exemption category and sub category in Employee Tax and Benefits (#18545) section From 56d03a456b44890602ddd00130107893f6f223e1 Mon Sep 17 00:00:00 2001 From: Harshit <46772424+harshit-30@users.noreply.github.com> Date: Fri, 23 Aug 2019 11:34:25 +0530 Subject: [PATCH 14/23] fix: add contract to CRM module (#18817) --- erpnext/config/crm.py | 5 +++++ 1 file changed, 5 insertions(+) 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"), + }, ] }, { From 23b5f4ec99d0f22348ba3b8f1384c9f14f9660ee Mon Sep 17 00:00:00 2001 From: KanchanChauhan Date: Fri, 23 Aug 2019 11:40:09 +0530 Subject: [PATCH 15/23] fix: Leave Application status filter (#18770) When we click on Status Open in list view it throws an error. After fix, it filters the results on Open status --- erpnext/hr/doctype/leave_application/leave_application_list.js | 2 ++ 1 file changed, 2 insertions(+) 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"]; } } }; From f3d78dd29c4c98d303fbcfe7f7e1db447c466a82 Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Fri, 23 Aug 2019 11:42:35 +0530 Subject: [PATCH 16/23] fix: Duplicate items check in Sales Invoice (#18660) * fix: Duplicate items check in sales Invoice * fix: Commonified function for duplicate items check in selling controller * fix: Set valuation rate and transaction date in child tabel --- erpnext/controllers/selling_controller.py | 29 +++++++++++++++++++ .../doctype/sales_order/sales_order.py | 9 ------ .../doctype/delivery_note/delivery_note.py | 17 ----------- 3 files changed, 29 insertions(+), 26 deletions(-) diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 2cbe596770..52d58dce62 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 == "Sales Order": + e = [d.item_code, d.description, d.warehouse, d.batch_no or ''] + 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/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 09dc9a9932..4342af5e19 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 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 From c890401fec89eb97a0656a1e18a297b78fe805de Mon Sep 17 00:00:00 2001 From: Michelle Alva <50285544+michellealva@users.noreply.github.com> Date: Fri, 23 Aug 2019 11:50:48 +0530 Subject: [PATCH 17/23] fix: added payroll period in HR module (#18543) From 6e7b9b5cb794705c15e8aa1575e7be206ef1470b Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 23 Aug 2019 12:27:05 +0530 Subject: [PATCH 18/23] Update sales_invoice.py --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) 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 From a7ab51c5ba95ae387302fca2d7d222344eccbf5c Mon Sep 17 00:00:00 2001 From: Tufan Kaynak <31142607+toofun666@users.noreply.github.com> Date: Fri, 23 Aug 2019 10:20:52 +0300 Subject: [PATCH 19/23] Update vehicle_expenses.py (#18768) --- erpnext/hr/report/vehicle_expenses/vehicle_expenses.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) 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 From d94a389dd3d66ad74d47300fa572a0179684056a Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Fri, 23 Aug 2019 16:31:09 +0530 Subject: [PATCH 20/23] chore: remove unwanted code (#18645) --- .../doctype/payment_order/regional/india.js | 29 --- erpnext/patches.txt | 4 +- .../make_custom_fields_for_bank_remittance.py | 12 -- .../remove_bank_remittance_custom_fields.py | 14 ++ erpnext/regional/india/bank_remittance.py | 190 ------------------ erpnext/regional/india/setup.py | 8 - 6 files changed, 16 insertions(+), 241 deletions(-) delete mode 100644 erpnext/accounts/doctype/payment_order/regional/india.js delete mode 100644 erpnext/patches/v12_0/make_custom_fields_for_bank_remittance.py create mode 100644 erpnext/patches/v12_0/remove_bank_remittance_custom_fields.py delete mode 100644 erpnext/regional/india/bank_remittance.py 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/patches.txt b/erpnext/patches.txt index 7b8280975d..dc6c6daa13 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,5 @@ 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 +erpnext.patches.v12_0.remove_bank_remittance_custom_fields +erpnext.patches.v12_0.generate_leave_ledger_entries 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/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', From fe579c2efdfe13fae20ef33986d535646dc6c579 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Fri, 23 Aug 2019 16:31:20 +0530 Subject: [PATCH 21/23] fix: show a descriptive message on submission of duplicate account (#18486) * fix: show a descriptive message on submission of duplicate account * Update account.py --- erpnext/accounts/doctype/account/account.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) 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:
  • {2}
") + .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")) From d8c262fd5c9611c65a33bafac57c2084f3dc8bbb Mon Sep 17 00:00:00 2001 From: Nabin Hait Date: Fri, 23 Aug 2019 16:32:58 +0530 Subject: [PATCH 22/23] fix: Translations of strings (#18825) --- erpnext/accounts/report/balance_sheet/balance_sheet.py | 6 +++--- .../payment_period_based_on_invoice_date.js | 4 ++-- .../payment_period_based_on_invoice_date.py | 10 +++++----- .../profit_and_loss_statement.py | 6 +++--- erpnext/selling/page/point_of_sale/point_of_sale.js | 2 +- 5 files changed, 14 insertions(+), 14 deletions(-) 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/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 }); From 5dd1b508026c69c0109ec5f09f4e62c7dcfde725 Mon Sep 17 00:00:00 2001 From: Mangesh-Khairnar Date: Mon, 26 Aug 2019 10:27:25 +0530 Subject: [PATCH 23/23] fix: update show in website on disabling item (#18831) --- erpnext/stock/doctype/item/item.py | 5 +++++ 1 file changed, 5 insertions(+) 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)