From a57d13e1e9538e56b401d651c52d2b9c5b84250f Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 29 Jul 2021 19:46:17 +0530 Subject: [PATCH 01/27] fix: Multiple fixes in payment entry --- .../doctype/payment_entry/payment_entry.js | 4 ++-- .../doctype/payment_entry/payment_entry.py | 15 +++++++++++++-- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 439b1edbce..d96bc271ef 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -533,8 +533,8 @@ frappe.ui.form.on('Payment Entry', { source_exchange_rate: function(frm) { if (frm.doc.paid_amount) { frm.set_value("base_paid_amount", flt(frm.doc.paid_amount) * flt(frm.doc.source_exchange_rate)); - if(!frm.set_paid_amount_based_on_received_amount && - (frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency)) { + // target exchange rate should always be same as source if both account currencies is same + if(frm.doc.paid_from_account_currency == frm.doc.paid_to_account_currency) { frm.set_value("target_exchange_rate", frm.doc.source_exchange_rate); frm.set_value("base_received_amount", frm.doc.base_paid_amount); } diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 46904f7c57..a131a810e1 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -55,8 +55,9 @@ class PaymentEntry(AccountsController): self.validate_mandatory() self.validate_reference_documents() self.set_tax_withholding() - self.apply_taxes() self.set_amounts() + self.validate_amounts() + self.apply_taxes() self.clear_unallocated_reference_document_rows() self.validate_payment_against_negative_invoice() self.validate_transaction_reference() @@ -236,7 +237,9 @@ class PaymentEntry(AccountsController): self.company_currency, self.posting_date) def set_target_exchange_rate(self, ref_doc=None): - if self.paid_to and not self.target_exchange_rate: + if self.paid_from_account_currency == self.paid_to_account_currency: + self.target_exchange_rate = self.source_exchange_rate + elif self.paid_to and not self.target_exchange_rate: if ref_doc: if self.paid_to_account_currency == ref_doc.currency: self.target_exchange_rate = ref_doc.get("exchange_rate") @@ -473,6 +476,14 @@ class PaymentEntry(AccountsController): self.set_unallocated_amount() self.set_difference_amount() + def validate_amounts(self): + self.validate_received_amount() + + def validate_received_amount(self): + if self.paid_from_account_currency == self.paid_to_account_currency: + if self.paid_amount != self.received_amount: + frappe.throw(_("Received Amount cannot be greater than Paid Amount")) + def set_received_amount(self): self.base_received_amount = self.base_paid_amount From c5276f3fd36764e5e90b0b068d0d9937a88e4447 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 1 Aug 2021 17:48:50 +0530 Subject: [PATCH 02/27] fix: Multiple fixes in payment entry --- .../accounts/doctype/payment_entry/payment_entry.py | 13 ++++++++----- erpnext/accounts/utils.py | 12 ++++++++---- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index a131a810e1..2231b47d9c 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -45,7 +45,7 @@ class PaymentEntry(AccountsController): self.party_account = self.paid_to self.party_account_currency = self.paid_to_account_currency - def validate(self): + def validate(self, on_reference_unlink=False): self.setup_party_account_field() self.set_missing_values() self.validate_payment_type() @@ -64,8 +64,9 @@ class PaymentEntry(AccountsController): self.set_title() self.set_remarks() self.validate_duplicate_entry() - self.validate_allocated_amount() - self.validate_paid_invoices() + if not on_reference_unlink: + self.validate_allocated_amount() + self.validate_paid_invoices() self.ensure_supplier_is_not_blocked() self.set_status() @@ -529,8 +530,10 @@ class PaymentEntry(AccountsController): base_total_allocated_amount += flt(flt(d.allocated_amount) * flt(d.exchange_rate), self.precision("base_paid_amount")) - self.total_allocated_amount = abs(total_allocated_amount) - self.base_total_allocated_amount = abs(base_total_allocated_amount) + # Do not use absolute values as only credit notes could be allocated + # and total allocated should be negative in that scenario + self.total_allocated_amount = total_allocated_amount + self.base_total_allocated_amount = base_total_allocated_amount def set_unallocated_amount(self): self.unallocated_amount = 0 diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 9272bc4fce..11e113d000 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -553,10 +553,14 @@ def remove_ref_doc_link_from_pe(ref_type, ref_no): and docstatus < 2""", (now(), frappe.session.user, ref_type, ref_no)) for pe in linked_pe: - pe_doc = frappe.get_doc("Payment Entry", pe) - pe_doc.set_total_allocated_amount() - pe_doc.set_unallocated_amount() - pe_doc.clear_unallocated_reference_document_rows() + try: + pe_doc = frappe.get_doc("Payment Entry", pe) + pe_doc.validate(on_reference_unlink=True) + except Exception as e: + msg = _("There were issues unlinking payment entry {0}.").format(pe_doc.name) + msg += '
' + msg += _("Please cancel payment entry manually first and then resubmit") + frappe.throw(msg, title=_("Payment Unlink Error")) frappe.db.sql("""update `tabPayment Entry` set total_allocated_amount=%s, base_total_allocated_amount=%s, unallocated_amount=%s, modified=%s, modified_by=%s From 188bba8feb64519de2c1ded0d5432308c397f04a Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 10 Aug 2021 14:04:31 +0530 Subject: [PATCH 03/27] fix: Validation for receivingfrom customer against negative outstanding --- .../accounts/doctype/payment_entry/payment_entry.py | 12 ++++++++---- erpnext/accounts/utils.py | 2 +- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 2231b47d9c..66b46675dc 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -64,6 +64,7 @@ class PaymentEntry(AccountsController): self.set_title() self.set_remarks() self.validate_duplicate_entry() + self.validate_payment_type_with_outstanding() if not on_reference_unlink: self.validate_allocated_amount() self.validate_paid_invoices() @@ -120,6 +121,11 @@ class PaymentEntry(AccountsController): if not self.get(field): self.set(field, bank_data.account) + def validate_payment_type_with_outstanding(self): + total_outstanding = sum(d.allocated_amount for d in self.get('references')) + if total_outstanding < 0 and self.party_type == 'Customer' and self.payment_type == 'Receive': + frappe.throw(_("Cannot receive from customer against negative outstanding"), title=_("Incorrect Payment Type")) + def validate_allocated_amount(self): for d in self.get("references"): if (flt(d.allocated_amount))> 0: @@ -530,10 +536,8 @@ class PaymentEntry(AccountsController): base_total_allocated_amount += flt(flt(d.allocated_amount) * flt(d.exchange_rate), self.precision("base_paid_amount")) - # Do not use absolute values as only credit notes could be allocated - # and total allocated should be negative in that scenario - self.total_allocated_amount = total_allocated_amount - self.base_total_allocated_amount = base_total_allocated_amount + self.total_allocated_amount = abs(total_allocated_amount) + self.base_total_allocated_amount = abs(base_total_allocated_amount) def set_unallocated_amount(self): self.unallocated_amount = 0 diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 11e113d000..9d84d94074 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -559,7 +559,7 @@ def remove_ref_doc_link_from_pe(ref_type, ref_no): except Exception as e: msg = _("There were issues unlinking payment entry {0}.").format(pe_doc.name) msg += '
' - msg += _("Please cancel payment entry manually first and then resubmit") + msg += _("Please cancel payment entry manually first") frappe.throw(msg, title=_("Payment Unlink Error")) frappe.db.sql("""update `tabPayment Entry` set total_allocated_amount=%s, From b5162390e5881a110ca460ad4720de40ff7d4e1d Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 10 Aug 2021 14:52:24 +0530 Subject: [PATCH 04/27] fix: Only do specific validations on reference unlink --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 7 +++---- erpnext/accounts/utils.py | 5 ++++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 66b46675dc..16b4720b37 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -45,7 +45,7 @@ class PaymentEntry(AccountsController): self.party_account = self.paid_to self.party_account_currency = self.paid_to_account_currency - def validate(self, on_reference_unlink=False): + def validate(self): self.setup_party_account_field() self.set_missing_values() self.validate_payment_type() @@ -65,9 +65,8 @@ class PaymentEntry(AccountsController): self.set_remarks() self.validate_duplicate_entry() self.validate_payment_type_with_outstanding() - if not on_reference_unlink: - self.validate_allocated_amount() - self.validate_paid_invoices() + self.validate_allocated_amount() + self.validate_paid_invoices() self.ensure_supplier_is_not_blocked() self.set_status() diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 9d84d94074..0d3aa8f0ef 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -555,7 +555,10 @@ def remove_ref_doc_link_from_pe(ref_type, ref_no): for pe in linked_pe: try: pe_doc = frappe.get_doc("Payment Entry", pe) - pe_doc.validate(on_reference_unlink=True) + pe_doc.set_total_allocated_amount() + pe_doc.set_unallocated_amount() + pe_doc.clear_unallocated_reference_document_rows() + pe_doc.validate_payment_type_with_outstanding() except Exception as e: msg = _("There were issues unlinking payment entry {0}.").format(pe_doc.name) msg += '
' From 7141860c04fd1541f8d346a48e3e058f4ad443c9 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 10 Aug 2021 22:21:28 +0530 Subject: [PATCH 05/27] test: Update exchange rate in test cases --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 4 +++- .../accounts/doctype/payment_entry/test_payment_entry.py | 6 +++--- erpnext/accounts/utils.py | 6 +++--- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 16b4720b37..a991c0679b 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -58,6 +58,7 @@ class PaymentEntry(AccountsController): self.set_amounts() self.validate_amounts() self.apply_taxes() + self.set_amounts_after_tax() self.clear_unallocated_reference_document_rows() self.validate_payment_against_negative_invoice() self.validate_transaction_reference() @@ -477,7 +478,6 @@ class PaymentEntry(AccountsController): def set_amounts(self): self.set_received_amount() self.set_amounts_in_company_currency() - self.set_amounts_after_tax() self.set_total_allocated_amount() self.set_unallocated_amount() self.set_difference_amount() @@ -492,6 +492,8 @@ class PaymentEntry(AccountsController): def set_received_amount(self): self.base_received_amount = self.base_paid_amount + if self.paid_from_account_currency == self.paid_to_account_currency: + self.received_amount = self.paid_amount def set_amounts_after_tax(self): applicable_tax = 0 diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index d1302f5ae7..420e8583ea 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -107,7 +107,7 @@ class TestPaymentEntry(unittest.TestCase): pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank USD - _TC") pe.reference_no = "1" pe.reference_date = "2016-01-01" - pe.target_exchange_rate = 50 + pe.source_exchange_rate = 50 pe.insert() pe.submit() @@ -154,7 +154,7 @@ class TestPaymentEntry(unittest.TestCase): pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank USD - _TC") pe.reference_no = "1" pe.reference_date = "2016-01-01" - pe.target_exchange_rate = 50 + pe.source_exchange_rate = 50 pe.insert() pe.submit() @@ -463,7 +463,7 @@ class TestPaymentEntry(unittest.TestCase): pe = get_payment_entry("Sales Invoice", si.name, bank_account="_Test Bank USD - _TC") pe.reference_no = "1" pe.reference_date = "2016-01-01" - pe.target_exchange_rate = 55 + pe.source_exchange_rate = 55 pe.append("deductions", { "account": "_Test Exchange Gain/Loss - _TC", diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 0d3aa8f0ef..73e2c2ea86 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -19,6 +19,7 @@ from erpnext.stock import get_warehouse_account_map class StockValueAndAccountBalanceOutOfSync(frappe.ValidationError): pass class FiscalYearError(frappe.ValidationError): pass +class PaymentEntryUnlinkError(frappe.ValidationError): pass @frappe.whitelist() def get_fiscal_year(date=None, fiscal_year=None, label="Date", verbose=1, company=None, as_dict=False): @@ -555,15 +556,14 @@ def remove_ref_doc_link_from_pe(ref_type, ref_no): for pe in linked_pe: try: pe_doc = frappe.get_doc("Payment Entry", pe) - pe_doc.set_total_allocated_amount() - pe_doc.set_unallocated_amount() + pe_doc.set_amounts() pe_doc.clear_unallocated_reference_document_rows() pe_doc.validate_payment_type_with_outstanding() except Exception as e: msg = _("There were issues unlinking payment entry {0}.").format(pe_doc.name) msg += '
' msg += _("Please cancel payment entry manually first") - frappe.throw(msg, title=_("Payment Unlink Error")) + frappe.throw(msg, exc=PaymentEntryUnlinkError, title=_("Payment Unlink Error")) frappe.db.sql("""update `tabPayment Entry` set total_allocated_amount=%s, base_total_allocated_amount=%s, unallocated_amount=%s, modified=%s, modified_by=%s From 02f44528f2b43c16fc8660b3e353533ba1bc3976 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Tue, 10 Aug 2021 22:21:52 +0530 Subject: [PATCH 06/27] test: Add test case for payment entry unlink --- .../sales_invoice/test_sales_invoice.py | 41 ++++++++++++++++++- 1 file changed, 40 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index be20b18bea..623ab3f6f2 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -25,6 +25,7 @@ from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt from erpnext.stock.doctype.delivery_note.delivery_note import make_sales_invoice from erpnext.stock.utils import get_incoming_rate +from erpnext.accounts.utils import PaymentEntryUnlinkError class TestSalesInvoice(unittest.TestCase): def make(self): @@ -135,7 +136,7 @@ class TestSalesInvoice(unittest.TestCase): pe.paid_to_account_currency = si.currency pe.source_exchange_rate = 1 pe.target_exchange_rate = 1 - pe.paid_amount = si.grand_total + pe.paid_amount = si.outstanding_amount pe.insert() pe.submit() @@ -144,6 +145,44 @@ class TestSalesInvoice(unittest.TestCase): self.assertRaises(frappe.LinkExistsError, si.cancel) unlink_payment_on_cancel_of_invoice() + def test_payment_entry_unlink_against_standalone_credit_note(self): + from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry + si1 = create_sales_invoice(rate=1000) + si2 = create_sales_invoice(rate=300) + si3 = create_sales_invoice(qty=-1, rate=300, is_return=1) + + + pe = get_payment_entry("Sales Invoice", si1.name, bank_account="_Test Bank - _TC") + pe.append('references', { + 'reference_doctype': 'Sales Invoice', + 'reference_name': si2.name, + 'total_amount': si2.grand_total, + 'outstanding_amount': si2.outstanding_amount, + 'allocated_amount': si2.outstanding_amount + }) + + pe.append('references', { + 'reference_doctype': 'Sales Invoice', + 'reference_name': si3.name, + 'total_amount': si3.grand_total, + 'outstanding_amount': si3.outstanding_amount, + 'allocated_amount': si3.outstanding_amount + }) + + pe.reference_no = 'Test001' + pe.reference_date = nowdate() + pe.save() + pe.submit() + + unlink_payment_on_cancel_of_invoice() + si2.load_from_db() + si2.cancel() + + si1.load_from_db() + self.assertRaises(PaymentEntryUnlinkError, si1.cancel) + unlink_payment_on_cancel_of_invoice(0) + + def test_sales_invoice_calculation_export_currency(self): si = frappe.copy_doc(test_records[2]) si.currency = "USD" From a1f0cebda5f21bf4d1dba4214fd77e0dec210578 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 11 Aug 2021 11:36:49 +0530 Subject: [PATCH 07/27] fix: Do not update settings for test --- erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 623ab3f6f2..92bf746ad8 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -174,13 +174,11 @@ class TestSalesInvoice(unittest.TestCase): pe.save() pe.submit() - unlink_payment_on_cancel_of_invoice() si2.load_from_db() si2.cancel() si1.load_from_db() self.assertRaises(PaymentEntryUnlinkError, si1.cancel) - unlink_payment_on_cancel_of_invoice(0) def test_sales_invoice_calculation_export_currency(self): From 57e326e7d0ad1a1053ac2d987aacd540353ef47d Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Sat, 21 Aug 2021 17:59:11 +0530 Subject: [PATCH 08/27] fix: Consolidated balance sheet showing incorrect values (#26975) --- .../consolidated_financial_statement.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py index 56a67bb098..fc4212733a 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py @@ -210,10 +210,10 @@ def get_data(companies, root_type, balance_must_be, fiscal_year, filters=None, i company_currency = get_company_currency(filters) if filters.filter_based_on == 'Fiscal Year': - start_date = fiscal_year.year_start_date + start_date = fiscal_year.year_start_date if filters.report != 'Balance Sheet' else None end_date = fiscal_year.year_end_date else: - start_date = filters.period_start_date + start_date = filters.period_start_date if filters.report != 'Balance Sheet' else None end_date = filters.period_end_date gl_entries_by_account = {} From 496bff5136c7a72c3346361af7b7e41e787cff1b Mon Sep 17 00:00:00 2001 From: Deepesh Garg <42651287+deepeshgarg007@users.noreply.github.com> Date: Sun, 22 Aug 2021 18:10:51 +0530 Subject: [PATCH 09/27] feat: Column for total amount due in Accounts Receivable/Payable Summary (#27069) --- .../report/accounts_receivable/accounts_receivable.py | 2 ++ .../accounts_receivable_summary.py | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index b54646fd27..cedfc0f58b 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -535,6 +535,8 @@ class ReceivablePayableReport(object): if getdate(entry_date) > getdate(self.filters.report_date): row.range1 = row.range2 = row.range3 = row.range4 = row.range5 = 0.0 + row.total_due = row.range1 + row.range2 + row.range3 + row.range4 + row.range5 + 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 = row.range5 = 0.0 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 e94b30921f..4bfb022c4e 100644 --- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py +++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py @@ -82,6 +82,7 @@ class AccountsReceivableSummary(ReceivablePayableReport): "range3": 0.0, "range4": 0.0, "range5": 0.0, + "total_due": 0.0, "sales_person": [] })) @@ -135,3 +136,6 @@ class AccountsReceivableSummary(ReceivablePayableReport): "{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)) + + # Add column for total due amount + self.add_column(label="Total Amount Due", fieldname='total_due') From 7d627df4dbe15b3db16da031945ab98ccaae71ea Mon Sep 17 00:00:00 2001 From: Subin Tom <36098155+nemesis189@users.noreply.github.com> Date: Mon, 23 Aug 2021 11:05:07 +0530 Subject: [PATCH 10/27] fix: pos return payment mode issue (#26872) --- erpnext/controllers/taxes_and_totals.py | 9 ++++----- erpnext/public/js/controllers/taxes_and_totals.js | 2 -- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py index 05edb2530c..7c6d3552f1 100644 --- a/erpnext/controllers/taxes_and_totals.py +++ b/erpnext/controllers/taxes_and_totals.py @@ -595,7 +595,8 @@ class calculate_taxes_and_totals(object): self.doc.precision("outstanding_amount")) if self.doc.doctype == 'Sales Invoice' and self.doc.get('is_pos') and self.doc.get('is_return'): - self.update_paid_amount_for_return(total_amount_to_pay) + self.set_total_amount_to_default_mop(total_amount_to_pay) + self.calculate_paid_amount() def calculate_paid_amount(self): @@ -675,7 +676,7 @@ class calculate_taxes_and_totals(object): def set_item_wise_tax_breakup(self): self.doc.other_charges_calculation = get_itemised_tax_breakup_html(self.doc) - def update_paid_amount_for_return(self, total_amount_to_pay): + def set_total_amount_to_default_mop(self, total_amount_to_pay): default_mode_of_payment = frappe.db.get_value('POS Payment Method', {'parent': self.doc.pos_profile, 'default': 1}, ['mode_of_payment'], as_dict=1) @@ -685,9 +686,7 @@ class calculate_taxes_and_totals(object): 'mode_of_payment': default_mode_of_payment.mode_of_payment, 'amount': total_amount_to_pay, 'default': 1 - }) - - self.calculate_paid_amount() + }) def get_itemised_tax_breakup_html(doc): if not doc.taxes: diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js index e8f31225ba..702064fe55 100644 --- a/erpnext/public/js/controllers/taxes_and_totals.js +++ b/erpnext/public/js/controllers/taxes_and_totals.js @@ -754,8 +754,6 @@ erpnext.taxes_and_totals = class TaxesAndTotals extends erpnext.payments { } }); this.frm.refresh_fields(); - - this.calculate_paid_amount(); } set_default_payment(total_amount_to_pay, update_paid_amount) { From 8b2fe9e793e226339bbcec554d8c57a09f87b5c5 Mon Sep 17 00:00:00 2001 From: Subin Tom <36098155+nemesis189@users.noreply.github.com> Date: Mon, 23 Aug 2021 11:17:31 +0530 Subject: [PATCH 11/27] fix: eway bill version changed to 1.0.0421 (#27044) --- erpnext/regional/india/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/india/utils.py b/erpnext/regional/india/utils.py index 4e4dcf8585..ce5aa10902 100644 --- a/erpnext/regional/india/utils.py +++ b/erpnext/regional/india/utils.py @@ -475,7 +475,7 @@ def get_ewb_data(dt, dn): ewaybills.append(data) data = { - 'version': '1.0.1118', + 'version': '1.0.0421', 'billLists': ewaybills } From 098d349bf4181100111e2b01025ce7d939b96392 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 23 Aug 2021 13:36:13 +0530 Subject: [PATCH 12/27] fix: stock ledger report not working if include uom selected in filter --- erpnext/stock/report/stock_ledger/stock_ledger.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py index 8909f217f4..b6923e97c4 100644 --- a/erpnext/stock/report/stock_ledger/stock_ledger.py +++ b/erpnext/stock/report/stock_ledger/stock_ledger.py @@ -23,6 +23,7 @@ def execute(filters=None): conversion_factors = [] if opening_row: data.append(opening_row) + conversion_factors.append(0) actual_qty = stock_value = 0 From e1f070437ae5869ac43896730cccb4b4c904e7de Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Mon, 23 Aug 2021 14:27:55 +0530 Subject: [PATCH 13/27] fix: selected batch no changed on updation of qty --- erpnext/selling/sales_common.js | 4 ++++ erpnext/stock/get_item_details.py | 4 ++++ 2 files changed, 8 insertions(+) diff --git a/erpnext/selling/sales_common.js b/erpnext/selling/sales_common.js index 22bf3fc94f..2de57c87f1 100644 --- a/erpnext/selling/sales_common.js +++ b/erpnext/selling/sales_common.js @@ -394,6 +394,10 @@ erpnext.selling.SellingController = class SellingController extends erpnext.Tran } _set_batch_number(doc) { + if (doc.batch_no) { + return + } + let args = {'item_code': doc.item_code, 'warehouse': doc.warehouse, 'qty': flt(doc.qty) * flt(doc.conversion_factor)}; if (doc.has_serial_no && doc.serial_no) { args['serial_no'] = doc.serial_no diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index a0fbcecc5d..c72073c614 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -278,6 +278,10 @@ def get_basic_details(args, item, overwrite_warehouse=True): else: args.uom = item.stock_uom + if (args.get("batch_no") and + item.name != frappe.get_cached_value('Batch', args.get("batch_no"), 'item')): + args['batch_no'] = '' + out = frappe._dict({ "item_code": item.name, "item_name": item.item_name, From 47b63a627d7d8b6d32cc5ff5713af9eb973001d3 Mon Sep 17 00:00:00 2001 From: Subin Tom <36098155+nemesis189@users.noreply.github.com> Date: Mon, 23 Aug 2021 15:38:26 +0530 Subject: [PATCH 14/27] fix: Eway bill test update to check ver 1.0.0421 (#27083) --- erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 984a652248..01a6b02732 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -2014,7 +2014,7 @@ class TestSalesInvoice(unittest.TestCase): data = get_ewb_data("Sales Invoice", [si.name]) - self.assertEqual(data['version'], '1.0.1118') + self.assertEqual(data['version'], '1.0.0421') self.assertEqual(data['billLists'][0]['fromGstin'], '27AAECE4835E1ZR') self.assertEqual(data['billLists'][0]['fromTrdName'], '_Test Company') self.assertEqual(data['billLists'][0]['toTrdName'], '_Test Customer') From 49e0a4f28714b468ba5edad296b30f22991210f3 Mon Sep 17 00:00:00 2001 From: Rushabh Mehta Date: Tue, 24 Aug 2021 08:33:57 +0530 Subject: [PATCH 15/27] Revert "fix: Salary component account filter (#26604)" This reverts commit ae9d1d9617015b6e2714a9fcd027a2957053d99a. --- .../doctype/salary_component/salary_component.js | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/erpnext/payroll/doctype/salary_component/salary_component.js b/erpnext/payroll/doctype/salary_component/salary_component.js index e9e6f81862..dbf75140ac 100644 --- a/erpnext/payroll/doctype/salary_component/salary_component.js +++ b/erpnext/payroll/doctype/salary_component/salary_component.js @@ -4,18 +4,11 @@ frappe.ui.form.on('Salary Component', { setup: function(frm) { frm.set_query("account", "accounts", function(doc, cdt, cdn) { - let d = frappe.get_doc(cdt, cdn); - - let root_type = "Liability"; - if (frm.doc.type == "Deduction") { - root_type = "Expense"; - } - + var d = locals[cdt][cdn]; return { filters: { "is_group": 0, - "company": d.company, - "root_type": root_type + "company": d.company } }; }); From 332ac105b5ffec04009539eba94b153a8b93c0c4 Mon Sep 17 00:00:00 2001 From: Pruthvi Patel Date: Tue, 24 Aug 2021 12:07:38 +0530 Subject: [PATCH 16/27] refactor: use `read_only_depends_on` instead of code (#27008) --- erpnext/accounts/doctype/pos_invoice/pos_invoice.js | 6 +----- erpnext/accounts/doctype/pos_invoice/pos_invoice.json | 5 +++-- erpnext/accounts/doctype/sales_invoice/sales_invoice.js | 8 +------- erpnext/accounts/doctype/sales_invoice/sales_invoice.json | 5 +++-- 4 files changed, 8 insertions(+), 16 deletions(-) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js index 181e9f8ec0..e317546481 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js @@ -111,16 +111,12 @@ erpnext.selling.POSInvoiceController = class POSInvoiceController extends erpnex } write_off_outstanding_amount_automatically() { - if(cint(this.frm.doc.write_off_outstanding_amount_automatically)) { + if (cint(this.frm.doc.write_off_outstanding_amount_automatically)) { frappe.model.round_floats_in(this.frm.doc, ["grand_total", "paid_amount"]); // this will make outstanding amount 0 this.frm.set_value("write_off_amount", flt(this.frm.doc.grand_total - this.frm.doc.paid_amount - this.frm.doc.total_advance, precision("write_off_amount")) ); - this.frm.toggle_enable("write_off_amount", false); - - } else { - this.frm.toggle_enable("write_off_amount", true); } this.calculate_outstanding_amount(false); diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json index fcccb39b70..3e22b9e00a 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json @@ -1183,7 +1183,8 @@ "label": "Write Off Amount", "no_copy": 1, "options": "currency", - "print_hide": 1 + "print_hide": 1, + "read_only_depends_on": "eval: doc.write_off_outstanding_amount_automatically" }, { "fieldname": "base_write_off_amount", @@ -1554,7 +1555,7 @@ "icon": "fa fa-file-text", "is_submittable": 1, "links": [], - "modified": "2021-08-17 20:13:44.255437", + "modified": "2021-08-18 16:13:52.080543", "modified_by": "Administrator", "module": "Accounts", "name": "POS Invoice", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index 8d65101b3b..2071827d99 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -324,16 +324,12 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e } write_off_outstanding_amount_automatically() { - if(cint(this.frm.doc.write_off_outstanding_amount_automatically)) { + if (cint(this.frm.doc.write_off_outstanding_amount_automatically)) { frappe.model.round_floats_in(this.frm.doc, ["grand_total", "paid_amount"]); // this will make outstanding amount 0 this.frm.set_value("write_off_amount", flt(this.frm.doc.grand_total - this.frm.doc.paid_amount - this.frm.doc.total_advance, precision("write_off_amount")) ); - this.frm.toggle_enable("write_off_amount", false); - - } else { - this.frm.toggle_enable("write_off_amount", true); } this.calculate_outstanding_amount(false); @@ -787,8 +783,6 @@ frappe.ui.form.on('Sales Invoice', { if (frappe.boot.sysdefaults.country == 'India') unhide_field(['c_form_applicable', 'c_form_no']); else hide_field(['c_form_applicable', 'c_form_no']); - frm.toggle_enable("write_off_amount", !!!cint(doc.write_off_outstanding_amount_automatically)); - frm.refresh_fields(); }, diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index e317443b91..5023c9c61a 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -1444,7 +1444,8 @@ "label": "Write Off Amount", "no_copy": 1, "options": "currency", - "print_hide": 1 + "print_hide": 1, + "read_only_depends_on": "eval:doc.write_off_outstanding_amount_automatically" }, { "fieldname": "base_write_off_amount", @@ -2014,7 +2015,7 @@ "link_fieldname": "consolidated_invoice" } ], - "modified": "2021-08-17 20:16:12.737743", + "modified": "2021-08-18 16:07:45.122570", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", From 0dff0beabaa937e397eae2f930bae337cfe8af82 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 24 Aug 2021 12:16:46 +0530 Subject: [PATCH 17/27] fix: stock analytics report date range issues and add company filter (#27014) * test: tests for correct get_period_date_ranges * fix: stock analytics report date range issues - Upon selecting second half of month with Monthly filter, data from that period was missing. - Solution: "round down" the date as per expected frequency. * chore: drop py2 and fix misleading docstring * test: fix test to avoid FY clash * feat: add company filter in stock analytics report [skip ci] Co-authored-by: Marica --- .../report/stock_analytics/stock_analytics.js | 16 +++++++- .../report/stock_analytics/stock_analytics.py | 37 ++++++++++++++++--- .../stock_analytics/test_stock_analytics.py | 35 ++++++++++++++++++ 3 files changed, 82 insertions(+), 6 deletions(-) create mode 100644 erpnext/stock/report/stock_analytics/test_stock_analytics.py diff --git a/erpnext/stock/report/stock_analytics/stock_analytics.js b/erpnext/stock/report/stock_analytics/stock_analytics.js index 6b384e2861..78afe6d264 100644 --- a/erpnext/stock/report/stock_analytics/stock_analytics.js +++ b/erpnext/stock/report/stock_analytics/stock_analytics.js @@ -36,12 +36,26 @@ frappe.query_reports["Stock Analytics"] = { options:"Brand", default: "", }, + { + fieldname: "company", + label: __("Company"), + fieldtype: "Link", + options: "Company", + default: frappe.defaults.get_user_default("Company"), + reqd: 1, + }, { fieldname: "warehouse", label: __("Warehouse"), fieldtype: "Link", - options:"Warehouse", + options: "Warehouse", default: "", + get_query: function() { + const company = frappe.query_report.get_filter_value('company'); + return { + filters: { 'company': company } + } + } }, { fieldname: "from_date", diff --git a/erpnext/stock/report/stock_analytics/stock_analytics.py b/erpnext/stock/report/stock_analytics/stock_analytics.py index d62abed91f..a1e1e7fce7 100644 --- a/erpnext/stock/report/stock_analytics/stock_analytics.py +++ b/erpnext/stock/report/stock_analytics/stock_analytics.py @@ -1,14 +1,15 @@ # Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors # For license information, please see license.txt +import datetime -from __future__ import unicode_literals import frappe from frappe import _, scrub -from frappe.utils import getdate, flt +from frappe.utils import getdate, get_quarter_start, get_first_day_of_week +from frappe.utils import get_first_day as get_first_day_of_month + from erpnext.stock.report.stock_balance.stock_balance import (get_items, get_stock_ledger_entries, get_item_details) from erpnext.accounts.utils import get_fiscal_year from erpnext.stock.utils import is_reposting_item_valuation_in_progress -from six import iteritems def execute(filters=None): is_reposting_item_valuation_in_progress() @@ -71,7 +72,8 @@ def get_columns(filters): def get_period_date_ranges(filters): from dateutil.relativedelta import relativedelta - from_date, to_date = getdate(filters.from_date), getdate(filters.to_date) + from_date = round_down_to_nearest_frequency(filters.from_date, filters.range) + to_date = getdate(filters.to_date) increment = { "Monthly": 1, @@ -97,6 +99,31 @@ def get_period_date_ranges(filters): return periodic_daterange + +def round_down_to_nearest_frequency(date: str, frequency: str) -> datetime.datetime: + """Rounds down the date to nearest frequency unit. + example: + + >>> round_down_to_nearest_frequency("2021-02-21", "Monthly") + datetime.datetime(2021, 2, 1) + + >>> round_down_to_nearest_frequency("2021-08-21", "Yearly") + datetime.datetime(2021, 1, 1) + """ + + def _get_first_day_of_fiscal_year(date): + fiscal_year = get_fiscal_year(date) + return fiscal_year and fiscal_year[1] or date + + round_down_function = { + "Monthly": get_first_day_of_month, + "Quarterly": get_quarter_start, + "Weekly": get_first_day_of_week, + "Yearly": _get_first_day_of_fiscal_year, + }.get(frequency, getdate) + return round_down_function(date) + + def get_period(posting_date, filters): months = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"] @@ -177,7 +204,7 @@ def get_data(filters): periodic_data = get_periodic_data(sle, filters) ranges = get_period_date_ranges(filters) - for dummy, item_data in iteritems(item_details): + for dummy, item_data in item_details.items(): row = { "name": item_data.name, "item_name": item_data.item_name, diff --git a/erpnext/stock/report/stock_analytics/test_stock_analytics.py b/erpnext/stock/report/stock_analytics/test_stock_analytics.py new file mode 100644 index 0000000000..00e268b4e0 --- /dev/null +++ b/erpnext/stock/report/stock_analytics/test_stock_analytics.py @@ -0,0 +1,35 @@ +import datetime +import unittest + +from frappe import _dict +from erpnext.accounts.utils import get_fiscal_year + +from erpnext.stock.report.stock_analytics.stock_analytics import get_period_date_ranges + + +class TestStockAnalyticsReport(unittest.TestCase): + def test_get_period_date_ranges(self): + + filters = _dict(range="Monthly", from_date="2020-12-28", to_date="2021-02-06") + + ranges = get_period_date_ranges(filters) + + expected_ranges = [ + [datetime.date(2020, 12, 1), datetime.date(2020, 12, 31)], + [datetime.date(2021, 1, 1), datetime.date(2021, 1, 31)], + [datetime.date(2021, 2, 1), datetime.date(2021, 2, 6)], + ] + + self.assertEqual(ranges, expected_ranges) + + def test_get_period_date_ranges_yearly(self): + + filters = _dict(range="Yearly", from_date="2021-01-28", to_date="2021-02-06") + + ranges = get_period_date_ranges(filters) + first_date = get_fiscal_year("2021-01-28")[1] + expected_ranges = [ + [first_date, datetime.date(2021, 2, 6)], + ] + + self.assertEqual(ranges, expected_ranges) From 6de7b8ea93e3ffe621976ebf3b613406a379216d Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 24 Aug 2021 12:18:40 +0530 Subject: [PATCH 18/27] fix: discard empty rows from update items (#27021) --- erpnext/controllers/accounts_controller.py | 5 +++++ erpnext/public/js/utils.js | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 8addbeb98f..01d354df81 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -1836,6 +1836,11 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil for d in data: new_child_flag = False + + if not d.get("item_code"): + # ignore empty rows + continue + if not d.get("docname"): new_child_flag = True check_doc_permissions(parent, 'create') diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index f1b9235fe3..f240b8ca91 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -563,7 +563,7 @@ erpnext.utils.update_child_items = function(opts) { }, ], primary_action: function() { - const trans_items = this.get_values()["trans_items"]; + const trans_items = this.get_values()["trans_items"].filter((item) => !!item.item_code); frappe.call({ method: 'erpnext.controllers.accounts_controller.update_child_qty_rate', freeze: true, From c09d8a28098ca2963122373ae18f0c8c1954fcf3 Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Tue, 24 Aug 2021 12:20:34 +0530 Subject: [PATCH 19/27] fix(ux): keep stock entry title & purpose in sync (#27043) Co-authored-by: rohitwaghchaure --- erpnext/stock/doctype/stock_entry/stock_entry.json | 4 +--- erpnext/stock/doctype/stock_entry/stock_entry.py | 9 +++++++++ 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json index e6ce3c851f..2f37778896 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.json +++ b/erpnext/stock/doctype/stock_entry/stock_entry.json @@ -84,8 +84,6 @@ "oldfieldtype": "Section Break" }, { - "allow_on_submit": 1, - "default": "{purpose}", "fieldname": "title", "fieldtype": "Data", "hidden": 1, @@ -630,7 +628,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2021-08-17 20:16:12.737743", + "modified": "2021-08-20 19:19:31.514846", "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 90a33d3617..0b4592c12f 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -58,6 +58,7 @@ class StockEntry(StockController): self.validate_posting_time() self.validate_purpose() + self.set_title() self.validate_item() self.validate_customer_provided_item() self.validate_qty() @@ -1608,6 +1609,14 @@ class StockEntry(StockController): return sorted(list(set(get_serial_nos(self.pro_doc.serial_no)) - set(used_serial_nos))) + def set_title(self): + if frappe.flags.in_import and self.title: + # Allow updating title during data import/update + return + + self.title = self.purpose + + @frappe.whitelist() def move_sample_to_retention_warehouse(company, items): if isinstance(items, string_types): From 1e05c9467bc5fa9315a67c7f8ed9ff5b17baa3e6 Mon Sep 17 00:00:00 2001 From: Suraj Shetty Date: Tue, 24 Aug 2021 14:21:14 +0530 Subject: [PATCH 20/27] fix: Use remove_all from file_manager --- erpnext/regional/italy/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/regional/italy/utils.py b/erpnext/regional/italy/utils.py index ba1aeafc3e..024f820568 100644 --- a/erpnext/regional/italy/utils.py +++ b/erpnext/regional/italy/utils.py @@ -6,7 +6,7 @@ import frappe from frappe.utils import flt, cstr from erpnext.controllers.taxes_and_totals import get_itemised_tax from frappe import _ -from frappe.core.doctype.file.file import remove_file +from frappe.utils.file_manager import remove_file from six import string_types from frappe.desk.form.load import get_attachments from erpnext.regional.italy import state_codes From 6cf9254ee5d6599f3dfe1b696dbb51edc33b965c Mon Sep 17 00:00:00 2001 From: Saqib Date: Tue, 24 Aug 2021 15:26:44 +0530 Subject: [PATCH 21/27] fix: invalid imports (#27101) --- erpnext/regional/italy/utils.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/erpnext/regional/italy/utils.py b/erpnext/regional/italy/utils.py index ba1aeafc3e..56f609eb23 100644 --- a/erpnext/regional/italy/utils.py +++ b/erpnext/regional/italy/utils.py @@ -6,9 +6,8 @@ import frappe from frappe.utils import flt, cstr from erpnext.controllers.taxes_and_totals import get_itemised_tax from frappe import _ -from frappe.core.doctype.file.file import remove_file +from frappe.utils.file_manager import remove_file from six import string_types -from frappe.desk.form.load import get_attachments from erpnext.regional.italy import state_codes From 9e82c24b277d09b61dc94f345b173603bee97a02 Mon Sep 17 00:00:00 2001 From: Saqib Date: Tue, 24 Aug 2021 15:37:00 +0530 Subject: [PATCH 22/27] fix: pos closing entry cancellation test (#27099) * fix: pos closing entry cancellation test * fix: invalid imports * fix: sider issue --- .../pos_closing_entry/test_pos_closing_entry.py | 10 ++++++++-- erpnext/accounts/doctype/pos_invoice/pos_invoice.js | 2 +- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py b/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py index b596c0cf25..5b18ebb40d 100644 --- a/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py +++ b/erpnext/accounts/doctype/pos_closing_entry/test_pos_closing_entry.py @@ -85,9 +85,15 @@ class TestPOSClosingEntry(unittest.TestCase): pcv_doc.load_from_db() pcv_doc.cancel() - si_doc.load_from_db() + + cancelled_invoice = frappe.db.get_value( + 'POS Invoice Merge Log', {'pos_closing_entry': pcv_doc.name}, + 'consolidated_invoice' + ) + docstatus = frappe.db.get_value("Sales Invoice", cancelled_invoice, 'docstatus') + self.assertEqual(docstatus, 2) + pos_inv1.load_from_db() - self.assertEqual(si_doc.docstatus, 2) self.assertEqual(pos_inv1.status, 'Paid') diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js index e317546481..15c292211c 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.js +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.js @@ -16,7 +16,7 @@ erpnext.selling.POSInvoiceController = class POSInvoiceController extends erpnex onload(doc) { super.onload(); - this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice Merge Log']; + this.frm.ignore_doctypes_on_cancel_all = ['POS Invoice Merge Log', 'POS Closing Entry']; if(doc.__islocal && doc.is_pos && frappe.get_route_str() !== 'point-of-sale') { this.frm.script_manager.trigger("is_pos"); this.frm.refresh_fields(); From d4d5a4221a00771864d3b27eeb9a4f9580614c04 Mon Sep 17 00:00:00 2001 From: Subin Tom <36098155+nemesis189@users.noreply.github.com> Date: Tue, 24 Aug 2021 16:22:46 +0530 Subject: [PATCH 23/27] feat: coupon code discount in pos invoice (#27004) --- .../doctype/pos_invoice/pos_invoice.json | 9 +++++++++ .../doctype/pos_invoice/pos_invoice.py | 11 +++++++++++ .../doctype/pricing_rule/pricing_rule.py | 9 ++++++++- erpnext/public/js/controllers/transaction.js | 19 +++++++++++++------ erpnext/public/scss/point-of-sale.scss | 2 ++ 5 files changed, 43 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json index 3e22b9e00a..b819537400 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json @@ -99,6 +99,7 @@ "loyalty_redemption_account", "loyalty_redemption_cost_center", "section_break_49", + "coupon_code", "apply_discount_on", "base_discount_amount", "column_break_51", @@ -1550,6 +1551,14 @@ "no_copy": 1, "options": "Sales Invoice", "read_only": 1 + }, + { + "depends_on": "coupon_code", + "fieldname": "coupon_code", + "fieldtype": "Link", + "label": "Coupon Code", + "options": "Coupon Code", + "print_hide": 1 } ], "icon": "fa fa-file-text", diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index 8ec4ef224c..759cad53d4 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -44,6 +44,9 @@ class POSInvoice(SalesInvoice): self.validate_pos() self.validate_payment_amount() self.validate_loyalty_transaction() + if self.coupon_code: + from erpnext.accounts.doctype.pricing_rule.utils import validate_coupon_code + validate_coupon_code(self.coupon_code) def on_submit(self): # create the loyalty point ledger entry if the customer is enrolled in any loyalty program @@ -58,6 +61,10 @@ class POSInvoice(SalesInvoice): self.check_phone_payments() self.set_status(update=True) + if self.coupon_code: + from erpnext.accounts.doctype.pricing_rule.utils import update_coupon_code_count + update_coupon_code_count(self.coupon_code,'used') + def before_cancel(self): if self.consolidated_invoice and frappe.db.get_value('Sales Invoice', self.consolidated_invoice, 'docstatus') == 1: pos_closing_entry = frappe.get_all( @@ -84,6 +91,10 @@ class POSInvoice(SalesInvoice): against_psi_doc.delete_loyalty_point_entry() against_psi_doc.make_loyalty_point_entry() + if self.coupon_code: + from erpnext.accounts.doctype.pricing_rule.utils import update_coupon_code_count + update_coupon_code_count(self.coupon_code,'cancelled') + def check_phone_payments(self): for pay in self.payments: if pay.type == "Phone" and pay.amount >= 0: diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index 556f49d34c..4903c50e17 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -198,12 +198,19 @@ def apply_pricing_rule(args, doc=None): set_serial_nos_based_on_fifo = frappe.db.get_single_value("Stock Settings", "automatically_set_serial_nos_based_on_fifo") + item_code_list = tuple(item.get('item_code') for item in item_list) + query_items = frappe.get_all('Item', fields=['item_code','has_serial_no'], filters=[['item_code','in',item_code_list]],as_list=1) + serialized_items = dict() + for item_code, val in query_items: + serialized_items.setdefault(item_code, val) + for item in item_list: args_copy = copy.deepcopy(args) args_copy.update(item) data = get_pricing_rule_for_item(args_copy, item.get('price_list_rate'), doc=doc) out.append(data) - if not item.get("serial_no") and set_serial_nos_based_on_fifo and not args.get('is_return'): + + if serialized_items.get(item.get('item_code')) and not item.get("serial_no") and set_serial_nos_based_on_fifo and not args.get('is_return'): out[0].update(get_serial_no_for_item(args_copy)) return out diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 9375e358a9..2538852bfa 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -2242,12 +2242,19 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe coupon_code() { var me = this; - frappe.run_serially([ - () => this.frm.doc.ignore_pricing_rule=1, - () => me.ignore_pricing_rule(), - () => this.frm.doc.ignore_pricing_rule=0, - () => me.apply_pricing_rule() - ]); + if (this.frm.doc.coupon_code) { + frappe.run_serially([ + () => this.frm.doc.ignore_pricing_rule=1, + () => me.ignore_pricing_rule(), + () => this.frm.doc.ignore_pricing_rule=0, + () => me.apply_pricing_rule() + ]); + } else { + frappe.run_serially([ + () => this.frm.doc.ignore_pricing_rule=1, + () => me.ignore_pricing_rule() + ]); + } } }; diff --git a/erpnext/public/scss/point-of-sale.scss b/erpnext/public/scss/point-of-sale.scss index c77b2ce3df..1677e9b3de 100644 --- a/erpnext/public/scss/point-of-sale.scss +++ b/erpnext/public/scss/point-of-sale.scss @@ -860,6 +860,8 @@ .invoice-fields { overflow-y: scroll; + height: 100%; + padding-right: var(--padding-sm); } } From ce129a141447f701240265e50c1de88b1ef46e12 Mon Sep 17 00:00:00 2001 From: Saqib Date: Tue, 24 Aug 2021 17:23:14 +0530 Subject: [PATCH 24/27] feat: re-arrange company doctype fields (#27091) --- erpnext/regional/india/setup.py | 2 + erpnext/setup/doctype/company/company.js | 78 +++++++------- erpnext/setup/doctype/company/company.json | 113 ++++++++++----------- 3 files changed, 99 insertions(+), 94 deletions(-) diff --git a/erpnext/regional/india/setup.py b/erpnext/regional/india/setup.py index a6ab6aba77..4db5551cb3 100644 --- a/erpnext/regional/india/setup.py +++ b/erpnext/regional/india/setup.py @@ -531,6 +531,7 @@ def make_custom_fields(update=True): fieldtype='Link', options='Salary Component', insert_after='hra_section'), dict(fieldname='hra_component', label='HRA Component', fieldtype='Link', options='Salary Component', insert_after='basic_component'), + dict(fieldname='hra_column_break', fieldtype='Column Break', insert_after='hra_component'), dict(fieldname='arrear_component', label='Arrear Component', fieldtype='Link', options='Salary Component', insert_after='hra_component'), dict(fieldname='non_profit_section', label='Non Profit Settings', @@ -539,6 +540,7 @@ def make_custom_fields(update=True): fieldtype='Data', insert_after='non_profit_section'), dict(fieldname='with_effect_from', label='80G With Effect From', fieldtype='Date', insert_after='company_80g_number'), + dict(fieldname='non_profit_column_break', fieldtype='Column Break', insert_after='with_effect_from'), dict(fieldname='pan_details', label='PAN Number', fieldtype='Data', insert_after='with_effect_from') ], diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index 8f83d3cd73..56700af79e 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -46,6 +46,43 @@ frappe.ui.form.on("Company", { }); }, + change_abbreviation(frm) { + var dialog = new frappe.ui.Dialog({ + title: "Replace Abbr", + fields: [ + {"fieldtype": "Data", "label": "New Abbreviation", "fieldname": "new_abbr", + "reqd": 1 }, + {"fieldtype": "Button", "label": "Update", "fieldname": "update"}, + ] + }); + + dialog.fields_dict.update.$input.click(function() { + var args = dialog.get_values(); + if (!args) return; + frappe.show_alert(__("Update in progress. It might take a while.")); + return frappe.call({ + method: "erpnext.setup.doctype.company.company.enqueue_replace_abbr", + args: { + "company": frm.doc.name, + "old": frm.doc.abbr, + "new": args.new_abbr + }, + callback: function(r) { + if (r.exc) { + frappe.msgprint(__("There were errors.")); + return; + } else { + frm.set_value("abbr", args.new_abbr); + } + dialog.hide(); + frm.refresh(); + }, + btn: this + }); + }); + dialog.show(); + }, + company_name: function(frm) { if(frm.doc.__islocal) { // add missing " " arg in split method @@ -127,6 +164,10 @@ frappe.ui.form.on("Company", { }, __('Manage')); } } + + frm.add_custom_button(__('Change Abbreviation'), () => { + frm.trigger('change_abbreviation'); + }, __('Manage')); } erpnext.company.set_chart_of_accounts_options(frm.doc); @@ -204,43 +245,6 @@ erpnext.company.set_chart_of_accounts_options = function(doc) { } } -cur_frm.cscript.change_abbr = function() { - var dialog = new frappe.ui.Dialog({ - title: "Replace Abbr", - fields: [ - {"fieldtype": "Data", "label": "New Abbreviation", "fieldname": "new_abbr", - "reqd": 1 }, - {"fieldtype": "Button", "label": "Update", "fieldname": "update"}, - ] - }); - - dialog.fields_dict.update.$input.click(function() { - var args = dialog.get_values(); - if(!args) return; - frappe.show_alert(__("Update in progress. It might take a while.")); - return frappe.call({ - method: "erpnext.setup.doctype.company.company.enqueue_replace_abbr", - args: { - "company": cur_frm.doc.name, - "old": cur_frm.doc.abbr, - "new": args.new_abbr - }, - callback: function(r) { - if(r.exc) { - frappe.msgprint(__("There were errors.")); - return; - } else { - cur_frm.set_value("abbr", args.new_abbr); - } - dialog.hide(); - cur_frm.refresh(); - }, - btn: this - }) - }); - dialog.show(); -} - erpnext.company.setup_queries = function(frm) { $.each([ ["default_bank_account", {"account_type": "Bank"}], diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index e6ec496a65..e4ee3ecea7 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -12,33 +12,48 @@ "details", "company_name", "abbr", - "change_abbr", + "default_currency", + "country", "is_group", "cb0", - "domain", - "parent_company", - "charts_section", - "default_currency", "default_letter_head", - "default_holiday_list", - "default_finance_book", - "default_selling_terms", - "default_buying_terms", - "default_warehouse_for_sales_return", - "default_in_transit_warehouse", - "column_break_10", - "country", - "create_chart_of_accounts_based_on", - "chart_of_accounts", - "existing_company", "tax_id", + "domain", "date_of_establishment", + "parent_company", + "company_info", + "company_logo", + "date_of_incorporation", + "phone_no", + "email", + "company_description", + "column_break1", + "date_of_commencement", + "fax", + "website", + "address_html", + "section_break_28", + "create_chart_of_accounts_based_on", + "existing_company", + "column_break_26", + "chart_of_accounts", + "charts_section", "sales_settings", - "monthly_sales_target", + "default_buying_terms", "sales_monthly_history", - "column_break_goals", - "transactions_annual_history", + "monthly_sales_target", "total_monthly_sales", + "column_break_goals", + "default_selling_terms", + "default_warehouse_for_sales_return", + "credit_limit", + "transactions_annual_history", + "hr_settings_section", + "default_holiday_list", + "default_expense_claim_payable_account", + "column_break_10", + "default_employee_advance_account", + "default_payroll_payable_account", "default_settings", "default_bank_account", "default_cash_account", @@ -52,24 +67,20 @@ "column_break0", "allow_account_creation_against_child_company", "default_payable_account", - "default_employee_advance_account", "default_expense_account", "default_income_account", "default_deferred_revenue_account", "default_deferred_expense_account", - "default_payroll_payable_account", - "default_expense_claim_payable_account", "default_discount_account", - "section_break_22", - "cost_center", - "column_break_26", - "credit_limit", "payment_terms", + "cost_center", + "default_finance_book", "auto_accounting_for_stock_settings", "enable_perpetual_inventory", "enable_perpetual_inventory_for_non_stock_items", "default_inventory_account", "stock_adjustment_account", + "default_in_transit_warehouse", "column_break_32", "stock_received_but_not_billed", "service_received_but_not_billed", @@ -79,25 +90,14 @@ "depreciation_expense_account", "series_for_depreciation_entry", "expenses_included_in_asset_valuation", + "repair_and_maintenance_account", "column_break_40", "disposal_account", "depreciation_cost_center", "capital_work_in_progress_account", - "repair_and_maintenance_account", "asset_received_but_not_billed", "budget_detail", "exception_budget_approver_role", - "company_info", - "company_logo", - "date_of_incorporation", - "address_html", - "date_of_commencement", - "phone_no", - "fax", - "email", - "website", - "column_break1", - "company_description", "registration_info", "registration_details", "lft", @@ -127,12 +127,6 @@ "oldfieldtype": "Data", "reqd": 1 }, - { - "depends_on": "eval:!doc.__islocal && in_list(frappe.user_roles, \"System Manager\")", - "fieldname": "change_abbr", - "fieldtype": "Button", - "label": "Change Abbreviation" - }, { "bold": 1, "default": "0", @@ -176,10 +170,9 @@ "label": "Company Description" }, { - "collapsible": 1, "fieldname": "sales_settings", "fieldtype": "Section Break", - "label": "Sales Settings" + "label": "Buying & Selling Settings" }, { "fieldname": "sales_monthly_history", @@ -442,10 +435,6 @@ "no_copy": 1, "options": "Account" }, - { - "fieldname": "section_break_22", - "fieldtype": "Section Break" - }, { "depends_on": "eval:!doc.__islocal", "fieldname": "cost_center", @@ -455,10 +444,6 @@ "no_copy": 1, "options": "Cost Center" }, - { - "fieldname": "column_break_26", - "fieldtype": "Column Break" - }, { "depends_on": "eval:!doc.__islocal", "fieldname": "credit_limit", @@ -589,10 +574,10 @@ }, { "collapsible": 1, - "description": "For reference only.", + "depends_on": "eval: doc.docstatus == 0 && doc.__islocal != 1", "fieldname": "company_info", "fieldtype": "Section Break", - "label": "Company Info" + "label": "Address & Contact" }, { "fieldname": "date_of_incorporation", @@ -741,6 +726,20 @@ "fieldtype": "Link", "label": "Repair and Maintenance Account", "options": "Account" + }, + { + "fieldname": "section_break_28", + "fieldtype": "Section Break", + "label": "Chart of Accounts" + }, + { + "fieldname": "hr_settings_section", + "fieldtype": "Section Break", + "label": "HR & Payroll Settings" + }, + { + "fieldname": "column_break_26", + "fieldtype": "Column Break" } ], "icon": "fa fa-building", @@ -748,7 +747,7 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2021-05-12 16:51:08.187233", + "modified": "2021-07-12 11:27:06.353860", "modified_by": "Administrator", "module": "Setup", "name": "Company", From 2e5525aba9073010374e193ed2c6ac5e66d6ddc9 Mon Sep 17 00:00:00 2001 From: Subin Tom <36098155+nemesis189@users.noreply.github.com> Date: Tue, 24 Aug 2021 17:24:41 +0530 Subject: [PATCH 25/27] fix: Correct price list rate value in return si (#27097) --- erpnext/controllers/sales_and_purchase_return.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index 80ccc6d75b..5ee1f2f7fb 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -329,7 +329,6 @@ def make_return_doc(doctype, source_name, target_doc=None): target_doc.po_detail = source_doc.po_detail target_doc.pr_detail = source_doc.pr_detail target_doc.purchase_invoice_item = source_doc.name - target_doc.price_list_rate = 0 elif doctype == "Delivery Note": returned_qty_map = get_returned_qty_map_for_row(source_doc.name, doctype) @@ -360,7 +359,6 @@ def make_return_doc(doctype, source_name, target_doc=None): else: target_doc.pos_invoice_item = source_doc.name - target_doc.price_list_rate = 0 if default_warehouse_for_sales_return: target_doc.warehouse = default_warehouse_for_sales_return From 9198caa5e71ce06d8e4e4af4f006ef9520e50944 Mon Sep 17 00:00:00 2001 From: Saqib Date: Tue, 24 Aug 2021 17:26:23 +0530 Subject: [PATCH 26/27] fix: incorrect gl entry on period closing involving finance books (#26921) --- .../doctype/finance_book/test_finance_book.py | 24 ++++----- .../period_closing_voucher.py | 46 ++++++---------- .../test_period_closing_voucher.py | 53 +++++++++++++++++++ erpnext/accounts/general_ledger.py | 2 +- 4 files changed, 81 insertions(+), 44 deletions(-) diff --git a/erpnext/accounts/doctype/finance_book/test_finance_book.py b/erpnext/accounts/doctype/finance_book/test_finance_book.py index cd8e204f4c..2ba21397ad 100644 --- a/erpnext/accounts/doctype/finance_book/test_finance_book.py +++ b/erpnext/accounts/doctype/finance_book/test_finance_book.py @@ -9,19 +9,8 @@ import frappe import unittest class TestFinanceBook(unittest.TestCase): - def create_finance_book(self): - if not frappe.db.exists("Finance Book", "_Test Finance Book"): - finance_book = frappe.get_doc({ - "doctype": "Finance Book", - "finance_book_name": "_Test Finance Book" - }).insert() - else: - finance_book = frappe.get_doc("Finance Book", "_Test Finance Book") - - return finance_book - def test_finance_book(self): - finance_book = self.create_finance_book() + finance_book = create_finance_book() # create jv entry jv = make_journal_entry("_Test Bank - _TC", @@ -41,3 +30,14 @@ class TestFinanceBook(unittest.TestCase): for gl_entry in gl_entries: self.assertEqual(gl_entry.finance_book, finance_book.name) + +def create_finance_book(): + if not frappe.db.exists("Finance Book", "_Test Finance Book"): + finance_book = frappe.get_doc({ + "doctype": "Finance Book", + "finance_book_name": "_Test Finance Book" + }).insert() + else: + finance_book = frappe.get_doc("Finance Book", "_Test Finance Book") + + return finance_book \ No newline at end of file diff --git a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py index a6e3bd98e7..289278ea8d 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/period_closing_voucher.py @@ -50,9 +50,13 @@ class PeriodClosingVoucher(AccountsController): .format(pce[0][0], self.posting_date)) def make_gl_entries(self): + gl_entries = self.get_gl_entries() + if gl_entries: + from erpnext.accounts.general_ledger import make_gl_entries + make_gl_entries(gl_entries) + + def get_gl_entries(self): gl_entries = [] - net_pl_balance = 0 - pl_accounts = self.get_pl_balances() for acc in pl_accounts: @@ -60,6 +64,7 @@ class PeriodClosingVoucher(AccountsController): gl_entries.append(self.get_gl_dict({ "account": acc.account, "cost_center": acc.cost_center, + "finance_book": acc.finance_book, "account_currency": acc.account_currency, "debit_in_account_currency": abs(flt(acc.bal_in_account_currency)) if flt(acc.bal_in_account_currency) < 0 else 0, "debit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) < 0 else 0, @@ -67,35 +72,13 @@ class PeriodClosingVoucher(AccountsController): "credit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) > 0 else 0 }, item=acc)) - net_pl_balance += flt(acc.bal_in_company_currency) + if gl_entries: + gle_for_net_pl_bal = self.get_pnl_gl_entry(pl_accounts) + gl_entries += gle_for_net_pl_bal - if net_pl_balance: - if self.cost_center_wise_pnl: - costcenter_wise_gl_entries = self.get_costcenter_wise_pnl_gl_entries(pl_accounts) - gl_entries += costcenter_wise_gl_entries - else: - gl_entry = self.get_pnl_gl_entry(net_pl_balance) - gl_entries.append(gl_entry) - - from erpnext.accounts.general_ledger import make_gl_entries - make_gl_entries(gl_entries) - - def get_pnl_gl_entry(self, net_pl_balance): - cost_center = frappe.db.get_value("Company", self.company, "cost_center") - gl_entry = self.get_gl_dict({ - "account": self.closing_account_head, - "debit_in_account_currency": abs(net_pl_balance) if net_pl_balance > 0 else 0, - "debit": abs(net_pl_balance) if net_pl_balance > 0 else 0, - "credit_in_account_currency": abs(net_pl_balance) if net_pl_balance < 0 else 0, - "credit": abs(net_pl_balance) if net_pl_balance < 0 else 0, - "cost_center": cost_center - }) - - self.update_default_dimensions(gl_entry) - - return gl_entry - - def get_costcenter_wise_pnl_gl_entries(self, pl_accounts): + return gl_entries + + def get_pnl_gl_entry(self, pl_accounts): company_cost_center = frappe.db.get_value("Company", self.company, "cost_center") gl_entries = [] @@ -104,6 +87,7 @@ class PeriodClosingVoucher(AccountsController): gl_entry = self.get_gl_dict({ "account": self.closing_account_head, "cost_center": acc.cost_center or company_cost_center, + "finance_book": acc.finance_book, "account_currency": acc.account_currency, "debit_in_account_currency": abs(flt(acc.bal_in_account_currency)) if flt(acc.bal_in_account_currency) > 0 else 0, "debit": abs(flt(acc.bal_in_company_currency)) if flt(acc.bal_in_company_currency) > 0 else 0, @@ -130,7 +114,7 @@ class PeriodClosingVoucher(AccountsController): def get_pl_balances(self): """Get balance for dimension-wise pl accounts""" - dimension_fields = ['t1.cost_center'] + dimension_fields = ['t1.cost_center', 't1.finance_book'] self.accounting_dimensions = get_accounting_dimensions() for dimension in self.accounting_dimensions: diff --git a/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py b/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py index f17a5c51a0..2d1939131c 100644 --- a/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py +++ b/erpnext/accounts/doctype/period_closing_voucher/test_period_closing_voucher.py @@ -8,6 +8,7 @@ import frappe from frappe.utils import flt, today from erpnext.accounts.utils import get_fiscal_year, now from erpnext.accounts.doctype.journal_entry.test_journal_entry import make_journal_entry +from erpnext.accounts.doctype.finance_book.test_finance_book import create_finance_book from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice class TestPeriodClosingVoucher(unittest.TestCase): @@ -118,6 +119,58 @@ class TestPeriodClosingVoucher(unittest.TestCase): self.assertTrue(pcv_gle, expected_gle) + def test_period_closing_with_finance_book_entries(self): + frappe.db.sql("delete from `tabGL Entry` where company='Test PCV Company'") + + company = create_company() + surplus_account = create_account() + cost_center = create_cost_center("Test Cost Center 1") + + create_sales_invoice( + company=company, + income_account="Sales - TPC", + expense_account="Cost of Goods Sold - TPC", + cost_center=cost_center, + rate=400, + debit_to="Debtors - TPC" + ) + jv = make_journal_entry( + account1="Cash - TPC", + account2="Sales - TPC", + amount=400, + cost_center=cost_center, + posting_date=now() + ) + jv.company = company + jv.finance_book = create_finance_book().name + jv.save() + jv.submit() + + pcv = frappe.get_doc({ + "transaction_date": today(), + "posting_date": today(), + "fiscal_year": get_fiscal_year(today())[0], + "company": company, + "closing_account_head": surplus_account, + "remarks": "Test", + "doctype": "Period Closing Voucher" + }) + pcv.insert() + pcv.submit() + + expected_gle = ( + (surplus_account, 0.0, 400.0, ''), + (surplus_account, 0.0, 400.0, jv.finance_book), + ('Sales - TPC', 400.0, 0.0, ''), + ('Sales - TPC', 400.0, 0.0, jv.finance_book) + ) + + pcv_gle = frappe.db.sql(""" + select account, debit, credit, finance_book from `tabGL Entry` where voucher_no=%s + """, (pcv.name)) + + self.assertTrue(pcv_gle, expected_gle) + def make_period_closing_voucher(self): pcv = frappe.get_doc({ "doctype": "Period Closing Voucher", diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py index 4c7c567b42..3126138408 100644 --- a/erpnext/accounts/general_ledger.py +++ b/erpnext/accounts/general_ledger.py @@ -101,7 +101,7 @@ def merge_similar_entries(gl_map, precision=None): def check_if_in_list(gle, gl_map, dimensions=None): account_head_fieldnames = ['voucher_detail_no', 'party', 'against_voucher', - 'cost_center', 'against_voucher_type', 'party_type', 'project'] + 'cost_center', 'against_voucher_type', 'party_type', 'project', 'finance_book'] if dimensions: account_head_fieldnames = account_head_fieldnames + dimensions From f47cbae5e07ba7f0e48e5fe11b807632f206bd45 Mon Sep 17 00:00:00 2001 From: Saqib Date: Tue, 24 Aug 2021 17:27:27 +0530 Subject: [PATCH 27/27] feat: allow draft pos invoices even if no stock available (#27078) --- erpnext/accounts/doctype/pos_invoice/pos_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py index 759cad53d4..034a217a26 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py @@ -138,7 +138,7 @@ class POSInvoice(SalesInvoice): .format(item.idx, bold_delivered_serial_nos), title=_("Item Unavailable")) def validate_stock_availablility(self): - if self.is_return: + if self.is_return or self.docstatus != 1: return allow_negative_stock = frappe.db.get_single_value('Stock Settings', 'allow_negative_stock')