From b3486b43c45df03697b13a4f0fc655d70bc52407 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Wed, 27 Sep 2023 18:51:09 +0200 Subject: [PATCH 001/130] fix: german translations of Accounts Settings --- erpnext/translations/de.csv | 39 +++++++++++++++++++++++++++++++------ 1 file changed, 33 insertions(+), 6 deletions(-) diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index 03e9de4c55..b9e010add9 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -3345,7 +3345,7 @@ Cannot Calculate Arrival Time as Driver Address is Missing.,"Die Ankunftszeit ka Cannot Optimize Route as Driver Address is Missing.,"Route kann nicht optimiert werden, da die Fahreradresse fehlt.", Cannot complete task {0} as its dependant task {1} are not ccompleted / cancelled.,"Aufgabe {0} kann nicht abgeschlossen werden, da die abhängige Aufgabe {1} nicht abgeschlossen / abgebrochen wurde.", Cannot find a matching Item. Please select some other value for {0}.,Ein passender Artikel kann nicht gefunden werden. Bitte einen anderen Wert für {0} auswählen., -"Cannot overbill for Item {0} in row {1} more than {2}. To allow over-billing, please set allowance in Accounts Settings","Artikel {0} in Zeile {1} kann nicht mehr als {2} in Rechnung gestellt werden. Um eine Überberechnung zuzulassen, legen Sie die Überberechnung in den Kontoeinstellungen fest", +"Cannot overbill for Item {0} in row {1} more than {2}. To allow over-billing, please set allowance in Accounts Settings","Artikel {0} in Zeile {1} kann nicht mehr als {2} in Rechnung gestellt werden. Um eine Überberechnung zuzulassen, legen Sie die Überberechnung in den Buchhaltungseinstellungen fest", "Capacity Planning Error, planned start time can not be same as end time","Kapazitätsplanungsfehler, die geplante Startzeit darf nicht mit der Endzeit übereinstimmen", Categories,Kategorien, Changes in {0},Änderungen in {0}, @@ -3751,7 +3751,7 @@ This page keeps track of items you want to buy from sellers.,"Auf dieser Seite w This page keeps track of your items in which buyers have showed some interest.,"Diese Seite verfolgt Ihre Artikel, an denen Käufer Interesse gezeigt haben.", Thursday,Donnerstag, Title,Bezeichnung, -"To allow over billing, update ""Over Billing Allowance"" in Accounts Settings or the Item.","Aktualisieren Sie "Over Billing Allowance" in den Kontoeinstellungen oder im Artikel, um eine Überberechnung zuzulassen.", +"To allow over billing, update ""Over Billing Allowance"" in Accounts Settings or the Item.","Aktualisieren Sie "Over Billing Allowance" in den Buchhaltungseinstellungen oder im Artikel, um eine Überberechnung zuzulassen.", "To allow over receipt / delivery, update ""Over Receipt/Delivery Allowance"" in Stock Settings or the Item.","Um eine Überbestätigung / Überlieferung zu ermöglichen, aktualisieren Sie "Überbestätigung / Überlieferung" in den Lagereinstellungen oder im Artikel.", Total,Summe, Total Payment Request amount cannot be greater than {0} amount,Der Gesamtbetrag der Zahlungsanforderung darf nicht größer als {0} sein, @@ -4118,8 +4118,8 @@ Mandatory For Profit and Loss Account,Obligatorisch für Gewinn- und Verlustrech Accounting Period,Abrechnungszeitraum, Period Name,Zeitraumname, Closed Documents,Geschlossene Dokumente, -Accounts Settings,Konteneinstellungen, -Settings for Accounts,Konteneinstellungen, +Accounts Settings,Buchhaltungseinstellungen, +Settings for Accounts,Einstellungen für das Buchhaltungsmodul, Make Accounting Entry For Every Stock Movement,Eine Buchung für jede Lagerbewegung erstellen, Users with this role are allowed to set frozen accounts and create / modify accounting entries against frozen accounts,Benutzer mit dieser Rolle sind berechtigt Konten zu sperren und Buchungen zu gesperrten Konten zu erstellen/verändern, Determine Address Tax Category From,Adresssteuerkategorie bestimmen von, @@ -7622,7 +7622,7 @@ Template Title,Vorlagentitel, Journal Entry Type,Buchungssatz-Typ, Journal Entry Template Account,Buchungssatzvorlagenkonto, Process Deferred Accounting,Aufgeschobene Buchhaltung verarbeiten, -Manual entry cannot be created! Disable automatic entry for deferred accounting in accounts settings and try again,Manuelle Eingabe kann nicht erstellt werden! Deaktivieren Sie die automatische Eingabe für die verzögerte Buchhaltung in den Konteneinstellungen und versuchen Sie es erneut, +Manual entry cannot be created! Disable automatic entry for deferred accounting in accounts settings and try again,Manuelle Eingabe kann nicht erstellt werden! Deaktivieren Sie die automatische Eingabe für die verzögerte Buchhaltung in den Buchhaltungseinstellungen und versuchen Sie es erneut, End date cannot be before start date,Das Enddatum darf nicht vor dem Startdatum liegen, Total Counts Targeted,Gesamtzahl der anvisierten Zählungen, Total Counts Completed,Gesamtzahl der abgeschlossenen Zählungen, @@ -7884,7 +7884,7 @@ Checking this will automatically create a Sales Invoice whenever an appointment Healthcare Service Items,Artikel im Gesundheitswesen, "You can create a service item for Inpatient Visit Charge and set it here. Similarly, you can set up other Healthcare Service Items for billing in this section. Click ",Sie können ein Serviceelement für die Gebühr für stationäre Besuche erstellen und hier festlegen. Ebenso können Sie in diesem Abschnitt andere Gesundheitsposten für die Abrechnung einrichten. Klicken, Set up default Accounts for the Healthcare Facility,Richten Sie Standardkonten für die Gesundheitseinrichtung ein, -"If you wish to override default accounts settings and configure the Income and Receivable accounts for Healthcare, you can do so here.","Wenn Sie die Standardkonteneinstellungen überschreiben und die Einkommens- und Debitorenkonten für das Gesundheitswesen konfigurieren möchten, können Sie dies hier tun.", +"If you wish to override default accounts settings and configure the Income and Receivable accounts for Healthcare, you can do so here.","Wenn Sie die Buchhaltungseinstellungen überschreiben und spezielle Einkommens- und Debitorenkonten für das Gesundheitswesen konfigurieren möchten, können Sie dies hier tun.", Out Patient SMS alerts,Out Patient SMS-Benachrichtigungen, "If you want to send SMS alert on Patient Registration, you can enable this option. Similary, you can set up Out Patient SMS alerts for other functionalities in this section. Click ","Wenn Sie bei der Patientenregistrierung eine SMS-Benachrichtigung senden möchten, können Sie diese Option aktivieren. Ebenso können Sie in diesem Abschnitt SMS-Benachrichtigungen für andere Patienten einrichten. Klicken", Admission Order Details,Details zur Zulassungsbestellung, @@ -8833,3 +8833,30 @@ Global Defaults,Allgemeine Voreinstellungen, Is Mandatory,Ist obligatorisch, WhatsApp,WhatsApp, Make a call,Einen Anruf tätigen, +Enable Automatic Party Matching,Automatisches Zuordnen von Parteien aktivieren, +Auto match and set the Party in Bank Transactions,"Partei automatisch anhand der Kontonummer bzw. IBAN zuordnen", +Enable Fuzzy Matching,Unscharfe Zuordnung aktivieren, +Approximately match the description/party name against parties,"Partei automatisch anhand grober Übereinstimmung des Names zuordnen" +Accounts Closing,Kontoabschluss, +Period Closing Settings,Periodenabschlusseinstellungen, +Ignore Account Closing Balance,Saldo des Kontos zum Periodenabschluss ignorieren, +Financial reports will be generated using GL Entry doctypes (should be enabled if Period Closing Voucher is not posted for all years sequentially or missing),"Finanzberichte werden anhand des Hauptbuchs erstellt (sollte aktiviert sein, wenn Periodenabschlüsse fehlen oder nicht sequentiell für alle Jahre gebucht werden)", +Asset Settings,Vermögenswerteinstellungen, +POS Setting,POS-Einstellungen, +Create Ledger Entries for Change Amount,Buchungssätze für Wechselgeld erstellen, +"If enabled, ledger entries will be posted for change amount in POS transactions","Wenn aktiviert, werden Buchungssätze für Wechselgeld in POS-Transaktionen erstellt", +Credit Limit Settings,Kreditlimit-Einstellungen, +Role Allowed to Over Bill, +Users with this role are allowed to over bill above the allowance percentage,"Roll, die mehr als den erlaubten Prozentsatz zusätzlich abrechnen darf", +Role allowed to bypass Credit Limit,"Rolle, die das Kreditlimit umgehen darf", +Invoice Cancellation,Rechnungsstornierung, +Delete Accounting and Stock Ledger Entries on deletion of Transaction,Beim Löschen einer Transaktion auch die entsprechenden Buchungs- und Lagerbuchungssätze löschen, +Enable Common Party Accounting,Verknüpfung von Kunden und Liefeanten erlauben, +Allow multi-currency invoices against single party account,Rechnungsbeträge in Fremdwährungen dürfen umgerechnet und in der Hauptwährung gebucht werden, +Enabling this will allow creation of multi-currency invoices against single party account in company currency,Bei Aktivierung können Rechnungen in Fremdwährungen gegen ein Konto in der Hauptwährung gebucht werden, +Payment Terms from orders will be fetched into the invoices as is,Die Zahlungsbedingungen aus einem Auftragen werden eins zu eins in die Rechnungen übernommen, +Automatically Fetch Payment Terms from Order,Zahlungsbedingungen aus Auftrag in die Rechnung übernehmen, +Enable Custom Cash Flow Format,Individuelles Cashflow-Format aktivieren, +Tax Settings,Umsatzsteuer-Einstellungen, +Book Tax Loss on Early Payment Discount,Umsatzsteueranteil bei Skonto berücksichtigen, +Split Early Payment Discount Loss into Income and Tax Loss,"Skontobetrag in Aufwand und Umsatzsteuerkorrektur aufteilen", From ff1dc72d740f32d75deb7fa6294e70b488f146b2 Mon Sep 17 00:00:00 2001 From: marination Date: Fri, 29 Sep 2023 15:19:09 +0530 Subject: [PATCH 002/130] fix: Set right party name in bank transaction - If party name and docname are different, set the docname in Bank Transaction --- .../doctype/bank_transaction/auto_match_party.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/bank_transaction/auto_match_party.py b/erpnext/accounts/doctype/bank_transaction/auto_match_party.py index 5d94a08f2f..671cde58a9 100644 --- a/erpnext/accounts/doctype/bank_transaction/auto_match_party.py +++ b/erpnext/accounts/doctype/bank_transaction/auto_match_party.py @@ -1,6 +1,7 @@ from typing import Tuple, Union import frappe +from frappe.core.utils import find from frappe.utils import flt from rapidfuzz import fuzz, process @@ -112,7 +113,8 @@ class AutoMatchbyPartyNameDescription: for party in parties: filters = {"status": "Active"} if party == "Employee" else {"disabled": 0} - names = frappe.get_all(party, filters=filters, pluck=party.lower() + "_name") + field = party.lower() + "_name" + names = frappe.get_all(party, filters=filters, fields=[f"{field} as party_name", "name"]) for field in ["bank_party_name", "description"]: if not self.get(field): @@ -131,12 +133,18 @@ class AutoMatchbyPartyNameDescription: def fuzzy_search_and_return_result(self, party, names, field) -> Union[Tuple, None]: skip = False - result = process.extract(query=self.get(field), choices=names, scorer=fuzz.token_set_ratio) + result = process.extract( + query=self.get(field), + choices=[name.get("party_name") for name in names], + scorer=fuzz.token_set_ratio, + ) party_name, skip = self.process_fuzzy_result(result) if not party_name: return None, skip + # Get Party Docname from the list of dicts + party_name = find(names, lambda x: x["party_name"] == party_name).get("name") return ( party, party_name, From 115f0242600a519209a7acd0e59fba434fd8c1c5 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Tue, 3 Oct 2023 23:06:48 +0200 Subject: [PATCH 003/130] fix(translations): suggestions from review --- erpnext/translations/de.csv | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index b9e010add9..577ba87ea4 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -3345,7 +3345,7 @@ Cannot Calculate Arrival Time as Driver Address is Missing.,"Die Ankunftszeit ka Cannot Optimize Route as Driver Address is Missing.,"Route kann nicht optimiert werden, da die Fahreradresse fehlt.", Cannot complete task {0} as its dependant task {1} are not ccompleted / cancelled.,"Aufgabe {0} kann nicht abgeschlossen werden, da die abhängige Aufgabe {1} nicht abgeschlossen / abgebrochen wurde.", Cannot find a matching Item. Please select some other value for {0}.,Ein passender Artikel kann nicht gefunden werden. Bitte einen anderen Wert für {0} auswählen., -"Cannot overbill for Item {0} in row {1} more than {2}. To allow over-billing, please set allowance in Accounts Settings","Artikel {0} in Zeile {1} kann nicht mehr als {2} in Rechnung gestellt werden. Um eine Überberechnung zuzulassen, legen Sie die Überberechnung in den Buchhaltungseinstellungen fest", +"Cannot overbill for Item {0} in row {1} more than {2}. To allow over-billing, please set allowance in Accounts Settings","Für Artikel {0} in Zeile {1} kann nicht mehr als {2} zusätzlich in Rechnung gestellt werden. Um diese Überfakturierung zuzulassen, passen Sie bitte die Grenzwerte in den Buchhaltungseinstellungen an.", "Capacity Planning Error, planned start time can not be same as end time","Kapazitätsplanungsfehler, die geplante Startzeit darf nicht mit der Endzeit übereinstimmen", Categories,Kategorien, Changes in {0},Änderungen in {0}, @@ -4119,7 +4119,7 @@ Accounting Period,Abrechnungszeitraum, Period Name,Zeitraumname, Closed Documents,Geschlossene Dokumente, Accounts Settings,Buchhaltungseinstellungen, -Settings for Accounts,Einstellungen für das Buchhaltungsmodul, +Settings for Accounts,Einstellungen für die Buchhaltung, Make Accounting Entry For Every Stock Movement,Eine Buchung für jede Lagerbewegung erstellen, Users with this role are allowed to set frozen accounts and create / modify accounting entries against frozen accounts,Benutzer mit dieser Rolle sind berechtigt Konten zu sperren und Buchungen zu gesperrten Konten zu erstellen/verändern, Determine Address Tax Category From,Adresssteuerkategorie bestimmen von, @@ -7884,7 +7884,7 @@ Checking this will automatically create a Sales Invoice whenever an appointment Healthcare Service Items,Artikel im Gesundheitswesen, "You can create a service item for Inpatient Visit Charge and set it here. Similarly, you can set up other Healthcare Service Items for billing in this section. Click ",Sie können ein Serviceelement für die Gebühr für stationäre Besuche erstellen und hier festlegen. Ebenso können Sie in diesem Abschnitt andere Gesundheitsposten für die Abrechnung einrichten. Klicken, Set up default Accounts for the Healthcare Facility,Richten Sie Standardkonten für die Gesundheitseinrichtung ein, -"If you wish to override default accounts settings and configure the Income and Receivable accounts for Healthcare, you can do so here.","Wenn Sie die Buchhaltungseinstellungen überschreiben und spezielle Einkommens- und Debitorenkonten für das Gesundheitswesen konfigurieren möchten, können Sie dies hier tun.", +"If you wish to override default accounts settings and configure the Income and Receivable accounts for Healthcare, you can do so here.","Wenn Sie die Standardkonteneinstellungen überschreiben und die Einkommens- und Debitorenkonten für das Gesundheitswesen konfigurieren möchten, können Sie dies hier tun.", Out Patient SMS alerts,Out Patient SMS-Benachrichtigungen, "If you want to send SMS alert on Patient Registration, you can enable this option. Similary, you can set up Out Patient SMS alerts for other functionalities in this section. Click ","Wenn Sie bei der Patientenregistrierung eine SMS-Benachrichtigung senden möchten, können Sie diese Option aktivieren. Ebenso können Sie in diesem Abschnitt SMS-Benachrichtigungen für andere Patienten einrichten. Klicken", Admission Order Details,Details zur Zulassungsbestellung, @@ -8835,9 +8835,9 @@ WhatsApp,WhatsApp, Make a call,Einen Anruf tätigen, Enable Automatic Party Matching,Automatisches Zuordnen von Parteien aktivieren, Auto match and set the Party in Bank Transactions,"Partei automatisch anhand der Kontonummer bzw. IBAN zuordnen", -Enable Fuzzy Matching,Unscharfe Zuordnung aktivieren, -Approximately match the description/party name against parties,"Partei automatisch anhand grober Übereinstimmung des Names zuordnen" -Accounts Closing,Kontoabschluss, +Enable Fuzzy Matching,Fuzzy Matching aktivieren, +Approximately match the description/party name against parties,"Partei automatisch anhand grober Übereinstimmung des Namens zuordnen" +Accounts Closing,Kontenabschluss, Period Closing Settings,Periodenabschlusseinstellungen, Ignore Account Closing Balance,Saldo des Kontos zum Periodenabschluss ignorieren, Financial reports will be generated using GL Entry doctypes (should be enabled if Period Closing Voucher is not posted for all years sequentially or missing),"Finanzberichte werden anhand des Hauptbuchs erstellt (sollte aktiviert sein, wenn Periodenabschlüsse fehlen oder nicht sequentiell für alle Jahre gebucht werden)", @@ -8854,7 +8854,7 @@ Delete Accounting and Stock Ledger Entries on deletion of Transaction,Beim Lösc Enable Common Party Accounting,Verknüpfung von Kunden und Liefeanten erlauben, Allow multi-currency invoices against single party account,Rechnungsbeträge in Fremdwährungen dürfen umgerechnet und in der Hauptwährung gebucht werden, Enabling this will allow creation of multi-currency invoices against single party account in company currency,Bei Aktivierung können Rechnungen in Fremdwährungen gegen ein Konto in der Hauptwährung gebucht werden, -Payment Terms from orders will be fetched into the invoices as is,Die Zahlungsbedingungen aus einem Auftragen werden eins zu eins in die Rechnungen übernommen, +Payment Terms from orders will be fetched into the invoices as is,Zahlungsbedingungen aus Aufträgen werden eins zu eins in Rechnungen übernommen, Automatically Fetch Payment Terms from Order,Zahlungsbedingungen aus Auftrag in die Rechnung übernehmen, Enable Custom Cash Flow Format,Individuelles Cashflow-Format aktivieren, Tax Settings,Umsatzsteuer-Einstellungen, From 705dadae8e3b49e751184b14323ba2686e5342cc Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Thu, 19 Oct 2023 16:21:15 +0530 Subject: [PATCH 004/130] refactor: avoid relying only on against in tds docs query --- .../tax_withholding_details.py | 66 ++++++++++++------- 1 file changed, 44 insertions(+), 22 deletions(-) diff --git a/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py b/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py index f2ec31c70e..e5aa6f52ee 100644 --- a/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py +++ b/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py @@ -70,7 +70,8 @@ def get_result( if net_total_map.get(name): if voucher_type == "Journal Entry": # back calcalute total amount from rate and tax_amount - total_amount = grand_total = base_total = tax_amount / (rate / 100) + if rate: + total_amount = grand_total = base_total = tax_amount / (rate / 100) else: total_amount, grand_total, base_total = net_total_map.get(name) else: @@ -253,27 +254,7 @@ def get_tds_docs(filters): "Tax Withholding Account", {"company": filters.get("company")}, pluck="account" ) - query_filters = { - "account": ("in", tds_accounts), - "posting_date": ("between", [filters.get("from_date"), filters.get("to_date")]), - "is_cancelled": 0, - "against": ("not in", bank_accounts), - } - - party = frappe.get_all(filters.get("party_type"), pluck="name") - or_filters.update({"against": ("in", party), "voucher_type": "Journal Entry"}) - - if filters.get("party"): - del query_filters["account"] - del query_filters["against"] - or_filters = {"against": filters.get("party"), "party": filters.get("party")} - - tds_docs = frappe.get_all( - "GL Entry", - filters=query_filters, - or_filters=or_filters, - fields=["voucher_no", "voucher_type", "against", "party"], - ) + tds_docs = get_tds_docs_query(filters, bank_accounts, tds_accounts).run(as_dict=True) for d in tds_docs: if d.voucher_type == "Purchase Invoice": @@ -309,6 +290,47 @@ def get_tds_docs(filters): ) +def get_tds_docs_query(filters, bank_accounts, tds_accounts): + if not tds_accounts: + frappe.throw( + _("No {} Accounts found for this company.".format(frappe.bold("Tax Withholding"))), + title="Accounts Missing Error", + ) + gle = frappe.qb.DocType("GL Entry") + query = ( + frappe.qb.from_(gle) + .select("voucher_no", "voucher_type", "against", "party") + .where((gle.is_cancelled == 0)) + ) + + if filters.get("from_date"): + query = query.where(gle.posting_date >= filters.get("from_date")) + if filters.get("to_date"): + query = query.where(gle.posting_date <= filters.get("to_date")) + + if bank_accounts: + query = query.where(gle.against.notin(bank_accounts)) + + if filters.get("party"): + party = [filters.get("party")] + query = query.where( + ((gle.account.isin(tds_accounts) & gle.against.isin(party))) + | ((gle.voucher_type == "Journal Entry") & (gle.party == filters.get("party"))) + | gle.party.isin(party) + ) + else: + party = frappe.get_all(filters.get("party_type"), pluck="name") + query = query.where( + ((gle.account.isin(tds_accounts) & gle.against.isin(party))) + | ( + (gle.voucher_type == "Journal Entry") + & ((gle.party_type == filters.get("party_type")) | (gle.party_type == "")) + ) + | gle.party.isin(party) + ) + return query + + def get_journal_entry_party_map(journal_entries): journal_entry_party_map = {} for d in frappe.db.get_all( From 7ecc0d5a04b6c8cd8b97256d8c0e4bdb12494d5d Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Thu, 19 Oct 2023 16:44:08 +0530 Subject: [PATCH 005/130] chore: change column order --- .../tax_withholding_details.py | 67 +++++++++---------- 1 file changed, 30 insertions(+), 37 deletions(-) diff --git a/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py b/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py index e5aa6f52ee..611893bf7e 100644 --- a/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py +++ b/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py @@ -158,14 +158,14 @@ def get_gle_map(documents): def get_columns(filters): pan = "pan" if frappe.db.has_column(filters.party_type, "pan") else "tax_id" columns = [ - {"label": _(frappe.unscrub(pan)), "fieldname": pan, "fieldtype": "Data", "width": 60}, { - "label": _(filters.get("party_type")), - "fieldname": "party", - "fieldtype": "Dynamic Link", - "options": "party_type", - "width": 180, + "label": _("Section Code"), + "options": "Tax Withholding Category", + "fieldname": "section_code", + "fieldtype": "Link", + "width": 90, }, + {"label": _(frappe.unscrub(pan)), "fieldname": pan, "fieldtype": "Data", "width": 60}, ] if filters.naming_series == "Naming Series": @@ -180,51 +180,38 @@ def get_columns(filters): columns.extend( [ - { - "label": _("Date of Transaction"), - "fieldname": "transaction_date", - "fieldtype": "Date", - "width": 100, - }, - { - "label": _("Section Code"), - "options": "Tax Withholding Category", - "fieldname": "section_code", - "fieldtype": "Link", - "width": 90, - }, {"label": _("Entity Type"), "fieldname": "entity_type", "fieldtype": "Data", "width": 100}, - { - "label": _("Total Amount"), - "fieldname": "total_amount", - "fieldtype": "Float", - "width": 90, - }, { "label": _("TDS Rate %") if filters.get("party_type") == "Supplier" else _("TCS Rate %"), "fieldname": "rate", "fieldtype": "Percent", - "width": 90, + "width": 60, }, { - "label": _("Tax Amount"), - "fieldname": "tax_amount", + "label": _("Total Amount"), + "fieldname": "total_amount", "fieldtype": "Float", - "width": 90, - }, - { - "label": _("Grand Total"), - "fieldname": "grand_total", - "fieldtype": "Float", - "width": 90, + "width": 120, }, { "label": _("Base Total"), "fieldname": "base_total", "fieldtype": "Float", - "width": 90, + "width": 120, }, - {"label": _("Transaction Type"), "fieldname": "transaction_type", "width": 100}, + { + "label": _("Tax Amount"), + "fieldname": "tax_amount", + "fieldtype": "Float", + "width": 120, + }, + { + "label": _("Grand Total"), + "fieldname": "grand_total", + "fieldtype": "Float", + "width": 120, + }, + {"label": _("Transaction Type"), "fieldname": "transaction_type", "width": 130}, { "label": _("Reference No."), "fieldname": "ref_no", @@ -232,6 +219,12 @@ def get_columns(filters): "options": "transaction_type", "width": 180, }, + { + "label": _("Date of Transaction"), + "fieldname": "transaction_date", + "fieldtype": "Date", + "width": 100, + }, ] ) From 4471ad581e1c3d220468f42170a09739a46dcc21 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Thu, 19 Oct 2023 16:54:10 +0530 Subject: [PATCH 006/130] fix: sort by section code --- .../report/tax_withholding_details/tax_withholding_details.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py b/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py index 611893bf7e..6f2ec176f0 100644 --- a/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py +++ b/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py @@ -97,7 +97,7 @@ def get_result( row.update( { - "section_code": tax_withholding_category, + "section_code": tax_withholding_category or "", "entity_type": party_map.get(party, {}).get(party_type), "rate": rate, "total_amount": total_amount, @@ -111,6 +111,8 @@ def get_result( ) out.append(row) + out.sort(key=lambda x: x["section_code"]) + return out From ed2457bddfaedc83301a96f9d77b093b36d15a72 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Thu, 19 Oct 2023 16:59:49 +0530 Subject: [PATCH 007/130] feat: proprietorship & partnership options in entity type --- erpnext/buying/doctype/supplier/supplier.json | 4 ++-- erpnext/selling/doctype/customer/customer.json | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/buying/doctype/supplier/supplier.json b/erpnext/buying/doctype/supplier/supplier.json index f37db5f115..60dd54c238 100644 --- a/erpnext/buying/doctype/supplier/supplier.json +++ b/erpnext/buying/doctype/supplier/supplier.json @@ -174,7 +174,7 @@ "fieldname": "supplier_type", "fieldtype": "Select", "label": "Supplier Type", - "options": "Company\nIndividual", + "options": "Company\nIndividual\nProprietorship\nPartnership", "reqd": 1 }, { @@ -485,7 +485,7 @@ "link_fieldname": "party" } ], - "modified": "2023-09-25 12:48:21.869563", + "modified": "2023-10-19 16:55:15.148325", "modified_by": "Administrator", "module": "Buying", "name": "Supplier", diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json index 40cab9f330..3b97123113 100644 --- a/erpnext/selling/doctype/customer/customer.json +++ b/erpnext/selling/doctype/customer/customer.json @@ -134,7 +134,7 @@ "label": "Customer Type", "oldfieldname": "customer_type", "oldfieldtype": "Select", - "options": "Company\nIndividual", + "options": "Company\nIndividual\nProprietorship\nPartnership", "reqd": 1 }, { @@ -584,7 +584,7 @@ "link_fieldname": "party" } ], - "modified": "2023-09-21 12:23:20.706020", + "modified": "2023-10-19 16:56:27.327035", "modified_by": "Administrator", "module": "Selling", "name": "Customer", From 75441017c6629f81104409b892db56bb9c1bf1dd Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Thu, 19 Oct 2023 17:13:58 +0530 Subject: [PATCH 008/130] chore: linting issues --- .../report/tax_withholding_details/tax_withholding_details.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py b/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py index 6f2ec176f0..69ca4d9707 100644 --- a/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py +++ b/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py @@ -288,7 +288,7 @@ def get_tds_docs(filters): def get_tds_docs_query(filters, bank_accounts, tds_accounts): if not tds_accounts: frappe.throw( - _("No {} Accounts found for this company.".format(frappe.bold("Tax Withholding"))), + _("No {0} Accounts found for this company.").format(frappe.bold("Tax Withholding")), title="Accounts Missing Error", ) gle = frappe.qb.DocType("GL Entry") From 6d5ccde864e373b787d781c583b85a26ed2d40e9 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Thu, 19 Oct 2023 17:55:24 +0530 Subject: [PATCH 009/130] feat: add cols for supplier inv details --- .../tax_withholding_details.py | 37 ++++++++++++++++++- 1 file changed, 36 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py b/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py index 69ca4d9707..2ba5ce0210 100644 --- a/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py +++ b/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py @@ -47,6 +47,7 @@ def get_result( out = [] for name, details in gle_map.items(): tax_amount, total_amount, grand_total, base_total = 0, 0, 0, 0 + bill_no, bill_date = "", "" tax_withholding_category = tax_category_map.get(name) rate = tax_rate_map.get(tax_withholding_category) @@ -72,6 +73,8 @@ def get_result( # back calcalute total amount from rate and tax_amount if rate: total_amount = grand_total = base_total = tax_amount / (rate / 100) + elif voucher_type == "Purchase Invoice": + total_amount, grand_total, base_total, bill_no, bill_date = net_total_map.get(name) else: total_amount, grand_total, base_total = net_total_map.get(name) else: @@ -107,6 +110,8 @@ def get_result( "transaction_date": posting_date, "transaction_type": voucher_type, "ref_no": name, + "supplier_invoice_no": bill_no, + "supplier_invoice_date": bill_date, } ) out.append(row) @@ -183,6 +188,28 @@ def get_columns(filters): columns.extend( [ {"label": _("Entity Type"), "fieldname": "entity_type", "fieldtype": "Data", "width": 100}, + ] + ) + if filters.party_type == "Supplier": + columns.extend( + [ + { + "label": _("Supplier Invoice No"), + "fieldname": "supplier_invoice_no", + "fieldtype": "Data", + "width": 120, + }, + { + "label": _("Supplier Invoice Date"), + "fieldname": "supplier_invoice_date", + "fieldtype": "Date", + "width": 120, + }, + ] + ) + + columns.extend( + [ { "label": _("TDS Rate %") if filters.get("party_type") == "Supplier" else _("TCS Rate %"), "fieldname": "rate", @@ -352,6 +379,8 @@ def get_doc_info(vouchers, doctype, tax_category_map, net_total_map=None): "base_tax_withholding_net_total", "grand_total", "base_total", + "bill_no", + "bill_date", ], "Sales Invoice": ["base_net_total", "grand_total", "base_total"], "Payment Entry": [ @@ -370,7 +399,13 @@ def get_doc_info(vouchers, doctype, tax_category_map, net_total_map=None): for entry in entries: tax_category_map.update({entry.name: entry.tax_withholding_category}) if doctype == "Purchase Invoice": - value = [entry.base_tax_withholding_net_total, entry.grand_total, entry.base_total] + value = [ + entry.base_tax_withholding_net_total, + entry.grand_total, + entry.base_total, + entry.bill_no, + entry.bill_date, + ] elif doctype == "Sales Invoice": value = [entry.base_net_total, entry.grand_total, entry.base_total] elif doctype == "Payment Entry": From 17ebc1ea8096a83e882bedd00f358a4bd799eff4 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Mon, 23 Oct 2023 11:56:28 +0530 Subject: [PATCH 010/130] fix: validate so item with qtn --- erpnext/selling/doctype/sales_order/sales_order.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index b91002eb86..76e2f8b78a 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -216,7 +216,15 @@ class SalesOrder(SellingController): def validate_with_previous_doc(self): super(SalesOrder, self).validate_with_previous_doc( - {"Quotation": {"ref_dn_field": "prevdoc_docname", "compare_fields": [["company", "="]]}} + { + "Quotation": {"ref_dn_field": "prevdoc_docname", "compare_fields": [["company", "="]]}, + "Quotation Item": { + "ref_dn_field": "quotation_item", + "compare_fields": [["item_code", "="], ["uom", "="], ["conversion_factor", "="]], + "is_child_table": True, + "allow_duplicate_prev_row_id": True, + }, + } ) if cint(frappe.db.get_single_value("Selling Settings", "maintain_same_sales_rate")): From 9ef26e1df00311e248989eb823413448fa757d3a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernd=20Oliver=20S=C3=BCnderhauf?= <46800703+bosue@users.noreply.github.com> Date: Mon, 23 Oct 2023 19:41:55 +0200 Subject: [PATCH 011/130] chore: Fix typo followup to #37636 --- erpnext/translations/af.csv | 2 +- erpnext/translations/am.csv | 2 +- erpnext/translations/ar.csv | 2 +- erpnext/translations/bg.csv | 2 +- erpnext/translations/bn.csv | 2 +- erpnext/translations/bs.csv | 2 +- erpnext/translations/ca.csv | 2 +- erpnext/translations/cs.csv | 2 +- erpnext/translations/cz.csv | 2 +- erpnext/translations/da.csv | 2 +- erpnext/translations/de.csv | 2 +- erpnext/translations/el.csv | 2 +- erpnext/translations/es.csv | 2 +- erpnext/translations/et.csv | 2 +- erpnext/translations/fa.csv | 2 +- erpnext/translations/fi.csv | 2 +- erpnext/translations/fr.csv | 2 +- erpnext/translations/gu.csv | 2 +- erpnext/translations/he.csv | 2 +- erpnext/translations/hi.csv | 2 +- erpnext/translations/hr.csv | 2 +- erpnext/translations/hu.csv | 2 +- erpnext/translations/id.csv | 2 +- erpnext/translations/is.csv | 2 +- erpnext/translations/it.csv | 2 +- erpnext/translations/ja.csv | 2 +- erpnext/translations/km.csv | 2 +- erpnext/translations/kn.csv | 2 +- erpnext/translations/ko.csv | 2 +- erpnext/translations/ku.csv | 2 +- erpnext/translations/lo.csv | 2 +- erpnext/translations/lt.csv | 2 +- erpnext/translations/lv.csv | 2 +- erpnext/translations/mk.csv | 2 +- erpnext/translations/ml.csv | 2 +- erpnext/translations/mr.csv | 2 +- erpnext/translations/ms.csv | 2 +- erpnext/translations/my.csv | 2 +- erpnext/translations/nl.csv | 2 +- erpnext/translations/no.csv | 2 +- erpnext/translations/pl.csv | 2 +- erpnext/translations/ps.csv | 2 +- erpnext/translations/pt-BR.csv | 2 +- erpnext/translations/pt.csv | 2 +- erpnext/translations/ro.csv | 2 +- erpnext/translations/ru.csv | 2 +- erpnext/translations/rw.csv | 2 +- erpnext/translations/si.csv | 2 +- erpnext/translations/sk.csv | 2 +- erpnext/translations/sl.csv | 2 +- erpnext/translations/sq.csv | 2 +- erpnext/translations/sr.csv | 2 +- erpnext/translations/sv.csv | 2 +- erpnext/translations/sw.csv | 2 +- erpnext/translations/ta.csv | 2 +- erpnext/translations/te.csv | 2 +- erpnext/translations/th.csv | 2 +- erpnext/translations/tr.csv | 2 +- erpnext/translations/uk.csv | 2 +- erpnext/translations/ur.csv | 2 +- erpnext/translations/uz.csv | 2 +- erpnext/translations/vi.csv | 2 +- erpnext/translations/zh-TW.csv | 2 +- erpnext/translations/zh.csv | 2 +- erpnext/translations/zh_tw.csv | 2 +- 65 files changed, 65 insertions(+), 65 deletions(-) diff --git a/erpnext/translations/af.csv b/erpnext/translations/af.csv index d4b823d995..f45731468d 100644 --- a/erpnext/translations/af.csv +++ b/erpnext/translations/af.csv @@ -7052,7 +7052,7 @@ Moving Average,Beweeg gemiddeld, Warranty Period (in days),Garantie Periode (in dae), Auto re-order,Outo herbestel, Reorder level based on Warehouse,Herbestel vlak gebaseer op Warehouse, -Will also apply for variants unless overrridden,Sal ook aansoek doen vir variante tensy dit oortree word, +Will also apply for variants unless overridden,Sal ook aansoek doen vir variante tensy dit oortree word, Units of Measure,Eenhede van maatreël, Will also apply for variants,Sal ook aansoek doen vir variante, Serial Nos and Batches,Serial Nos and Batches, diff --git a/erpnext/translations/am.csv b/erpnext/translations/am.csv index 764868d8dc..0453d5d685 100644 --- a/erpnext/translations/am.csv +++ b/erpnext/translations/am.csv @@ -7052,7 +7052,7 @@ Moving Average,በመውሰድ ላይ አማካኝ, Warranty Period (in days),(ቀናት ውስጥ) የዋስትና ክፍለ ጊዜ, Auto re-order,ራስ-ዳግም-ትዕዛዝ, Reorder level based on Warehouse,መጋዘን ላይ የተመሠረተ አስይዝ ደረጃ, -Will also apply for variants unless overrridden,overrridden በስተቀር ደግሞ ተለዋጮች ማመልከት ይሆን, +Will also apply for variants unless overridden,overridden በስተቀር ደግሞ ተለዋጮች ማመልከት ይሆን, Units of Measure,ይለኩ አሃዶች, Will also apply for variants,በተጨማሪም ተለዋጮች ማመልከት ይሆን, Serial Nos and Batches,ተከታታይ ቁጥሮች እና ቡድኖች, diff --git a/erpnext/translations/ar.csv b/erpnext/translations/ar.csv index 4e03a18a5c..67b409e7ef 100644 --- a/erpnext/translations/ar.csv +++ b/erpnext/translations/ar.csv @@ -7052,7 +7052,7 @@ Moving Average,المتوسط المتحرك, Warranty Period (in days),فترة الضمان (بالأيام), Auto re-order,إعادة ترتيب تلقائي, Reorder level based on Warehouse,مستوى إعادة الطلب بناء على مستودع, -Will also apply for variants unless overrridden,سوف تطبق أيضا على المتغيرات الا اذا تم التغير فوقها, +Will also apply for variants unless overridden,سوف تطبق أيضا على المتغيرات الا اذا تم التغير فوقها, Units of Measure,وحدات القياس, Will also apply for variants,سوف تطبق أيضا على المتغيرات, Serial Nos and Batches,الرقم التسلسلي ودفعات, diff --git a/erpnext/translations/bg.csv b/erpnext/translations/bg.csv index 8dff755941..787f81ee7f 100644 --- a/erpnext/translations/bg.csv +++ b/erpnext/translations/bg.csv @@ -7052,7 +7052,7 @@ Moving Average,Пълзяща средна стойност, Warranty Period (in days),Гаранционен срок (в дни), Auto re-order,Автоматична повторна поръчка, Reorder level based on Warehouse,Пренареждане равнище въз основа на Warehouse, -Will also apply for variants unless overrridden,"Ще се прилага и за варианти, освен ако overrridden", +Will also apply for variants unless overridden,"Ще се прилага и за варианти, освен ако overridden", Units of Measure,Мерни единици за измерване, Will also apply for variants,Ще се прилага и за варианти, Serial Nos and Batches,Серийни номера и партиди, diff --git a/erpnext/translations/bn.csv b/erpnext/translations/bn.csv index 8a698dfca4..69fd08cb51 100644 --- a/erpnext/translations/bn.csv +++ b/erpnext/translations/bn.csv @@ -7052,7 +7052,7 @@ Moving Average,চলন্ত গড়, Warranty Period (in days),(দিন) ওয়্যারেন্টি সময়কাল, Auto re-order,অটো পুনরায় আদেশ, Reorder level based on Warehouse,গুদাম উপর ভিত্তি রেকর্ডার স্তর, -Will also apply for variants unless overrridden,Overrridden তবে এছাড়াও ভিন্নতা জন্য আবেদন করতে হবে, +Will also apply for variants unless overridden,Overrridden তবে এছাড়াও ভিন্নতা জন্য আবেদন করতে হবে, Units of Measure,পরিমাপ ইউনিট, Will also apply for variants,এছাড়াও ভিন্নতা জন্য আবেদন করতে হবে, Serial Nos and Batches,সিরিয়াল আমরা এবং ব্যাচ, diff --git a/erpnext/translations/bs.csv b/erpnext/translations/bs.csv index 7ba4a883cb..ef680a36ad 100644 --- a/erpnext/translations/bs.csv +++ b/erpnext/translations/bs.csv @@ -7052,7 +7052,7 @@ Moving Average,Moving Average, Warranty Period (in days),Jamstveni period (u danima), Auto re-order,Autorefiniš reda, Reorder level based on Warehouse,Nivo Ponovno red zasnovan na Skladište, -Will also apply for variants unless overrridden,Primjenjivat će se i za varijante osim overrridden, +Will also apply for variants unless overridden,Primjenjivat će se i za varijante osim overridden, Units of Measure,Jedinice mjere, Will also apply for variants,Primjenjivat će se i za varijante, Serial Nos and Batches,Serijski brojevi i Paketi, diff --git a/erpnext/translations/ca.csv b/erpnext/translations/ca.csv index cce1f3a13b..fa545a4c19 100644 --- a/erpnext/translations/ca.csv +++ b/erpnext/translations/ca.csv @@ -7052,7 +7052,7 @@ Moving Average,Mitjana Mòbil, Warranty Period (in days),Període de garantia (en dies), Auto re-order,Acte reordenar, Reorder level based on Warehouse,Nivell de comanda basat en Magatzem, -Will also apply for variants unless overrridden,També s'aplicarà per a les variants menys overrridden, +Will also apply for variants unless overridden,També s'aplicarà per a les variants menys overridden, Units of Measure,Unitats de mesura, Will also apply for variants,També s'aplicarà per a les variants, Serial Nos and Batches,Nº de sèrie i lots, diff --git a/erpnext/translations/cs.csv b/erpnext/translations/cs.csv index 1072ed31fc..7fb1679f9c 100644 --- a/erpnext/translations/cs.csv +++ b/erpnext/translations/cs.csv @@ -7052,7 +7052,7 @@ Moving Average,Klouzavý průměr, Warranty Period (in days),Záruční doba (ve dnech), Auto re-order,Automatické znovuobjednání, Reorder level based on Warehouse,Úroveň Změna pořadí na základě Warehouse, -Will also apply for variants unless overrridden,"Bude platit i pro varianty, pokud nebude přepsáno", +Will also apply for variants unless overridden,"Bude platit i pro varianty, pokud nebude přepsáno", Units of Measure,Jednotky měření, Will also apply for variants,Bude platit i pro varianty, Serial Nos and Batches,Sériové čísla a dávky, diff --git a/erpnext/translations/cz.csv b/erpnext/translations/cz.csv index 270a7104ba..96de062059 100644 --- a/erpnext/translations/cz.csv +++ b/erpnext/translations/cz.csv @@ -1991,7 +1991,7 @@ Maintenance start date can not be before delivery date for Serial No {0},Datum z Actual End Date,Skutečné datum ukončen$1, Applicable To (Role),Vztahující se na (Role) Purpose,Účel, -Will also apply for variants unless overrridden,"Bude platit i pro varianty, pokud nebude přepsáno" +Will also apply for variants unless overridden,"Bude platit i pro varianty, pokud nebude přepsáno" Advances,Zálohy, Approving User cannot be same as user the rule is Applicable To,Schválení Uživatel nemůže být stejná jako uživatel pravidlo se vztahuje na, No of Requested SMS,Počet žádaným SMS, diff --git a/erpnext/translations/da.csv b/erpnext/translations/da.csv index 138da5d1f2..4eb39609f2 100644 --- a/erpnext/translations/da.csv +++ b/erpnext/translations/da.csv @@ -7052,7 +7052,7 @@ Moving Average,Glidende gennemsnit, Warranty Period (in days),Garantiperiode (i dage), Auto re-order,Auto genbestil, Reorder level based on Warehouse,Genbestil niveau baseret på Warehouse, -Will also apply for variants unless overrridden,"Vil også gælde for varianter, medmindre overrridden", +Will also apply for variants unless overridden,"Vil også gælde for varianter, medmindre overridden", Units of Measure,Måleenheder, Will also apply for variants,Vil også gælde for varianter, Serial Nos and Batches,Serienummer og partier, diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index 79b9574239..c8fc2cf5c4 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -7074,7 +7074,7 @@ Moving Average,Gleitender Durchschnitt, Warranty Period (in days),Garantiefrist (in Tagen), Auto re-order,Automatische Nachbestellung, Reorder level based on Warehouse,Meldebestand auf Basis des Lagers, -Will also apply for variants unless overrridden,"Gilt auch für Varianten, sofern nicht außer Kraft gesetzt", +Will also apply for variants unless overridden,"Gilt auch für Varianten, sofern nicht außer Kraft gesetzt", Units of Measure,Maßeinheiten, Will also apply for variants,Gilt auch für Varianten, Serial Nos and Batches,Seriennummern und Chargen, diff --git a/erpnext/translations/el.csv b/erpnext/translations/el.csv index 7a83cc5dcd..21fb435901 100644 --- a/erpnext/translations/el.csv +++ b/erpnext/translations/el.csv @@ -7052,7 +7052,7 @@ Moving Average,Κινητός μέσος, Warranty Period (in days),Περίοδος εγγύησης (σε ημέρες), Auto re-order,Αυτόματη εκ νέου προκειμένου, Reorder level based on Warehouse,Αναδιάταξη επίπεδο με βάση Αποθήκης, -Will also apply for variants unless overrridden,Θα ισχύουν επίσης για τις παραλλαγές εκτός αν υπάρχει υπέρβαση, +Will also apply for variants unless overridden,Θα ισχύουν επίσης για τις παραλλαγές εκτός αν υπάρχει υπέρβαση, Units of Measure,Μονάδες μέτρησης, Will also apply for variants,Θα ισχύουν επίσης για τις παραλλαγές, Serial Nos and Batches,Σειριακοί αριθμοί και παρτίδες, diff --git a/erpnext/translations/es.csv b/erpnext/translations/es.csv index fadf7a78e2..2abe418707 100644 --- a/erpnext/translations/es.csv +++ b/erpnext/translations/es.csv @@ -7052,7 +7052,7 @@ Moving Average,Precio medio variable, Warranty Period (in days),Período de garantía (en días), Auto re-order,Ordenar Automáticamente, Reorder level based on Warehouse,Nivel de reabastecimiento basado en almacén, -Will also apply for variants unless overrridden,También se aplicará para las variantes menos que se sobre escriba, +Will also apply for variants unless overridden,También se aplicará para las variantes menos que se sobre escriba, Units of Measure,Unidades de medida, Will also apply for variants,También se aplicará para las variantes, Serial Nos and Batches,Números de serie y lotes, diff --git a/erpnext/translations/et.csv b/erpnext/translations/et.csv index 4e26a82f85..a4a8736006 100644 --- a/erpnext/translations/et.csv +++ b/erpnext/translations/et.csv @@ -7052,7 +7052,7 @@ Moving Average,Libisev keskmine, Warranty Period (in days),Garantii Periood (päeva), Auto re-order,Auto ümber korraldada, Reorder level based on Warehouse,Reorder tasandil põhineb Warehouse, -Will also apply for variants unless overrridden,"Kehtib ka variante, kui overrridden", +Will also apply for variants unless overridden,"Kehtib ka variante, kui overridden", Units of Measure,Mõõtühikud, Will also apply for variants,Kehtib ka variandid, Serial Nos and Batches,Serial Nos ning partiid, diff --git a/erpnext/translations/fa.csv b/erpnext/translations/fa.csv index 530965d8cf..bd40c8b396 100644 --- a/erpnext/translations/fa.csv +++ b/erpnext/translations/fa.csv @@ -7052,7 +7052,7 @@ Moving Average,میانگین متحرک, Warranty Period (in days),دوره گارانتی (در روز), Auto re-order,خودکار دوباره سفارش, Reorder level based on Warehouse,سطح تغییر مجدد ترتیب بر اساس انبار, -Will also apply for variants unless overrridden,همچنین برای انواع اعمال می شود مگر اینکه overrridden, +Will also apply for variants unless overridden,همچنین برای انواع اعمال می شود مگر اینکه overridden, Units of Measure,واحدهای اندازه گیری, Will also apply for variants,همچنین برای انواع اعمال می شود, Serial Nos and Batches,سریال شماره و دسته, diff --git a/erpnext/translations/fi.csv b/erpnext/translations/fi.csv index 6e9380cf74..33cf1574ae 100644 --- a/erpnext/translations/fi.csv +++ b/erpnext/translations/fi.csv @@ -7052,7 +7052,7 @@ Moving Average,Liukuva keskiarvo, Warranty Period (in days),Takuuaika (päivinä), Auto re-order,Auto re-order, Reorder level based on Warehouse,Varastoon perustuva täydennystilaustaso, -Will also apply for variants unless overrridden,"Sovelletaan myös tuotemalleissa, ellei kumota", +Will also apply for variants unless overridden,"Sovelletaan myös tuotemalleissa, ellei kumota", Units of Measure,Mittayksiköt, Will also apply for variants,Sovelletaan myös tuotemalleissa, Serial Nos and Batches,Sarjanumerot ja Erät, diff --git a/erpnext/translations/fr.csv b/erpnext/translations/fr.csv index d3875c1132..d15af74d47 100644 --- a/erpnext/translations/fr.csv +++ b/erpnext/translations/fr.csv @@ -6650,7 +6650,7 @@ Moving Average,Moyenne Mobile, Warranty Period (in days),Période de Garantie (en jours), Auto re-order,Re-commande auto, Reorder level based on Warehouse,Niveau de réapprovisionnement basé sur l’Entrepôt, -Will also apply for variants unless overrridden,S'appliquera également pour des variantes sauf si remplacé, +Will also apply for variants unless overridden,S'appliquera également pour des variantes sauf si remplacé, Units of Measure,Unités de Mesure, Will also apply for variants,S'appliquera également pour les variantes, Serial Nos and Batches,N° de Série et Lots, diff --git a/erpnext/translations/gu.csv b/erpnext/translations/gu.csv index e2de8ce96d..06a3cc64af 100644 --- a/erpnext/translations/gu.csv +++ b/erpnext/translations/gu.csv @@ -7052,7 +7052,7 @@ Moving Average,ખસેડવું સરેરાશ, Warranty Period (in days),(દિવસોમાં) વોરંટી સમયગાળા, Auto re-order,ઓટો ફરી ઓર્ડર, Reorder level based on Warehouse,વેરહાઉસ પર આધારિત પુનઃક્રમાંકિત કરો સ્તર, -Will also apply for variants unless overrridden,Overrridden સિવાય પણ ચલો માટે લાગુ પડશે, +Will also apply for variants unless overridden,Overrridden સિવાય પણ ચલો માટે લાગુ પડશે, Units of Measure,માપવા એકમો, Will also apply for variants,પણ ચલો માટે લાગુ પડશે, Serial Nos and Batches,સીરીયલ સંખ્યા અને બૅચેસ, diff --git a/erpnext/translations/he.csv b/erpnext/translations/he.csv index 6cd900a7cf..d5fcab6454 100644 --- a/erpnext/translations/he.csv +++ b/erpnext/translations/he.csv @@ -7052,7 +7052,7 @@ Moving Average,ממוצע נע, Warranty Period (in days),תקופת אחריות (בימים), Auto re-order,רכב מחדש כדי, Reorder level based on Warehouse,רמת הזמנה חוזרת המבוסס על מחסן, -Will also apply for variants unless overrridden,תחול גם לגרסות אלא אם overrridden, +Will also apply for variants unless overridden,תחול גם לגרסות אלא אם overridden, Units of Measure,יחידות מידה, Will also apply for variants,תחול גם לגרסות, Serial Nos and Batches,מספרים וסידורים סדרתיים, diff --git a/erpnext/translations/hi.csv b/erpnext/translations/hi.csv index 388b502c11..a5caa666c2 100644 --- a/erpnext/translations/hi.csv +++ b/erpnext/translations/hi.csv @@ -7052,7 +7052,7 @@ Moving Average,चलायमान औसत, Warranty Period (in days),वारंटी अवधि (दिनों में), Auto re-order,ऑटो पुनः आदेश, Reorder level based on Warehouse,गोदाम के आधार पर पुन: व्यवस्थित स्तर, -Will also apply for variants unless overrridden,Overrridden जब तक भी वेरिएंट के लिए लागू होगी, +Will also apply for variants unless overridden,Overrridden जब तक भी वेरिएंट के लिए लागू होगी, Units of Measure,मापन की इकाई, Will also apply for variants,यह भी वेरिएंट के लिए लागू होगी, Serial Nos and Batches,सीरियल नंबर और बैचों, diff --git a/erpnext/translations/hr.csv b/erpnext/translations/hr.csv index b44babc7eb..2834602248 100644 --- a/erpnext/translations/hr.csv +++ b/erpnext/translations/hr.csv @@ -7052,7 +7052,7 @@ Moving Average,Prosječna ponderirana cijena, Warranty Period (in days),Jamstveni period (u danima), Auto re-order,Automatski reorganiziraj, Reorder level based on Warehouse,Razina redoslijeda na temelju Skladište, -Will also apply for variants unless overrridden,Također će zatražiti varijante osim overrridden, +Will also apply for variants unless overridden,Također će zatražiti varijante osim overridden, Units of Measure,Mjerne jedinice, Will also apply for variants,Također će podnijeti zahtjev za varijante, Serial Nos and Batches,Serijski brojevi i serije, diff --git a/erpnext/translations/hu.csv b/erpnext/translations/hu.csv index 4ea5b9a714..a262c8a994 100644 --- a/erpnext/translations/hu.csv +++ b/erpnext/translations/hu.csv @@ -7052,7 +7052,7 @@ Moving Average,Mozgóátlag, Warranty Period (in days),Garancia hossza (napokban), Auto re-order,Auto újra-rendelés, Reorder level based on Warehouse,Raktárkészleten alapuló újrerendelési szint, -Will also apply for variants unless overrridden,"Változatokra is alkalmazni fogja, hacsak nem kerül fellülírásra", +Will also apply for variants unless overridden,"Változatokra is alkalmazni fogja, hacsak nem kerül fellülírásra", Units of Measure,Mértékegységek, Will also apply for variants,Változatokra is alkalmazni fogja, Serial Nos and Batches,Sorszámok és kötegek, diff --git a/erpnext/translations/id.csv b/erpnext/translations/id.csv index d84c3fdcd6..c4e50fdfe5 100644 --- a/erpnext/translations/id.csv +++ b/erpnext/translations/id.csv @@ -7052,7 +7052,7 @@ Moving Average,Moving Average, Warranty Period (in days),Masa Garansi (dalam hari), Auto re-order,Auto re-order, Reorder level based on Warehouse,Tingkat Re-Order berdasarkan Gudang, -Will also apply for variants unless overrridden,Juga akan berlaku untuk varian kecuali tertimpa, +Will also apply for variants unless overridden,Juga akan berlaku untuk varian kecuali tertimpa, Units of Measure,Satuan ukur, Will also apply for variants,Juga akan berlaku untuk varian, Serial Nos and Batches,Nomor Seri dan Partai, diff --git a/erpnext/translations/is.csv b/erpnext/translations/is.csv index 7687e4a680..50c06ecfbb 100644 --- a/erpnext/translations/is.csv +++ b/erpnext/translations/is.csv @@ -7052,7 +7052,7 @@ Moving Average,Moving Average, Warranty Period (in days),Ábyrgðartímabilið (í dögum), Auto re-order,Auto endurraða, Reorder level based on Warehouse,Uppröðun stigi byggist á Lager, -Will also apply for variants unless overrridden,Mun einnig gilda um afbrigði nema overrridden, +Will also apply for variants unless overridden,Mun einnig gilda um afbrigði nema overridden, Units of Measure,Mælieiningar, Will also apply for variants,Mun einnig gilda fyrir afbrigði, Serial Nos and Batches,Raðnúmer og lotur, diff --git a/erpnext/translations/it.csv b/erpnext/translations/it.csv index b88cadad36..37608958c3 100644 --- a/erpnext/translations/it.csv +++ b/erpnext/translations/it.csv @@ -7052,7 +7052,7 @@ Moving Average,Media Mobile, Warranty Period (in days),Periodo di garanzia (in giorni), Auto re-order,Auto riordino, Reorder level based on Warehouse,Livello di riordino sulla base di Magazzino, -Will also apply for variants unless overrridden,Si applica anche per le varianti meno overrridden, +Will also apply for variants unless overridden,Si applica anche per le varianti meno overridden, Units of Measure,Unità di misura, Will also apply for variants,Si applica anche per le varianti, Serial Nos and Batches,Numero e lotti seriali, diff --git a/erpnext/translations/ja.csv b/erpnext/translations/ja.csv index 11455bdf7d..888ec800c9 100644 --- a/erpnext/translations/ja.csv +++ b/erpnext/translations/ja.csv @@ -7052,7 +7052,7 @@ Moving Average,移動平均, Warranty Period (in days),保証期間(日数), Auto re-order,自動再注文, Reorder level based on Warehouse,倉庫ごとの再注文レベル, -Will also apply for variants unless overrridden,上書きされない限り、バリエーションについても適用されます, +Will also apply for variants unless overridden,上書きされない限り、バリエーションについても適用されます, Units of Measure,測定の単位, Will also apply for variants,バリエーションについても適用されます, Serial Nos and Batches,シリアル番号とバッチ, diff --git a/erpnext/translations/km.csv b/erpnext/translations/km.csv index 46dcabaa86..d2003c004e 100644 --- a/erpnext/translations/km.csv +++ b/erpnext/translations/km.csv @@ -7052,7 +7052,7 @@ Moving Average,ជាមធ្យមការផ្លាស់ប្តូរ, Warranty Period (in days),ការធានារយៈពេល (នៅក្នុងថ្ងៃ), Auto re-order,ការបញ្ជាទិញជាថ្មីម្តងទៀតដោយស្វ័យប្រវត្តិ, Reorder level based on Warehouse,កម្រិតនៃការរៀបចំដែលមានមូលដ្ឋានលើឃ្លាំង, -Will also apply for variants unless overrridden,ក៏នឹងអនុវត្តសម្រាប់វ៉ារ្យ៉ង់បានទេលុះត្រាតែ overrridden, +Will also apply for variants unless overridden,ក៏នឹងអនុវត្តសម្រាប់វ៉ារ្យ៉ង់បានទេលុះត្រាតែ overridden, Units of Measure,ឯកតារង្វាស់, Will also apply for variants,ក៏នឹងអនុវត្តសម្រាប់វ៉ារ្យ៉ង់, Serial Nos and Batches,សៀរៀល nos និងជំនាន់, diff --git a/erpnext/translations/kn.csv b/erpnext/translations/kn.csv index 18e44a1bb2..72066711ca 100644 --- a/erpnext/translations/kn.csv +++ b/erpnext/translations/kn.csv @@ -7052,7 +7052,7 @@ Moving Average,ಸರಾಸರಿ ಮೂವಿಂಗ್, Warranty Period (in days),( ದಿನಗಳಲ್ಲಿ ) ಖಾತರಿ ಅವಧಿಯ, Auto re-order,ಆಟೋ ಪುನಃ ಸಲುವಾಗಿ, Reorder level based on Warehouse,ವೇರ್ಹೌಸ್ ಆಧರಿಸಿ ಮರುಕ್ರಮಗೊಳಿಸಿ ಮಟ್ಟದ, -Will also apply for variants unless overrridden,Overrridden ಹೊರತು ಸಹ ರೂಪಾಂತರಗಳು ಅನ್ವಯವಾಗುವುದು, +Will also apply for variants unless overridden,Overrridden ಹೊರತು ಸಹ ರೂಪಾಂತರಗಳು ಅನ್ವಯವಾಗುವುದು, Units of Measure,ಮಾಪನದ ಘಟಕಗಳಿಗೆ, Will also apply for variants,ಸಹ ರೂಪಾಂತರಗಳು ಅನ್ವಯವಾಗುವುದು, Serial Nos and Batches,ಸೀರಿಯಲ್ ಸೂಲ ಮತ್ತು ಬ್ಯಾಚ್, diff --git a/erpnext/translations/ko.csv b/erpnext/translations/ko.csv index 788655c044..99119256c1 100644 --- a/erpnext/translations/ko.csv +++ b/erpnext/translations/ko.csv @@ -7052,7 +7052,7 @@ Moving Average,움직임 평균, Warranty Period (in days),(일) 보증 기간, Auto re-order,자동 재 주문, Reorder level based on Warehouse,웨어 하우스를 기반으로 재정렬 수준, -Will also apply for variants unless overrridden,overrridden가 아니면 변형 적용됩니다, +Will also apply for variants unless overridden,overridden가 아니면 변형 적용됩니다, Units of Measure,측정 단위, Will also apply for variants,또한 변형 적용됩니다, Serial Nos and Batches,일련 번호 및 배치, diff --git a/erpnext/translations/ku.csv b/erpnext/translations/ku.csv index a7fcf4e1ed..8fec05993d 100644 --- a/erpnext/translations/ku.csv +++ b/erpnext/translations/ku.csv @@ -7052,7 +7052,7 @@ Moving Average,Moving Average, Warranty Period (in days),Period Warranty (di rojên), Auto re-order,Auto re-da, Reorder level based on Warehouse,asta DIRTYHERTZ li ser Warehouse, -Will also apply for variants unless overrridden,jî wê ji bo Guhertoyên serî heta overrridden, +Will also apply for variants unless overridden,jî wê ji bo Guhertoyên serî heta overridden, Units of Measure,Yekîneyên Measure, Will also apply for variants,jî wê ji bo Guhertoyên serî, Serial Nos and Batches,Serial Nos û lekerên, diff --git a/erpnext/translations/lo.csv b/erpnext/translations/lo.csv index 9b74f655b2..0831788651 100644 --- a/erpnext/translations/lo.csv +++ b/erpnext/translations/lo.csv @@ -7052,7 +7052,7 @@ Moving Average,ການເຄື່ອນຍ້າຍໂດຍສະເລ່ Warranty Period (in days),ໄລຍະເວລາຮັບປະກັນ (ໃນວັນເວລາ), Auto re-order,Auto Re: ຄໍາສັ່ງ, Reorder level based on Warehouse,ລະດັບລໍາດັບຂຶ້ນຢູ່ກັບຄັງສິນຄ້າ, -Will also apply for variants unless overrridden,ຍັງຈະນໍາໃຊ້ສໍາລັບການ variants ເວັ້ນເສຍແຕ່ວ່າ overrridden, +Will also apply for variants unless overridden,ຍັງຈະນໍາໃຊ້ສໍາລັບການ variants ເວັ້ນເສຍແຕ່ວ່າ overridden, Units of Measure,ຫົວຫນ່ວຍວັດແທກ, Will also apply for variants,ຍັງຈະນໍາໃຊ້ສໍາລັບການ variants, Serial Nos and Batches,Serial Nos ແລະສໍາຫລັບຂະບວນ, diff --git a/erpnext/translations/lt.csv b/erpnext/translations/lt.csv index 2c87256d2a..82152754e8 100644 --- a/erpnext/translations/lt.csv +++ b/erpnext/translations/lt.csv @@ -7052,7 +7052,7 @@ Moving Average,slenkamasis vidurkis, Warranty Period (in days),Garantinis laikotarpis (dienomis), Auto re-order,Auto naujo užsakymas, Reorder level based on Warehouse,Pertvarkyti lygį remiantis Warehouse, -Will also apply for variants unless overrridden,Bus taikoma variantų nebent overrridden, +Will also apply for variants unless overridden,Bus taikoma variantų nebent overridden, Units of Measure,Matavimo vienetai, Will also apply for variants,Bus taikoma variantų, Serial Nos and Batches,Eilės Nr ir Partijos, diff --git a/erpnext/translations/lv.csv b/erpnext/translations/lv.csv index 2cfa1306f1..8c4526ca52 100644 --- a/erpnext/translations/lv.csv +++ b/erpnext/translations/lv.csv @@ -7052,7 +7052,7 @@ Moving Average,Moving Average, Warranty Period (in days),Garantijas periods (dienās), Auto re-order,Auto re-pasūtīt, Reorder level based on Warehouse,Pārkārtot līmenis balstās uz Noliktava, -Will also apply for variants unless overrridden,"Attieksies arī uz variantiem, ja vien overrridden", +Will also apply for variants unless overridden,"Attieksies arī uz variantiem, ja vien overridden", Units of Measure,Mērvienību, Will also apply for variants,Attieksies arī uz variantiem, Serial Nos and Batches,Sērijas Nr un Partijām, diff --git a/erpnext/translations/mk.csv b/erpnext/translations/mk.csv index f01b1b0cf2..a622524bf1 100644 --- a/erpnext/translations/mk.csv +++ b/erpnext/translations/mk.csv @@ -7052,7 +7052,7 @@ Moving Average,Се движат просек, Warranty Period (in days),Гарантниот период (во денови), Auto re-order,Автоматско повторно цел, Reorder level based on Warehouse,Ниво врз основа на промените редоследот Магацински, -Will also apply for variants unless overrridden,"Ќе се казни и варијанти, освен ако overrridden", +Will also apply for variants unless overridden,"Ќе се казни и варијанти, освен ако overridden", Units of Measure,На мерните единици, Will also apply for variants,Ќе се применуваат и за варијанти, Serial Nos and Batches,Сериски броеви и Пакетите, diff --git a/erpnext/translations/ml.csv b/erpnext/translations/ml.csv index 59d3160d39..777d5c64ff 100644 --- a/erpnext/translations/ml.csv +++ b/erpnext/translations/ml.csv @@ -7052,7 +7052,7 @@ Moving Average,ശരാശരി നീക്കുന്നു, Warranty Period (in days),(ദിവസങ്ങളിൽ) വാറന്റി കാലാവധി, Auto re-order,ഓട്ടോ റീ-ഓർഡർ, Reorder level based on Warehouse,വെയർഹൗസ് അടിസ്ഥാനമാക്കിയുള്ള പുനഃക്രമീകരിക്കുക തലത്തിൽ, -Will also apply for variants unless overrridden,കൂടാതെ overrridden അവയൊഴിച്ച് മോഡലുകൾക്കാണ് ബാധകമാകും, +Will also apply for variants unless overridden,കൂടാതെ overridden അവയൊഴിച്ച് മോഡലുകൾക്കാണ് ബാധകമാകും, Units of Measure,അളവിന്റെ യൂണിറ്റുകൾ, Will also apply for variants,കൂടാതെ മോഡലുകൾക്കാണ് ബാധകമാകും, Serial Nos and Batches,സീരിയൽ എണ്ണം ബാച്ചുകളും, diff --git a/erpnext/translations/mr.csv b/erpnext/translations/mr.csv index ff339b6d9f..624f1ab481 100644 --- a/erpnext/translations/mr.csv +++ b/erpnext/translations/mr.csv @@ -7052,7 +7052,7 @@ Moving Average,हलवित/Moving सरासरी, Warranty Period (in days),(दिवस मध्ये) वॉरंटी कालावधी, Auto re-order,ऑटो पुन्हा आदेश, Reorder level based on Warehouse,वखारवर आधारित पुन्हा क्रमवारी लावा पातळी, -Will also apply for variants unless overrridden,Overrridden आहेत तोपर्यंत देखील रूपे लागू राहील, +Will also apply for variants unless overridden,Overrridden आहेत तोपर्यंत देखील रूपे लागू राहील, Units of Measure,माप युनिट, Will also apply for variants,तसेच रूपे लागू राहील, Serial Nos and Batches,सिरियल क्र आणि बॅच, diff --git a/erpnext/translations/ms.csv b/erpnext/translations/ms.csv index 2258a1804f..75e150a88d 100644 --- a/erpnext/translations/ms.csv +++ b/erpnext/translations/ms.csv @@ -7052,7 +7052,7 @@ Moving Average,Purata bergerak, Warranty Period (in days),Tempoh jaminan (dalam hari), Auto re-order,Auto semula perintah, Reorder level based on Warehouse,Tahap pesanan semula berdasarkan Warehouse, -Will also apply for variants unless overrridden,Juga akan memohon varian kecuali overrridden, +Will also apply for variants unless overridden,Juga akan memohon varian kecuali overridden, Units of Measure,Unit ukuran, Will also apply for variants,Juga akan memohon varian, Serial Nos and Batches,Serial Nos dan Kelompok, diff --git a/erpnext/translations/my.csv b/erpnext/translations/my.csv index dc5ab12f43..36cd8740d7 100644 --- a/erpnext/translations/my.csv +++ b/erpnext/translations/my.csv @@ -7052,7 +7052,7 @@ Moving Average,ပျမ်းမျှ Moving, Warranty Period (in days),(ရက်) ကိုအာမခံကာလ, Auto re-order,မော်တော်ကားပြန်လည်အမိန့်, Reorder level based on Warehouse,ဂိုဒေါင်အပေါ်အခြေခံပြီး reorder level ကို, -Will also apply for variants unless overrridden,စ overrridden မဟုတ်လျှင်မျိုးကွဲလျှောက်ထားလိမ့်မည်ဟု, +Will also apply for variants unless overridden,စ overridden မဟုတ်လျှင်မျိုးကွဲလျှောက်ထားလိမ့်မည်ဟု, Units of Measure,တိုင်း၏ယူနစ်, Will also apply for variants,စမျိုးကွဲလျှောက်ထားလိမ့်မည်ဟု, Serial Nos and Batches,serial Nos နှင့် batch, diff --git a/erpnext/translations/nl.csv b/erpnext/translations/nl.csv index ad11eae08b..5859833861 100644 --- a/erpnext/translations/nl.csv +++ b/erpnext/translations/nl.csv @@ -7052,7 +7052,7 @@ Moving Average,Moving Average, Warranty Period (in days),Garantieperiode (in dagen), Auto re-order,Auto re-order, Reorder level based on Warehouse,Bestelniveau gebaseerd op Warehouse, -Will also apply for variants unless overrridden,Geldt ook voor varianten tenzij overrridden, +Will also apply for variants unless overridden,Geldt ook voor varianten tenzij overridden, Units of Measure,Meeteenheden, Will also apply for variants,Geldt ook voor varianten, Serial Nos and Batches,Serienummers en batches, diff --git a/erpnext/translations/no.csv b/erpnext/translations/no.csv index b136e97954..a3236ac084 100644 --- a/erpnext/translations/no.csv +++ b/erpnext/translations/no.csv @@ -7052,7 +7052,7 @@ Moving Average,Glidende gjennomsnitt, Warranty Period (in days),Garantiperioden (i dager), Auto re-order,Auto re-order, Reorder level based on Warehouse,Omgjøre nivå basert på Warehouse, -Will also apply for variants unless overrridden,Vil også gjelde for varianter med mindre overrridden, +Will also apply for variants unless overridden,Vil også gjelde for varianter med mindre overridden, Units of Measure,Måleenheter, Will also apply for variants,Vil også gjelde for varianter, Serial Nos and Batches,Serienummer og partier, diff --git a/erpnext/translations/pl.csv b/erpnext/translations/pl.csv index 9be56f36f7..df41e39862 100644 --- a/erpnext/translations/pl.csv +++ b/erpnext/translations/pl.csv @@ -6987,7 +6987,7 @@ Moving Average,Średnia Ruchoma, Warranty Period (in days),Okres gwarancji (w dniach), Auto re-order,Automatyczne ponowne zamówienie, Reorder level based on Warehouse,Zmiana kolejności w oparciu o poziom Magazynu, -Will also apply for variants unless overrridden,"Również zostanie zastosowany do wariantów, chyba że zostanie nadpisany", +Will also apply for variants unless overridden,"Również zostanie zastosowany do wariantów, chyba że zostanie nadpisany", Units of Measure,Jednostki miary, Will also apply for variants,Również zastosowanie do wariantów, Serial Nos and Batches,Numery seryjne i partie, diff --git a/erpnext/translations/ps.csv b/erpnext/translations/ps.csv index 8033752f78..5a0b2a50cb 100644 --- a/erpnext/translations/ps.csv +++ b/erpnext/translations/ps.csv @@ -7052,7 +7052,7 @@ Moving Average,حرکت اوسط, Warranty Period (in days),ګرنټی د دورې (په ورځو), Auto re-order,د موټرونو د بيا نظم, Reorder level based on Warehouse,ترمیمي په کچه د پر بنسټ د ګدام, -Will also apply for variants unless overrridden,مګر overrridden به د بېرغونو هم تر غوږو, +Will also apply for variants unless overridden,مګر overridden به د بېرغونو هم تر غوږو, Units of Measure,د اندازه کولو واحدونه, Will also apply for variants,به هم د بېرغونو درخواست, Serial Nos and Batches,سریال وځيري او دستو, diff --git a/erpnext/translations/pt-BR.csv b/erpnext/translations/pt-BR.csv index 9823470afb..bc5b616080 100644 --- a/erpnext/translations/pt-BR.csv +++ b/erpnext/translations/pt-BR.csv @@ -7052,7 +7052,7 @@ Moving Average,Média Móvel, Warranty Period (in days),Período de Garantia (em dias), Auto re-order,Reposição Automática, Reorder level based on Warehouse,Nível de reposição baseado no Armazén, -Will also apply for variants unless overrridden,Também se aplica a variantes a não ser que seja sobrescrito, +Will also apply for variants unless overridden,Também se aplica a variantes a não ser que seja sobrescrito, Units of Measure,Unidades de Medida, Will also apply for variants,Também se aplica às variantes, Serial Nos and Batches,Números de Série e Lotes, diff --git a/erpnext/translations/pt.csv b/erpnext/translations/pt.csv index c2afe3216d..e6846c6a37 100644 --- a/erpnext/translations/pt.csv +++ b/erpnext/translations/pt.csv @@ -7052,7 +7052,7 @@ Moving Average,Média Móvel, Warranty Period (in days),Período de Garantia (em dias), Auto re-order,Voltar a Pedir Autom., Reorder level based on Warehouse,Nível de reencomenda no Armazém, -Will also apply for variants unless overrridden,Também se aplica para as variantes a menos que seja anulado, +Will also apply for variants unless overridden,Também se aplica para as variantes a menos que seja anulado, Units of Measure,Unidades de medida, Will also apply for variants,Também se aplicará para as variantes, Serial Nos and Batches,Números de série e lotes, diff --git a/erpnext/translations/ro.csv b/erpnext/translations/ro.csv index 0cb3f84278..ac7e598e2d 100644 --- a/erpnext/translations/ro.csv +++ b/erpnext/translations/ro.csv @@ -7052,7 +7052,7 @@ Moving Average,Mutarea medie, Warranty Period (in days),Perioada de garanție (în zile), Auto re-order,Re-comandă automată, Reorder level based on Warehouse,Nivel pentru re-comanda bazat pe Magazie, -Will also apply for variants unless overrridden,Se va aplica și pentru variantele cu excepția cazului în overrridden, +Will also apply for variants unless overridden,Se va aplica și pentru variantele cu excepția cazului în overridden, Units of Measure,Unitati de masura, Will also apply for variants,"Va aplică, de asemenea pentru variante", Serial Nos and Batches,Numere și loturi seriale, diff --git a/erpnext/translations/ru.csv b/erpnext/translations/ru.csv index da4e1bef82..52c29982f3 100644 --- a/erpnext/translations/ru.csv +++ b/erpnext/translations/ru.csv @@ -6985,7 +6985,7 @@ Moving Average,Скользящее среднее, Warranty Period (in days),Гарантийный период (дней), Auto re-order,Автоматический перезаказ, Reorder level based on Warehouse,Уровень переупорядочивания на основе склада, -Will also apply for variants unless overrridden,"Будет также применяться для модификаций, если не отменено", +Will also apply for variants unless overridden,"Будет также применяться для модификаций, если не отменено", Units of Measure,Единицы измерения, Will also apply for variants,Также применять к модификациям, Serial Nos and Batches,Серийные номера и партии, diff --git a/erpnext/translations/rw.csv b/erpnext/translations/rw.csv index ae0cb3a908..f035d57985 100644 --- a/erpnext/translations/rw.csv +++ b/erpnext/translations/rw.csv @@ -7052,7 +7052,7 @@ Moving Average,Impuzandengo, Warranty Period (in days),Igihe cya garanti (muminsi), Auto re-order,Ongera utumire, Reorder level based on Warehouse,Urwego rwo kwisubiramo rushingiye kububiko, -Will also apply for variants unless overrridden,Uzasaba kandi kubitandukanye keretse birenze, +Will also apply for variants unless overridden,Uzasaba kandi kubitandukanye keretse birenze, Units of Measure,Ibipimo, Will also apply for variants,Uzasaba kandi kubitandukanye, Serial Nos and Batches,Urutonde Nomero, diff --git a/erpnext/translations/si.csv b/erpnext/translations/si.csv index fa6fbf090a..4047263492 100644 --- a/erpnext/translations/si.csv +++ b/erpnext/translations/si.csv @@ -7052,7 +7052,7 @@ Moving Average,වෙනස්වන සාමාන්යය, Warranty Period (in days),වගකීම් කාලය (දින තුළ), Auto re-order,වාහන නැවත අනුපිළිවෙලට, Reorder level based on Warehouse,ගබඩාව මත පදනම් මොහොත මට්ටමේ, -Will also apply for variants unless overrridden,ද overrridden මිස ප්රභේද සඳහා අයදුම් කරනු ඇත, +Will also apply for variants unless overridden,ද overridden මිස ප්රභේද සඳහා අයදුම් කරනු ඇත, Units of Measure,නු ඒකක, Will also apply for variants,ද ප්රභේද සඳහා අයදුම් කරනු ඇත, Serial Nos and Batches,අනුක්රමික අංක සහ කාණ්ඩ, diff --git a/erpnext/translations/sk.csv b/erpnext/translations/sk.csv index 0e51158b27..98e1663f1c 100644 --- a/erpnext/translations/sk.csv +++ b/erpnext/translations/sk.csv @@ -7052,7 +7052,7 @@ Moving Average,Klouzavý průměr, Warranty Period (in days),Záruční doba (ve dnech), Auto re-order,Auto re-order, Reorder level based on Warehouse,Úroveň Zmena poradia na základe Warehouse, -Will also apply for variants unless overrridden,"Bude platiť aj pre varianty, pokiaľ nebude prepísané", +Will also apply for variants unless overridden,"Bude platiť aj pre varianty, pokiaľ nebude prepísané", Units of Measure,merné jednotky, Will also apply for variants,Bude platiť aj pre varianty, Serial Nos and Batches,Sériové čísla a dávky, diff --git a/erpnext/translations/sl.csv b/erpnext/translations/sl.csv index 7c4e11b63f..5380714bdc 100644 --- a/erpnext/translations/sl.csv +++ b/erpnext/translations/sl.csv @@ -7052,7 +7052,7 @@ Moving Average,Moving Average, Warranty Period (in days),Garancijski rok (v dnevih), Auto re-order,Auto re-order, Reorder level based on Warehouse,Raven Preureditev temelji na Warehouse, -Will also apply for variants unless overrridden,Bo veljalo tudi za variante razen overrridden, +Will also apply for variants unless overridden,Bo veljalo tudi za variante razen overridden, Units of Measure,Merske enote, Will also apply for variants,Bo veljalo tudi za variante, Serial Nos and Batches,Serijska št in Serije, diff --git a/erpnext/translations/sq.csv b/erpnext/translations/sq.csv index 0f10795113..2a893d272b 100644 --- a/erpnext/translations/sq.csv +++ b/erpnext/translations/sq.csv @@ -7052,7 +7052,7 @@ Moving Average,Moving Mesatare, Warranty Period (in days),Garanci Periudha (në ditë), Auto re-order,Auto ri-qëllim, Reorder level based on Warehouse,Niveli Reorder bazuar në Magazina, -Will also apply for variants unless overrridden,Gjithashtu do të aplikojë për variantet nëse overrridden, +Will also apply for variants unless overridden,Gjithashtu do të aplikojë për variantet nëse overridden, Units of Measure,Njësitë e masës, Will also apply for variants,Gjithashtu do të aplikojë për variantet, Serial Nos and Batches,Serial Nr dhe Batches, diff --git a/erpnext/translations/sr.csv b/erpnext/translations/sr.csv index 38116ecc9e..c1e5eb0eea 100644 --- a/erpnext/translations/sr.csv +++ b/erpnext/translations/sr.csv @@ -7052,7 +7052,7 @@ Moving Average,Мовинг Авераге, Warranty Period (in days),Гарантни период (у данима), Auto re-order,Ауто поново реда, Reorder level based on Warehouse,Промени редослед ниво на основу Варехоусе, -Will also apply for variants unless overrridden,Ће конкурисати и за варијанте осим оверрридден, +Will also apply for variants unless overridden,Ће конкурисати и за варијанте осим оверрридден, Units of Measure,Мерних јединица, Will also apply for variants,Ће конкурисати и за варијанте, Serial Nos and Batches,Сериал Нос анд Пакети, diff --git a/erpnext/translations/sv.csv b/erpnext/translations/sv.csv index c4d46ea130..8b4ab068eb 100644 --- a/erpnext/translations/sv.csv +++ b/erpnext/translations/sv.csv @@ -7052,7 +7052,7 @@ Moving Average,Rörligt medelvärde, Warranty Period (in days),Garantitiden (i dagar), Auto re-order,Auto återbeställning, Reorder level based on Warehouse,Beställningsnivå baserat på Warehouse, -Will also apply for variants unless overrridden,Kommer också att ansöka om varianter såvida inte överskriden, +Will also apply for variants unless overridden,Kommer också att ansöka om varianter såvida inte överskriden, Units of Measure,Måttenheter, Will also apply for variants,Kommer också att ansöka om varianter, Serial Nos and Batches,Serienummer och partier, diff --git a/erpnext/translations/sw.csv b/erpnext/translations/sw.csv index ad144f04d6..fa2287c3bb 100644 --- a/erpnext/translations/sw.csv +++ b/erpnext/translations/sw.csv @@ -7052,7 +7052,7 @@ Moving Average,Kusonga Wastani, Warranty Period (in days),Kipindi cha udhamini (katika siku), Auto re-order,Rejesha upya, Reorder level based on Warehouse,Weka upya ngazi kulingana na Ghala, -Will also apply for variants unless overrridden,Pia itatumika kwa vipengee isipokuwa imeingizwa, +Will also apply for variants unless overridden,Pia itatumika kwa vipengee isipokuwa imeingizwa, Units of Measure,Units of Measure, Will also apply for variants,Pia itatumika kwa vipengee, Serial Nos and Batches,Serial Nos na Batches, diff --git a/erpnext/translations/ta.csv b/erpnext/translations/ta.csv index 5ccabc044d..6eaae34a6e 100644 --- a/erpnext/translations/ta.csv +++ b/erpnext/translations/ta.csv @@ -7052,7 +7052,7 @@ Moving Average,சராசரியாக நகர்கிறது, Warranty Period (in days),உத்தரவாதத்தை காலம் (நாட்கள்), Auto re-order,வாகன மறு ஒழுங்கு, Reorder level based on Warehouse,கிடங்கில் அடிப்படையில் மறுவரிசைப்படுத்துக நிலை, -Will also apply for variants unless overrridden,Overrridden வரை கூட வகைகளில் விண்ணப்பிக்க, +Will also apply for variants unless overridden,Overrridden வரை கூட வகைகளில் விண்ணப்பிக்க, Units of Measure,அளவின் அலகுகள், Will also apply for variants,கூட வகைகளில் விண்ணப்பிக்க, Serial Nos and Batches,சீரியல் எண்கள் மற்றும் தொகுப்புகளும், diff --git a/erpnext/translations/te.csv b/erpnext/translations/te.csv index 8472163af5..d3f739af8b 100644 --- a/erpnext/translations/te.csv +++ b/erpnext/translations/te.csv @@ -7052,7 +7052,7 @@ Moving Average,మూవింగ్ సగటు, Warranty Period (in days),(రోజుల్లో) వారంటీ వ్యవధి, Auto re-order,ఆటో క్రమాన్ని, Reorder level based on Warehouse,వేర్హౌస్ ఆధారంగా క్రమాన్ని స్థాయి, -Will also apply for variants unless overrridden,Overrridden తప్ప కూడా రూపాంతరాలు వర్తిస్తాయని, +Will also apply for variants unless overridden,Overrridden తప్ప కూడా రూపాంతరాలు వర్తిస్తాయని, Units of Measure,యూనిట్స్ ఆఫ్ మెజర్, Will also apply for variants,కూడా రూపాంతరాలు వర్తిస్తాయని, Serial Nos and Batches,సీరియల్ Nos మరియు ఇస్తున్న, diff --git a/erpnext/translations/th.csv b/erpnext/translations/th.csv index dcd632bb3c..a065595898 100644 --- a/erpnext/translations/th.csv +++ b/erpnext/translations/th.csv @@ -7052,7 +7052,7 @@ Moving Average,ค่าเฉลี่ยเคลื่อนที่, Warranty Period (in days),ระยะเวลารับประกัน (วัน), Auto re-order,Auto สั่งซื้อใหม่, Reorder level based on Warehouse,ระดับสั่งซื้อใหม่บนพื้นฐานของคลังสินค้า, -Will also apply for variants unless overrridden,นอกจากนี้ยังจะใช้สำหรับสายพันธุ์เว้นแต่ overrridden, +Will also apply for variants unless overridden,นอกจากนี้ยังจะใช้สำหรับสายพันธุ์เว้นแต่ overridden, Units of Measure,หน่วยวัด, Will also apply for variants,นอกจากนี้ยังจะใช้สำหรับสายพันธุ์, Serial Nos and Batches,หมายเลขและชุดเลขที่ผลิตภัณฑ์, diff --git a/erpnext/translations/tr.csv b/erpnext/translations/tr.csv index 3708246a9d..9e916f0315 100644 --- a/erpnext/translations/tr.csv +++ b/erpnext/translations/tr.csv @@ -7051,7 +7051,7 @@ Moving Average,Hareketli Ortalama, Warranty Period (in days),Garanti Süresi (gün), Auto re-order,Otomatik Yeniden Sipariş, Reorder level based on Warehouse,Depo bazlı Yeniden sipariş seviyesi, -Will also apply for variants unless overrridden,Geçersiz kılınmadığı sürece varyantlar için de geçerli olacaktır., +Will also apply for variants unless overridden,Geçersiz kılınmadığı sürece varyantlar için de geçerli olacaktır., Units of Measure,Ölçü Birimleri, Will also apply for variants,Varyantlar için de geçerli olacak, Serial Nos and Batches,Seri No ve Batches (Parti), diff --git a/erpnext/translations/uk.csv b/erpnext/translations/uk.csv index 1752330754..8d1fb04fdb 100644 --- a/erpnext/translations/uk.csv +++ b/erpnext/translations/uk.csv @@ -7052,7 +7052,7 @@ Moving Average,Moving Average, Warranty Period (in days),Гарантійний термін (в днях), Auto re-order,Авто-дозамовлення, Reorder level based on Warehouse,Рівень перезамовлення по складу, -Will also apply for variants unless overrridden,"Буде також застосовуватися для варіантів, якщо не перевказано", +Will also apply for variants unless overridden,"Буде також застосовуватися для варіантів, якщо не перевказано", Units of Measure,одиниці виміру, Will also apply for variants,Буде також застосовуватися для варіантів, Serial Nos and Batches,Серійні номери і Порції, diff --git a/erpnext/translations/ur.csv b/erpnext/translations/ur.csv index 504cc8de0c..649c1c7759 100644 --- a/erpnext/translations/ur.csv +++ b/erpnext/translations/ur.csv @@ -7052,7 +7052,7 @@ Moving Average,حرکت پذیری اوسط, Warranty Period (in days),(دن میں) وارنٹی مدت, Auto re-order,آٹو دوبارہ آرڈر, Reorder level based on Warehouse,گودام کی بنیاد پر ترتیب کی سطح کو منتخب, -Will also apply for variants unless overrridden,overrridden جب تک بھی مختلف حالتوں کے لئے لاگو ہوں گے, +Will also apply for variants unless overridden,overridden جب تک بھی مختلف حالتوں کے لئے لاگو ہوں گے, Units of Measure,پیمائش کی اکائیوں, Will also apply for variants,بھی مختلف حالتوں کے لئے لاگو ہوں گے, Serial Nos and Batches,سیریل نمبر اور بیچوں, diff --git a/erpnext/translations/uz.csv b/erpnext/translations/uz.csv index 6b25e7b152..5ca51ccb0f 100644 --- a/erpnext/translations/uz.csv +++ b/erpnext/translations/uz.csv @@ -7052,7 +7052,7 @@ Moving Average,O'rtacha harakatlanuvchi, Warranty Period (in days),Kafolat muddati (kunlar), Auto re-order,Avtomatik buyurtma, Reorder level based on Warehouse,Qoidalarga asoslangan qayta tartiblash, -Will also apply for variants unless overrridden,"Variantlar uchun ham qo'llanilmaydi, agar bekor qilinsa", +Will also apply for variants unless overridden,"Variantlar uchun ham qo'llanilmaydi, agar bekor qilinsa", Units of Measure,O'lchov birliklari, Will also apply for variants,"Shuningdek, variantlar uchun ham qo'llaniladi", Serial Nos and Batches,Seriya nos va paketlar, diff --git a/erpnext/translations/vi.csv b/erpnext/translations/vi.csv index e576ef7119..7a005fa414 100644 --- a/erpnext/translations/vi.csv +++ b/erpnext/translations/vi.csv @@ -7052,7 +7052,7 @@ Moving Average,Di chuyển trung bình, Warranty Period (in days),Thời gian bảo hành (trong...ngày), Auto re-order,Auto lại trật tự, Reorder level based on Warehouse,mức đèn đỏ mua vật tư (phải bổ xung hoặc đặt mua thêm), -Will also apply for variants unless overrridden,Cũng sẽ được áp dụng cho các biến thể trừ phần bị ghi đèn, +Will also apply for variants unless overridden,Cũng sẽ được áp dụng cho các biến thể trừ phần bị ghi đèn, Units of Measure,Đơn vị đo lường, Will also apply for variants,Cũng sẽ được áp dụng cho các biến thể, Serial Nos and Batches,Số hàng loạt và hàng loạt, diff --git a/erpnext/translations/zh-TW.csv b/erpnext/translations/zh-TW.csv index 699d802206..0f76e97f97 100644 --- a/erpnext/translations/zh-TW.csv +++ b/erpnext/translations/zh-TW.csv @@ -2964,7 +2964,7 @@ Replace BOM,更換BOM, Code {0} already exist,代碼{0}已經存在 Sales orders are not available for production,銷售訂單不可用於生產 Fixed Asset Depreciation Settings,固定資產折舊設置 -Will also apply for variants unless overrridden,同時將申請變種,除非overrridden, +Will also apply for variants unless overridden,同時將申請變種,除非overridden, Advances,進展 Manufacture against Material Request,對製造材料要求 Assessment Group: ,評估組: diff --git a/erpnext/translations/zh.csv b/erpnext/translations/zh.csv index 70ec81af26..1d8a261005 100644 --- a/erpnext/translations/zh.csv +++ b/erpnext/translations/zh.csv @@ -7052,7 +7052,7 @@ Moving Average,移动平均, Warranty Period (in days),保修期限(天数), Auto re-order,自动重订货, Reorder level based on Warehouse,根据仓库订货点水平, -Will also apply for variants unless overrridden,除非手动指定,否则会同时应用于变体, +Will also apply for variants unless overridden,除非手动指定,否则会同时应用于变体, Units of Measure,计量单位, Will also apply for variants,会同时应用于变体, Serial Nos and Batches,序列号和批号, diff --git a/erpnext/translations/zh_tw.csv b/erpnext/translations/zh_tw.csv index 75157f02fc..8ecbaaa9c5 100644 --- a/erpnext/translations/zh_tw.csv +++ b/erpnext/translations/zh_tw.csv @@ -7485,7 +7485,7 @@ Moving Average,移動平均線, Warranty Period (in days),保修期限(天數), Auto re-order,自動重新排序, Reorder level based on Warehouse,根據倉庫訂貨點水平, -Will also apply for variants unless overrridden,同時將申請變種,除非overrridden, +Will also apply for variants unless overridden,同時將申請變種,除非overridden, Units of Measure,測量的單位, Will also apply for variants,同時將申請變種, Serial Nos and Batches,序列號和批號, From d69b0d76dd2d89fe0c1b9a634923cf8716a37c11 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Wed, 25 Oct 2023 13:59:37 +0530 Subject: [PATCH 012/130] fix: status for over delivery or billing --- erpnext/controllers/status_updater.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py index 73a248fb53..d09001c8fc 100644 --- a/erpnext/controllers/status_updater.py +++ b/erpnext/controllers/status_updater.py @@ -47,15 +47,15 @@ status_map = { ], [ "To Bill", - "eval:(self.per_delivered == 100 or self.skip_delivery_note) and self.per_billed < 100 and self.docstatus == 1", + "eval:(self.per_delivered >= 100 or self.skip_delivery_note) and self.per_billed < 100 and self.docstatus == 1", ], [ "To Deliver", - "eval:self.per_delivered < 100 and self.per_billed == 100 and self.docstatus == 1 and not self.skip_delivery_note", + "eval:self.per_delivered < 100 and self.per_billed >= 100 and self.docstatus == 1 and not self.skip_delivery_note", ], [ "Completed", - "eval:(self.per_delivered == 100 or self.skip_delivery_note) and self.per_billed == 100 and self.docstatus == 1", + "eval:(self.per_delivered >= 100 or self.skip_delivery_note) and self.per_billed >= 100 and self.docstatus == 1", ], ["Cancelled", "eval:self.docstatus==2"], ["Closed", "eval:self.status=='Closed' and self.docstatus != 2"], From 4bbad7f44819bb9a2db2163879fb8cedfcd83871 Mon Sep 17 00:00:00 2001 From: viralkansodiya15 <98073516+viralpatel15@users.noreply.github.com> Date: Thu, 26 Oct 2023 11:45:47 +0530 Subject: [PATCH 013/130] fix: set docstatus filter to ignore cancel document --- .../stock/doctype/landed_cost_voucher/landed_cost_voucher.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py index 7f0dc2df9f..5e35c16365 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py @@ -205,7 +205,7 @@ class LandedCostVoucher(Document): ) docs = frappe.db.get_all( "Asset", - filters={receipt_document_type: item.receipt_document, "item_code": item.item_code}, + filters={receipt_document_type: item.receipt_document, "item_code": item.item_code, "docstatus":['!=', 2]}, fields=["name", "docstatus"], ) if not docs or len(docs) != item.qty: From 8903c1bc6f24c4e0187f05739887ac6f4af6a7b6 Mon Sep 17 00:00:00 2001 From: vishal Date: Fri, 27 Oct 2023 11:12:55 +0530 Subject: [PATCH 014/130] feat: multi-select customer group in AR Report --- .../accounts_receivable.js | 9 ++++++--- .../accounts_receivable.py | 19 ++++++++++++++++++- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js index 1073be0bdc..393a6ff4ed 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js @@ -114,10 +114,13 @@ frappe.query_reports["Accounts Receivable"] = { "reqd": 1 }, { - "fieldname": "customer_group", + "fieldname":"customer_group", "label": __("Customer Group"), - "fieldtype": "Link", - "options": "Customer Group" + "fieldtype": "MultiSelectList", + "options": "Customer Group", + get_data: function(txt) { + return frappe.db.get_link_options('Customer Group', txt); + } }, { "fieldname": "payment_terms_template", diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index b9c7a0bfb8..dfe801c73b 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -823,7 +823,9 @@ class ReceivablePayableReport(object): self.customer = qb.DocType("Customer") if self.filters.get("customer_group"): - self.get_hierarchical_filters("Customer Group", "customer_group") + groups = get_customer_group_with_children(self.filters.customer_group) + customers = qb.from_(self.customer).select(self.customer.name).where(self.customer['customer_group'].isin(groups)) + self.qb_selection_filter.append(self.ple.party.isin(customers)) if self.filters.get("territory"): self.get_hierarchical_filters("Territory", "territory") @@ -1115,3 +1117,18 @@ class ReceivablePayableReport(object): .run() ) self.err_journals = [x[0] for x in results] if results else [] + +def get_customer_group_with_children(customer_groups): + if not isinstance(customer_groups, list): + customer_groups = [d.strip() for d in customer_groups.strip().split(",") if d] + + all_customer_groups = [] + for d in customer_groups: + if frappe.db.exists("Customer Group", d): + lft, rgt = frappe.db.get_value("Customer Group", d, ["lft", "rgt"]) + children = frappe.get_all("Customer Group", filters={"lft": [">=", lft], "rgt": ["<=", rgt]}) + all_customer_groups += [c.name for c in children] + else: + frappe.throw(_("Customer Group: {0} does not exist").format(d)) + + return list(set(all_customer_groups)) From b60c57a97dee1ab1a2b1654d4cc2d146e0141ff2 Mon Sep 17 00:00:00 2001 From: vishal Date: Fri, 27 Oct 2023 11:22:55 +0530 Subject: [PATCH 015/130] fix: minor issue --- .../report/accounts_receivable/accounts_receivable.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index dfe801c73b..39dfd296e3 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -824,7 +824,11 @@ class ReceivablePayableReport(object): if self.filters.get("customer_group"): groups = get_customer_group_with_children(self.filters.customer_group) - customers = qb.from_(self.customer).select(self.customer.name).where(self.customer['customer_group'].isin(groups)) + customers = ( + qb.from_(self.customer) + .select(self.customer.name) + .where(self.customer["customer_group"].isin(groups)) + ) self.qb_selection_filter.append(self.ple.party.isin(customers)) if self.filters.get("territory"): @@ -1118,6 +1122,7 @@ class ReceivablePayableReport(object): ) self.err_journals = [x[0] for x in results] if results else [] + def get_customer_group_with_children(customer_groups): if not isinstance(customer_groups, list): customer_groups = [d.strip() for d in customer_groups.strip().split(",") if d] From e72afd0bd6c0b6100834888329f5bf985a826b65 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Sun, 29 Oct 2023 17:48:48 +0100 Subject: [PATCH 016/130] fix: make project page translatable --- erpnext/templates/pages/projects.html | 44 +++++++++++++-------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/erpnext/templates/pages/projects.html b/erpnext/templates/pages/projects.html index 9fe4338477..3b8698f4ab 100644 --- a/erpnext/templates/pages/projects.html +++ b/erpnext/templates/pages/projects.html @@ -14,18 +14,16 @@ {% block style %} {% endblock %} {% block page_content %}
-
Status: {{ doc.status }}
-
Progress: {{ doc.percent_complete }}%
-
Hours Spent: {{ doc.actual_time | round }}
+
{{ _("Status") }}: {{ _(doc.status) }}
+
{{ _("Progress") }}: {{ doc.get_formatted("percent_complete") }}
+
{{ _("Hours Spent") }}: {{ doc.get_formatted("actual_time") }}
@@ -34,7 +32,7 @@
- + @@ -44,39 +42,39 @@
-
Tasks
-
Status
-
End Date
-
Assignment
-
Modified On
+
{{ _("Tasks") }}
+
{{ _("Status") }}
+
{{ _("End Date") }}
+
{{ _("Assignment") }}
+
{{ _("Modified On") }}
{% include "erpnext/templates/includes/projects/project_tasks.html" %}
{% else %} - {{ empty_state('Task')}} + {{ empty_state(_("Task")) }} {% endif %} - + {% if doc.timesheets %}
-
Timesheet
-
Status
-
From
-
To
-
Modified By
-
Modified On
+
{{ _("Timesheet") }}
+
{{ _("Status") }}
+
{{ _("From") }}
+
{{ _("To") }}
+
{{ _("Modified By") }}
+
{{ _("Modified On") }}
{% include "erpnext/templates/includes/projects/project_timesheets.html" %}
{% else %} - {{ empty_state('Timesheet')}} + {{ empty_state(_("Timesheet")) }} {% endif %} {% if doc.attachments %} @@ -113,7 +111,7 @@ {% macro progress_bar(percent_complete) %} {% if percent_complete %} - Project Progress: + {{ _("Project Progress:") }}
Generic Empty State
-

You haven't created a {{ section_name }} yet

+

{{ _("You haven't created a {0} yet").format(section_name) }}

From ece704939055d2998a93701614dce5c4eea9dd76 Mon Sep 17 00:00:00 2001 From: Richard Case Date: Mon, 30 Oct 2023 00:15:05 +0000 Subject: [PATCH 017/130] feat: in_party_currency option for AR/AP reports --- .../accounts_payable/accounts_payable.js | 5 +++++ .../accounts_receivable.js | 5 +++++ .../accounts_receivable.py | 22 +++++++++++-------- 3 files changed, 23 insertions(+), 9 deletions(-) mode change 100755 => 100644 erpnext/accounts/report/accounts_receivable/accounts_receivable.py diff --git a/erpnext/accounts/report/accounts_payable/accounts_payable.js b/erpnext/accounts/report/accounts_payable/accounts_payable.js index 9c73cbb344..8fa81cd9dc 100644 --- a/erpnext/accounts/report/accounts_payable/accounts_payable.js +++ b/erpnext/accounts/report/accounts_payable/accounts_payable.js @@ -143,6 +143,11 @@ frappe.query_reports["Accounts Payable"] = { "fieldname": "show_future_payments", "label": __("Show Future Payments"), "fieldtype": "Check", + }, + { + "fieldname": "in_party_currency", + "label": __("In Party Currency"), + "fieldtype": "Check", } ], diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js index 1073be0bdc..c49b67a727 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js @@ -172,6 +172,11 @@ frappe.query_reports["Accounts Receivable"] = { "fieldname": "show_remarks", "label": __("Show Remarks"), "fieldtype": "Check", + }, + { + "fieldname": "in_party_currency", + "label": __("In Party Currency"), + "fieldtype": "Check", } ], diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py old mode 100755 new mode 100644 index b9c7a0bfb8..e8cf91507b --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -28,8 +28,8 @@ from erpnext.accounts.utils import get_currency_precision # 6. Configurable Ageing Groups (0-30, 30-60 etc) can be set via filters # 7. For overpayment against an invoice with payment terms, there will be an additional row # 8. Invoice details like Sales Persons, Delivery Notes are also fetched comma separated -# 9. Report amounts are in "Party Currency" if party is selected, or company currency for multi-party -# 10. This reports is based on all GL Entries that are made against account_type "Receivable" or "Payable" +# 9. Report amounts are in party currency if in_party_currency is selected, otherwise company currency +# 10. This report is based on Payment Ledger Entries def execute(filters=None): @@ -84,6 +84,9 @@ class ReceivablePayableReport(object): self.total_row_map = {} self.skip_total_row = 1 + if self.filters.get("in_party_currency"): + self.skip_total_row = 1 + def get_data(self): self.get_ple_entries() self.get_sales_invoices_or_customers_based_on_sales_person() @@ -140,7 +143,7 @@ class ReceivablePayableReport(object): if self.filters.get("group_by_party"): self.init_subtotal_row(ple.party) - if self.filters.get("group_by_party"): + if self.filters.get("group_by_party") and not self.filters.get("in_party_currency"): self.init_subtotal_row("Total") def get_invoices(self, ple): @@ -210,8 +213,7 @@ class ReceivablePayableReport(object): if not row: return - # amount in "Party Currency", if its supplied. If not, amount in company currency - if self.filters.get("party_type") and self.filters.get("party"): + if self.filters.get("in_party_currency"): amount = ple.amount_in_account_currency else: amount = ple.amount @@ -242,8 +244,10 @@ class ReceivablePayableReport(object): def update_sub_total_row(self, row, party): total_row = self.total_row_map.get(party) - for field in self.get_currency_fields(): - total_row[field] += row.get(field, 0.0) + if total_row: + for field in self.get_currency_fields(): + total_row[field] += row.get(field, 0.0) + total_row["currency"] = row.get("currency", "") def append_subtotal_row(self, party): sub_total_row = self.total_row_map.get(party) @@ -295,7 +299,7 @@ class ReceivablePayableReport(object): if self.filters.get("group_by_party"): self.append_subtotal_row(self.previous_party) if self.data: - self.data.append(self.total_row_map.get("Total")) + self.data.append(self.total_row_map.get("Total", {})) def append_row(self, row): self.allocate_future_payments(row) @@ -426,7 +430,7 @@ class ReceivablePayableReport(object): party_details = self.get_party_details(row.party) or {} row.update(party_details) - if self.filters.get("party_type") and self.filters.get("party"): + if self.filters.get("in_party_currency"): row.currency = row.account_currency else: row.currency = self.company_currency From 208d5042ee96c01eec19ed54bfbc609a08e6de16 Mon Sep 17 00:00:00 2001 From: Richard Case Date: Mon, 30 Oct 2023 00:45:02 +0000 Subject: [PATCH 018/130] chore: update tests --- .../accounts/report/accounts_payable/test_accounts_payable.py | 1 + .../report/accounts_receivable/test_accounts_receivable.py | 1 + 2 files changed, 2 insertions(+) diff --git a/erpnext/accounts/report/accounts_payable/test_accounts_payable.py b/erpnext/accounts/report/accounts_payable/test_accounts_payable.py index 9f03d92cd5..b4cb25ff1b 100644 --- a/erpnext/accounts/report/accounts_payable/test_accounts_payable.py +++ b/erpnext/accounts/report/accounts_payable/test_accounts_payable.py @@ -40,6 +40,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): "range2": 60, "range3": 90, "range4": 120, + "in_party_currency": 1, } data = execute(filters) diff --git a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py index cbeb6d3106..04a4e8038e 100644 --- a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py @@ -557,6 +557,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): "range2": 60, "range3": 90, "range4": 120, + "in_party_currency": 1, } si = self.create_sales_invoice(no_payment_schedule=True, do_not_submit=True) From de58c679918229c15925cd3257cc5ddbc7c20473 Mon Sep 17 00:00:00 2001 From: viralkansodiya15 <98073516+viralpatel15@users.noreply.github.com> Date: Mon, 30 Oct 2023 10:44:17 +0530 Subject: [PATCH 019/130] fix: linter test solve --- .../landed_cost_voucher/landed_cost_voucher.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py index 5e35c16365..69b4cc1cf1 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py @@ -204,10 +204,14 @@ class LandedCostVoucher(Document): "purchase_invoice" if item.receipt_document_type == "Purchase Invoice" else "purchase_receipt" ) docs = frappe.db.get_all( - "Asset", - filters={receipt_document_type: item.receipt_document, "item_code": item.item_code, "docstatus":['!=', 2]}, - fields=["name", "docstatus"], - ) + "Asset", ++ filters={ ++ receipt_document_type: item.receipt_document, ++ "item_code": item.item_code, ++ "docstatus": ["!=", 2], ++ }, + fields=["name", "docstatus"], + ) if not docs or len(docs) != item.qty: frappe.throw( _( From a15484fe3d38923b2e4ae3128b475ea1b6fa364c Mon Sep 17 00:00:00 2001 From: viralkansodiya15 <98073516+viralpatel15@users.noreply.github.com> Date: Mon, 30 Oct 2023 13:01:25 +0530 Subject: [PATCH 020/130] fix: solve linter test and update --- .../landed_cost_voucher/landed_cost_voucher.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py index 69b4cc1cf1..8bbc660899 100644 --- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py +++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py @@ -204,14 +204,14 @@ class LandedCostVoucher(Document): "purchase_invoice" if item.receipt_document_type == "Purchase Invoice" else "purchase_receipt" ) docs = frappe.db.get_all( - "Asset", -+ filters={ -+ receipt_document_type: item.receipt_document, -+ "item_code": item.item_code, -+ "docstatus": ["!=", 2], -+ }, - fields=["name", "docstatus"], - ) + "Asset", + filters={ + receipt_document_type: item.receipt_document, + "item_code": item.item_code, + "docstatus": ["!=", 2], + }, + fields=["name", "docstatus"], + ) if not docs or len(docs) != item.qty: frappe.throw( _( From 056b74b162ed58dd979cd9748129752fb8cab242 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 31 Oct 2023 12:32:52 +0530 Subject: [PATCH 021/130] fix: indexing on Delivery Note Item (#37766) fix: added indexing on Delivery Note Item --- .../doctype/purchase_receipt_item/purchase_receipt_item.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json index f5240a6094..718f007577 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -900,7 +900,8 @@ "label": "Delivery Note Item", "no_copy": 1, "print_hide": 1, - "read_only": 1 + "read_only": 1, + "search_index": 1 }, { "collapsible": 1, @@ -1089,7 +1090,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2023-10-19 10:50:58.071735", + "modified": "2023-10-30 17:32:24.560337", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", From d758fc1b8910dfa339f82edf860722414fbfe242 Mon Sep 17 00:00:00 2001 From: "mergify[bot]" <37929162+mergify[bot]@users.noreply.github.com> Date: Tue, 31 Oct 2023 16:28:27 +0530 Subject: [PATCH 022/130] fix: incorrect material request quantity in production plan (backport #37785) (#37789) fix: incorrect material request quantity in production plan (#37785) (cherry picked from commit 25718d9f1b7cda3b87263c2cf885958cbd283947) Co-authored-by: rohitwaghchaure --- .../production_plan/production_plan.py | 5 +++- .../production_plan/test_production_plan.py | 27 +++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index ddd9375211..1850d1e09e 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -1735,7 +1735,10 @@ def get_raw_materials_of_sub_assembly_items( if not item.conversion_factor and item.purchase_uom: item.conversion_factor = get_uom_conversion_factor(item.item_code, item.purchase_uom) - item_details.setdefault(item.get("item_code"), item) + if details := item_details.get(item.get("item_code")): + details.qty += item.get("qty") + else: + item_details.setdefault(item.get("item_code"), item) return item_details diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py index 6ab9232788..d414988f41 100644 --- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py @@ -1332,6 +1332,33 @@ class TestProductionPlan(FrappeTestCase): self.assertTrue(row.warehouse == mrp_warhouse) self.assertEqual(row.quantity, 12) + def test_mr_qty_for_same_rm_with_different_sub_assemblies(self): + from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom + + bom_tree = { + "Fininshed Goods2 For SUB Test": { + "SubAssembly2 For SUB Test": {"ChildPart2 For SUB Test": {}}, + "SubAssembly3 For SUB Test": {"ChildPart2 For SUB Test": {}}, + } + } + + parent_bom = create_nested_bom(bom_tree, prefix="") + plan = create_production_plan( + item_code=parent_bom.item, + planned_qty=1, + ignore_existing_ordered_qty=1, + do_not_submit=1, + skip_available_sub_assembly_item=1, + warehouse="_Test Warehouse - _TC", + ) + + plan.get_sub_assembly_items() + plan.make_material_request() + + for row in plan.mr_items: + if row.item_code == "ChildPart2 For SUB Test": + self.assertEqual(row.quantity, 2) + def create_production_plan(**args): """ From 139a68fd0f1c1d8893b1f121c13781e25b0a2629 Mon Sep 17 00:00:00 2001 From: David Arnold Date: Tue, 31 Oct 2023 12:20:09 +0100 Subject: [PATCH 023/130] test: fix bad test data (#37773) * test(stock): fix bad test data * test(asset): fix bad test data * test(stock): fix bad test data * test(manufacturing): fix bad test data --- .../test_asset_maintenance.py | 139 ++++-------------- .../asset_maintenance/test_records.json | 68 +++++++++ .../doctype/work_order/test_work_order.py | 1 + .../test_records.json | 4 + .../test_repost_item_valuation.py | 4 - 5 files changed, 100 insertions(+), 116 deletions(-) create mode 100644 erpnext/assets/doctype/asset_maintenance/test_records.json diff --git a/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py b/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py index 23088c9ccf..eac875896b 100644 --- a/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py +++ b/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py @@ -13,25 +13,22 @@ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_pu class TestAssetMaintenance(unittest.TestCase): def setUp(self): set_depreciation_settings_in_company() - create_asset_data() - create_maintenance_team() - - def test_create_asset_maintenance(self): - pr = make_purchase_receipt( + self.pr = make_purchase_receipt( item_code="Photocopier", qty=1, rate=100000.0, location="Test Location" ) + self.asset_name = frappe.db.get_value("Asset", {"purchase_receipt": self.pr.name}, "name") + self.asset_doc = frappe.get_doc("Asset", self.asset_name) - asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, "name") - asset_doc = frappe.get_doc("Asset", asset_name) + def test_create_asset_maintenance_with_log(self): month_end_date = get_last_day(nowdate()) purchase_date = nowdate() if nowdate() != month_end_date else add_days(nowdate(), -15) - asset_doc.available_for_use_date = purchase_date - asset_doc.purchase_date = purchase_date + self.asset_doc.available_for_use_date = purchase_date + self.asset_doc.purchase_date = purchase_date - asset_doc.calculate_depreciation = 1 - asset_doc.append( + self.asset_doc.calculate_depreciation = 1 + self.asset_doc.append( "finance_books", { "expected_value_after_useful_life": 200, @@ -42,97 +39,32 @@ class TestAssetMaintenance(unittest.TestCase): }, ) - asset_doc.save() + self.asset_doc.save() - if not frappe.db.exists("Asset Maintenance", "Photocopier"): - asset_maintenance = frappe.get_doc( - { - "doctype": "Asset Maintenance", - "asset_name": "Photocopier", - "maintenance_team": "Team Awesome", - "company": "_Test Company", - "asset_maintenance_tasks": get_maintenance_tasks(), - } - ).insert() + asset_maintenance = frappe.get_doc( + { + "doctype": "Asset Maintenance", + "asset_name": self.asset_name, + "maintenance_team": "Team Awesome", + "company": "_Test Company", + "asset_maintenance_tasks": get_maintenance_tasks(), + } + ).insert() - next_due_date = calculate_next_due_date(nowdate(), "Monthly") - self.assertEqual(asset_maintenance.asset_maintenance_tasks[0].next_due_date, next_due_date) - - def test_create_asset_maintenance_log(self): - if not frappe.db.exists("Asset Maintenance Log", "Photocopier"): - asset_maintenance_log = frappe.get_doc( - { - "doctype": "Asset Maintenance Log", - "asset_maintenance": "Photocopier", - "task": "Change Oil", - "completion_date": add_days(nowdate(), 2), - "maintenance_status": "Completed", - } - ).insert() - asset_maintenance = frappe.get_doc("Asset Maintenance", "Photocopier") - next_due_date = calculate_next_due_date(asset_maintenance_log.completion_date, "Monthly") + next_due_date = calculate_next_due_date(nowdate(), "Monthly") self.assertEqual(asset_maintenance.asset_maintenance_tasks[0].next_due_date, next_due_date) - -def create_asset_data(): - if not frappe.db.exists("Asset Category", "Equipment"): - create_asset_category() - - if not frappe.db.exists("Location", "Test Location"): - frappe.get_doc({"doctype": "Location", "location_name": "Test Location"}).insert() - - if not frappe.db.exists("Item", "Photocopier"): - meta = frappe.get_meta("Asset") - naming_series = meta.get_field("naming_series").options - frappe.get_doc( + asset_maintenance_log = frappe.get_doc( { - "doctype": "Item", - "item_code": "Photocopier", - "item_name": "Photocopier", - "item_group": "All Item Groups", - "company": "_Test Company", - "is_fixed_asset": 1, - "is_stock_item": 0, - "asset_category": "Equipment", - "auto_create_assets": 1, - "asset_naming_series": naming_series, + "doctype": "Asset Maintenance Log", + "asset_maintenance": self.asset_name, + "task": "Change Oil", + "completion_date": add_days(nowdate(), 2), + "maintenance_status": "Completed", } ).insert() - - -def create_maintenance_team(): - user_list = ["marcus@abc.com", "thalia@abc.com", "mathias@abc.com"] - if not frappe.db.exists("Role", "Technician"): - frappe.get_doc({"doctype": "Role", "role_name": "Technician"}).insert() - for user in user_list: - if not frappe.db.get_value("User", user): - frappe.get_doc( - { - "doctype": "User", - "email": user, - "first_name": user, - "new_password": "password", - "roles": [{"doctype": "Has Role", "role": "Technician"}], - } - ).insert() - - if not frappe.db.exists("Asset Maintenance Team", "Team Awesome"): - frappe.get_doc( - { - "doctype": "Asset Maintenance Team", - "maintenance_manager": "marcus@abc.com", - "maintenance_team_name": "Team Awesome", - "company": "_Test Company", - "maintenance_team_members": get_maintenance_team(user_list), - } - ).insert() - - -def get_maintenance_team(user_list): - return [ - {"team_member": user, "full_name": user, "maintenance_role": "Technician"} - for user in user_list[1:] - ] + next_due_date = calculate_next_due_date(asset_maintenance_log.completion_date, "Monthly") + self.assertEqual(asset_maintenance.asset_maintenance_tasks[0].next_due_date, next_due_date) def get_maintenance_tasks(): @@ -156,23 +88,6 @@ def get_maintenance_tasks(): ] -def create_asset_category(): - asset_category = frappe.new_doc("Asset Category") - asset_category.asset_category_name = "Equipment" - asset_category.total_number_of_depreciations = 3 - asset_category.frequency_of_depreciation = 3 - asset_category.append( - "accounts", - { - "company_name": "_Test Company", - "fixed_asset_account": "_Test Fixed Asset - _TC", - "accumulated_depreciation_account": "_Test Accumulated Depreciations - _TC", - "depreciation_expense_account": "_Test Depreciations - _TC", - }, - ) - asset_category.insert() - - def set_depreciation_settings_in_company(): company = frappe.get_doc("Company", "_Test Company") company.accumulated_depreciation_account = "_Test Accumulated Depreciations - _TC" diff --git a/erpnext/assets/doctype/asset_maintenance/test_records.json b/erpnext/assets/doctype/asset_maintenance/test_records.json new file mode 100644 index 0000000000..8306fad6cb --- /dev/null +++ b/erpnext/assets/doctype/asset_maintenance/test_records.json @@ -0,0 +1,68 @@ +[ + { + "doctype": "Asset Category", + "asset_category_name": "Equipment", + "total_number_of_depreciations": 3, + "frequency_of_depreciation": 3, + "accounts": [ + { + "company_name": "_Test Company", + "fixed_asset_account": "_Test Fixed Asset - _TC", + "accumulated_depreciation_account": "_Test Accumulated Depreciations - _TC", + "depreciation_expense_account": "_Test Depreciations - _TC" + } + ] + }, + { + "doctype": "Location", + "location_name": "Test Location" + }, + { + "doctype": "Role", + "role_name": "Technician" + }, + { + "doctype": "User", + "email": "marcus@abc.com", + "first_name": "marcus@abc.com", + "new_password": "password", + "roles": [{"doctype": "Has Role", "role": "Technician"}] + }, + { + "doctype": "User", + "email": "thalia@abc.com", + "first_name": "thalia@abc.com", + "new_password": "password", + "roles": [{"doctype": "Has Role", "role": "Technician"}] + }, + { + "doctype": "User", + "email": "mathias@abc.com", + "first_name": "mathias@abc.com", + "new_password": "password", + "roles": [{"doctype": "Has Role", "role": "Technician"}] + }, + { + "doctype": "Asset Maintenance Team", + "maintenance_manager": "marcus@abc.com", + "maintenance_team_name": "Team Awesome", + "company": "_Test Company", + "maintenance_team_members": [ + {"team_member": "marcus@abc.com", "full_name": "marcus@abc.com", "maintenance_role": "Technician"}, + {"team_member": "thalia@abc.com", "full_name": "thalia@abc.com", "maintenance_role": "Technician"}, + {"team_member": "mathias@abc.com", "full_name": "mathias@abc.com", "maintenance_role": "Technician"} + ] + }, + { + "doctype": "Item", + "item_code": "Photocopier", + "item_name": "Photocopier", + "item_group": "All Item Groups", + "company": "_Test Company", + "is_fixed_asset": 1, + "is_stock_item": 0, + "asset_category": "Equipment", + "auto_create_assets": 1, + "asset_naming_series": "ABC.###" + } +] diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py index c828c878eb..0ae7657c42 100644 --- a/erpnext/manufacturing/doctype/work_order/test_work_order.py +++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py @@ -494,6 +494,7 @@ class TestWorkOrder(FrappeTestCase): "from_time": row.from_time, "to_time": row.to_time, "time_in_mins": row.time_in_mins, + "completed_qty": 0, }, ) diff --git a/erpnext/stock/doctype/quality_inspection_template/test_records.json b/erpnext/stock/doctype/quality_inspection_template/test_records.json index 980f49a80a..2da99f616a 100644 --- a/erpnext/stock/doctype/quality_inspection_template/test_records.json +++ b/erpnext/stock/doctype/quality_inspection_template/test_records.json @@ -1,4 +1,8 @@ [ + { + "doctype": "Quality Inspection Parameter", + "parameter" : "_Test Param" + }, { "quality_inspection_template_name" : "_Test Quality Inspection Template", "doctype": "Quality Inspection Template", diff --git a/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py index 1853f45f58..623e8fafe9 100644 --- a/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py +++ b/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py @@ -137,8 +137,6 @@ class TestRepostItemValuation(FrappeTestCase, StockTestMixin): item_code="_Test Item", warehouse="_Test Warehouse - _TC", based_on="Item and Warehouse", - voucher_type="Sales Invoice", - voucher_no="SI-1", posting_date="2021-01-02", posting_time="00:01:00", ) @@ -148,8 +146,6 @@ class TestRepostItemValuation(FrappeTestCase, StockTestMixin): riv1.flags.dont_run_in_test = True riv1.submit() _assert_status(riv1, "Queued") - self.assertEqual(riv1.voucher_type, "Sales Invoice") # traceability - self.assertEqual(riv1.voucher_no, "SI-1") # newer than existing duplicate - riv1 riv2 = frappe.get_doc(riv_args.update({"posting_date": "2021-01-03"})) From eb73017798eed964e8f019db4cef3513162855f6 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 31 Oct 2023 17:30:42 +0530 Subject: [PATCH 024/130] refactor: pull remarks only if needed on AR/AP report --- .../report/accounts_receivable/accounts_receivable.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index b9c7a0bfb8..20444f9496 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -718,6 +718,7 @@ class ReceivablePayableReport(object): query = ( qb.from_(ple) .select( + ple.name, ple.account, ple.voucher_type, ple.voucher_no, @@ -731,13 +732,15 @@ class ReceivablePayableReport(object): ple.account_currency, ple.amount, ple.amount_in_account_currency, - ple.remarks, ) .where(ple.delinked == 0) .where(Criterion.all(self.qb_selection_filter)) .where(Criterion.any(self.or_filters)) ) + if self.filters.get("show_remarks"): + query = query.select(ple.remarks) + if self.filters.get("group_by_party"): query = query.orderby(self.ple.party, self.ple.posting_date) else: From fb0ec74d086085160fbcca9ccfffb6966673e0cb Mon Sep 17 00:00:00 2001 From: David Arnold Date: Tue, 31 Oct 2023 13:11:44 +0100 Subject: [PATCH 025/130] fix(packed_item): ensure proper names for ref integrity (#37597) --- erpnext/selling/doctype/sales_order/sales_order.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 2f6578ea16..a40cde12f8 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -759,6 +759,8 @@ def make_delivery_note(source_name, target_doc=None, kwargs=None): if target.company_address: target.update(get_fetch_values("Delivery Note", "company_address", target.company_address)) + # set target items names to ensure proper linking with packed_items + target.set_new_name() make_packing_list(target) def condition(doc): From 1fd888175f2f224bcebba3e9a958d672ab09bcd5 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 31 Oct 2023 17:47:02 +0530 Subject: [PATCH 026/130] chore: update default limit values in reconciliation tool --- erpnext/patches.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/patches.txt b/erpnext/patches.txt index d7f33adeea..78d2c2c340 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -343,5 +343,7 @@ erpnext.patches.v14_0.create_accounting_dimensions_in_sales_order_item erpnext.patches.v15_0.update_sre_from_voucher_details erpnext.patches.v14_0.rename_over_order_allowance_field erpnext.patches.v14_0.migrate_delivery_stop_lock_field +execute:frappe.db.set_single_value("Payment Reconciliation", "invoice_limit", 50) +execute:frappe.db.set_single_value("Payment Reconciliation", "payment_limit", 50) # below migration patch should always run last erpnext.patches.v14_0.migrate_gl_to_payment_ledger From 77af247450fc6901ae49ef6109073bb4f637113c Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 31 Oct 2023 18:02:24 +0530 Subject: [PATCH 027/130] chore: fixed test cases (#37792) --- .../test_asset_maintenance.py | 20 +++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py b/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py index eac875896b..a33acfd833 100644 --- a/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py +++ b/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py @@ -54,16 +54,24 @@ class TestAssetMaintenance(unittest.TestCase): next_due_date = calculate_next_due_date(nowdate(), "Monthly") self.assertEqual(asset_maintenance.asset_maintenance_tasks[0].next_due_date, next_due_date) - asset_maintenance_log = frappe.get_doc( + asset_maintenance_log = frappe.db.get_value( + "Asset Maintenance Log", + {"asset_maintenance": asset_maintenance.name, "task_name": "Change Oil"}, + "name", + ) + + asset_maintenance_log_doc = frappe.get_doc("Asset Maintenance Log", asset_maintenance_log) + asset_maintenance_log_doc.update( { - "doctype": "Asset Maintenance Log", - "asset_maintenance": self.asset_name, - "task": "Change Oil", "completion_date": add_days(nowdate(), 2), "maintenance_status": "Completed", } - ).insert() - next_due_date = calculate_next_due_date(asset_maintenance_log.completion_date, "Monthly") + ) + + asset_maintenance_log_doc.save() + next_due_date = calculate_next_due_date(asset_maintenance_log_doc.completion_date, "Monthly") + + asset_maintenance.reload() self.assertEqual(asset_maintenance.asset_maintenance_tasks[0].next_due_date, next_due_date) From daf2ec063c4b0313e32e247459d0cb8dc6958833 Mon Sep 17 00:00:00 2001 From: hyaray Date: Tue, 31 Oct 2023 21:21:27 +0800 Subject: [PATCH 028/130] fix: In-Transit Warehouse company filter (#37796) --- erpnext/setup/doctype/company/company.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index 23b93dc161..1bd469b956 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -40,7 +40,7 @@ frappe.ui.form.on("Company", { filters:{ 'warehouse_type' : 'Transit', 'is_group': 0, - 'company': frm.doc.company + 'company': frm.doc.company_name } }; }); From e16cc38b70ef39f8f43a85eb555fbb05cdadee6e Mon Sep 17 00:00:00 2001 From: Aadhil <36843795+aadhilpm@users.noreply.github.com> Date: Sun, 29 Oct 2023 12:47:15 +0300 Subject: [PATCH 029/130] fix: remove GoCardless Settings and Mpesa Settings from Workspace --- .../erpnext_integrations.json | 22 +------------------ 1 file changed, 1 insertion(+), 21 deletions(-) diff --git a/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json b/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json index 510317f5c2..dfef223c43 100644 --- a/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json +++ b/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json @@ -192,26 +192,6 @@ "onboard": 0, "type": "Card Break" }, - { - "hidden": 0, - "is_query_report": 0, - "label": "GoCardless Settings", - "link_count": 0, - "link_to": "GoCardless Settings", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Mpesa Settings", - "link_count": 0, - "link_to": "Mpesa Settings", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, { "hidden": 0, "is_query_report": 0, @@ -223,7 +203,7 @@ "type": "Link" } ], - "modified": "2023-08-29 15:48:59.010704", + "modified": "2023-10-31 19:57:32.748726", "modified_by": "Administrator", "module": "ERPNext Integrations", "name": "ERPNext Integrations", From e0a03789aef1891273ed6fe5a83b182d3f76e1b7 Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 31 Oct 2023 16:53:08 +0100 Subject: [PATCH 030/130] fix: Use `process.extract` to get the corresponding party doc name of the result - rapidfuzz accepts an iterable or a dict. dict input gives the dict key and value in the result --- .../doctype/bank_transaction/auto_match_party.py | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/doctype/bank_transaction/auto_match_party.py b/erpnext/accounts/doctype/bank_transaction/auto_match_party.py index 671cde58a9..04dab4c28a 100644 --- a/erpnext/accounts/doctype/bank_transaction/auto_match_party.py +++ b/erpnext/accounts/doctype/bank_transaction/auto_match_party.py @@ -1,7 +1,6 @@ from typing import Tuple, Union import frappe -from frappe.core.utils import find from frappe.utils import flt from rapidfuzz import fuzz, process @@ -135,7 +134,7 @@ class AutoMatchbyPartyNameDescription: skip = False result = process.extract( query=self.get(field), - choices=[name.get("party_name") for name in names], + choices={row.get("name"): row.get("party_name") for row in names}, scorer=fuzz.token_set_ratio, ) party_name, skip = self.process_fuzzy_result(result) @@ -143,8 +142,6 @@ class AutoMatchbyPartyNameDescription: if not party_name: return None, skip - # Get Party Docname from the list of dicts - party_name = find(names, lambda x: x["party_name"] == party_name).get("name") return ( party, party_name, @@ -157,14 +154,14 @@ class AutoMatchbyPartyNameDescription: Returns: Result, Skip (whether or not to discontinue matching) """ - PARTY, SCORE, CUTOFF = 0, 1, 80 + SCORE, PARTY_ID, CUTOFF = 1, 2, 80 if not result or not len(result): return None, False first_result = result[0] if len(result) == 1: - return (first_result[PARTY] if first_result[SCORE] > CUTOFF else None), True + return (first_result[PARTY_ID] if first_result[SCORE] > CUTOFF else None), True second_result = result[1] if first_result[SCORE] > CUTOFF: @@ -173,7 +170,7 @@ class AutoMatchbyPartyNameDescription: if first_result[SCORE] == second_result[SCORE]: return None, True - return first_result[PARTY], True + return first_result[PARTY_ID], True else: return None, False From 028b3e2fbf52cdb7561e8d63b54d5918b5bc8af4 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Wed, 1 Nov 2023 11:48:27 +0530 Subject: [PATCH 031/130] fix: `TypeError` in PR for non-stock item --- erpnext/stock/doctype/purchase_receipt/purchase_receipt.py | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index 2a4b6f34b5..cbc1693eaa 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -600,11 +600,10 @@ class PurchaseReceipt(BuyingController): make_rate_difference_entry(d) make_sub_contracting_gl_entries(d) make_divisional_loss_gl_entry(d, outgoing_amount) - elif ( - d.warehouse not in warehouse_with_no_account - or d.rejected_warehouse not in warehouse_with_no_account + elif (d.warehouse and d.warehouse not in warehouse_with_no_account) or ( + d.rejected_warehouse and d.rejected_warehouse not in warehouse_with_no_account ): - warehouse_with_no_account.append(d.warehouse) + warehouse_with_no_account.append(d.warehouse or d.rejected_warehouse) if d.is_fixed_asset: self.update_assets(d, d.valuation_rate) From 7e67d42d1d1837e47a5df3b1e6e22a72c996d761 Mon Sep 17 00:00:00 2001 From: mrchenxxx <44868578+mrchenxxx@users.noreply.github.com> Date: Wed, 1 Nov 2023 17:55:36 +0800 Subject: [PATCH 032/130] chore: Update translations chore: Update translations --- erpnext/translations/zh.csv | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/translations/zh.csv b/erpnext/translations/zh.csv index 1d8a261005..cf89dc6852 100644 --- a/erpnext/translations/zh.csv +++ b/erpnext/translations/zh.csv @@ -8743,3 +8743,4 @@ WhatsApp,WhatsApp的, Make a call,打个电话, Approve,同意, Reject,拒绝, +Stock,库存, From ec1a7869f82c9bb3d5e12e8a9dc695823e502a4a Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Wed, 1 Nov 2023 15:35:18 +0530 Subject: [PATCH 033/130] refactor: rearrange fields and update label --- erpnext/stock/doctype/bin/bin.json | 53 ++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 14 deletions(-) diff --git a/erpnext/stock/doctype/bin/bin.json b/erpnext/stock/doctype/bin/bin.json index a11572776a..02371cf90f 100644 --- a/erpnext/stock/doctype/bin/bin.json +++ b/erpnext/stock/doctype/bin/bin.json @@ -5,22 +5,27 @@ "doctype": "DocType", "engine": "InnoDB", "field_order": [ - "warehouse", "item_code", - "reserved_qty", + "column_break_yreo", + "warehouse", + "section_break_stag", "actual_qty", - "ordered_qty", - "indented_qty", "planned_qty", - "projected_qty", + "indented_qty", + "ordered_qty", + "column_break_xn5j", + "reserved_qty", "reserved_qty_for_production", "reserved_qty_for_sub_contract", "reserved_qty_for_production_plan", - "ma_rate", + "projected_qty", + "section_break_pmrs", "stock_uom", - "fcfs_rate", + "column_break_0slj", "valuation_rate", - "stock_value" + "stock_value", + "fcfs_rate", + "ma_rate" ], "fields": [ { @@ -56,7 +61,7 @@ "fieldname": "reserved_qty", "fieldtype": "Float", "in_list_view": 1, - "label": "Reserved Quantity", + "label": "Reserved Qty", "oldfieldname": "reserved_qty", "oldfieldtype": "Currency", "read_only": 1 @@ -67,7 +72,7 @@ "fieldtype": "Float", "in_filter": 1, "in_list_view": 1, - "label": "Actual Quantity", + "label": "Actual Qty", "oldfieldname": "actual_qty", "oldfieldtype": "Currency", "read_only": 1 @@ -77,7 +82,7 @@ "fieldname": "ordered_qty", "fieldtype": "Float", "in_list_view": 1, - "label": "Ordered Quantity", + "label": "Ordered Qty", "oldfieldname": "ordered_qty", "oldfieldtype": "Currency", "read_only": 1 @@ -86,7 +91,7 @@ "default": "0.00", "fieldname": "indented_qty", "fieldtype": "Float", - "label": "Requested Quantity", + "label": "Requested Qty", "oldfieldname": "indented_qty", "oldfieldtype": "Currency", "read_only": 1 @@ -116,7 +121,7 @@ { "fieldname": "reserved_qty_for_sub_contract", "fieldtype": "Float", - "label": "Reserved Qty for sub contract", + "label": "Reserved Qty for Subcontract", "read_only": 1 }, { @@ -172,13 +177,33 @@ "fieldtype": "Float", "label": "Reserved Qty for Production Plan", "read_only": 1 + }, + { + "fieldname": "section_break_stag", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_yreo", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_xn5j", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_pmrs", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_0slj", + "fieldtype": "Column Break" } ], "hide_toolbar": 1, "idx": 1, "in_create": 1, "links": [], - "modified": "2023-05-02 23:26:21.806965", + "modified": "2023-11-01 15:28:55.240522", "modified_by": "Administrator", "module": "Stock", "name": "Bin", From f0a1f4ac7cb9362e8dfe05179533fe4ad00d061e Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Wed, 1 Nov 2023 15:36:41 +0530 Subject: [PATCH 034/130] refactor: remove unused fields `fcfs_rate` and `ma_rate` from Bin --- erpnext/stock/doctype/bin/bin.json | 28 ++-------------------------- 1 file changed, 2 insertions(+), 26 deletions(-) diff --git a/erpnext/stock/doctype/bin/bin.json b/erpnext/stock/doctype/bin/bin.json index 02371cf90f..02684a7241 100644 --- a/erpnext/stock/doctype/bin/bin.json +++ b/erpnext/stock/doctype/bin/bin.json @@ -23,9 +23,7 @@ "stock_uom", "column_break_0slj", "valuation_rate", - "stock_value", - "fcfs_rate", - "ma_rate" + "stock_value" ], "fields": [ { @@ -124,17 +122,6 @@ "label": "Reserved Qty for Subcontract", "read_only": 1 }, - { - "fieldname": "ma_rate", - "fieldtype": "Float", - "hidden": 1, - "label": "Moving Average Rate", - "oldfieldname": "ma_rate", - "oldfieldtype": "Currency", - "print_hide": 1, - "read_only": 1, - "report_hide": 1 - }, { "fieldname": "stock_uom", "fieldtype": "Link", @@ -145,17 +132,6 @@ "options": "UOM", "read_only": 1 }, - { - "fieldname": "fcfs_rate", - "fieldtype": "Float", - "hidden": 1, - "label": "FCFS Rate", - "oldfieldname": "fcfs_rate", - "oldfieldtype": "Currency", - "print_hide": 1, - "read_only": 1, - "report_hide": 1 - }, { "fieldname": "valuation_rate", "fieldtype": "Float", @@ -203,7 +179,7 @@ "idx": 1, "in_create": 1, "links": [], - "modified": "2023-11-01 15:28:55.240522", + "modified": "2023-11-01 15:35:51.722534", "modified_by": "Administrator", "module": "Stock", "name": "Bin", From 3d9938221a7fef7e08c0072364d8ebb855933d11 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Wed, 1 Nov 2023 15:40:54 +0530 Subject: [PATCH 035/130] fix: remove validation for negative outstanding invoices --- .../doctype/payment_entry/payment_entry.py | 34 +------------------ 1 file changed, 1 insertion(+), 33 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index e6403fddef..f98cd65950 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -83,7 +83,6 @@ class PaymentEntry(AccountsController): self.apply_taxes() self.set_amounts_after_tax() self.clear_unallocated_reference_document_rows() - self.validate_payment_against_negative_invoice() self.validate_transaction_reference() self.set_title() self.set_remarks() @@ -952,35 +951,6 @@ class PaymentEntry(AccountsController): self.name, ) - def validate_payment_against_negative_invoice(self): - if (self.payment_type != "Pay" or self.party_type != "Customer") and ( - self.payment_type != "Receive" or self.party_type != "Supplier" - ): - return - - total_negative_outstanding = sum( - abs(flt(d.outstanding_amount)) for d in self.get("references") if flt(d.outstanding_amount) < 0 - ) - - paid_amount = self.paid_amount if self.payment_type == "Receive" else self.received_amount - additional_charges = sum(flt(d.amount) for d in self.deductions) - - if not total_negative_outstanding: - if self.party_type == "Customer": - msg = _("Cannot pay to Customer without any negative outstanding invoice") - else: - msg = _("Cannot receive from Supplier without any negative outstanding invoice") - - frappe.throw(msg, InvalidPaymentEntry) - - elif paid_amount - additional_charges > total_negative_outstanding: - frappe.throw( - _("Paid Amount cannot be greater than total negative outstanding amount {0}").format( - fmt_money(total_negative_outstanding) - ), - InvalidPaymentEntry, - ) - def set_title(self): if frappe.flags.in_import and self.title: # do not set title dynamically if title exists during data import. @@ -1083,9 +1053,7 @@ class PaymentEntry(AccountsController): item=self, ) - dr_or_cr = ( - "credit" if erpnext.get_party_account_type(self.party_type) == "Receivable" else "debit" - ) + dr_or_cr = "credit" if self.payment_type == "Receive" else "debit" for d in self.get("references"): cost_center = self.cost_center From c5f5aa8208ce2449e626cc0e4cf120f30b0260b9 Mon Sep 17 00:00:00 2001 From: Raffael Meyer <14891507+barredterra@users.noreply.github.com> Date: Wed, 1 Nov 2023 11:19:21 +0100 Subject: [PATCH 036/130] feat: report Timesheet Billing Summary (#37451) --- erpnext/projects/report/billing_summary.py | 155 ------------------ .../employee_billing_summary.js | 34 ---- .../employee_billing_summary.json | 36 ---- .../employee_billing_summary.py | 15 -- .../project_billing_summary/__init__.py | 0 .../project_billing_summary.js | 34 ---- .../project_billing_summary.py | 15 -- .../__init__.py | 0 .../timesheet_billing_summary.js | 67 ++++++++ .../timesheet_billing_summary.json} | 22 ++- .../timesheet_billing_summary.py | 146 +++++++++++++++++ .../projects/workspace/projects/projects.json | 10 +- 12 files changed, 232 insertions(+), 302 deletions(-) delete mode 100644 erpnext/projects/report/billing_summary.py delete mode 100644 erpnext/projects/report/employee_billing_summary/employee_billing_summary.js delete mode 100644 erpnext/projects/report/employee_billing_summary/employee_billing_summary.json delete mode 100644 erpnext/projects/report/employee_billing_summary/employee_billing_summary.py delete mode 100644 erpnext/projects/report/project_billing_summary/__init__.py delete mode 100644 erpnext/projects/report/project_billing_summary/project_billing_summary.js delete mode 100644 erpnext/projects/report/project_billing_summary/project_billing_summary.py rename erpnext/projects/report/{employee_billing_summary => timesheet_billing_summary}/__init__.py (100%) create mode 100644 erpnext/projects/report/timesheet_billing_summary/timesheet_billing_summary.js rename erpnext/projects/report/{project_billing_summary/project_billing_summary.json => timesheet_billing_summary/timesheet_billing_summary.json} (61%) create mode 100644 erpnext/projects/report/timesheet_billing_summary/timesheet_billing_summary.py diff --git a/erpnext/projects/report/billing_summary.py b/erpnext/projects/report/billing_summary.py deleted file mode 100644 index ac1524a49d..0000000000 --- a/erpnext/projects/report/billing_summary.py +++ /dev/null @@ -1,155 +0,0 @@ -# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -from frappe import _ -from frappe.utils import flt, time_diff_in_hours - - -def get_columns(): - return [ - { - "label": _("Employee ID"), - "fieldtype": "Link", - "fieldname": "employee", - "options": "Employee", - "width": 300, - }, - { - "label": _("Employee Name"), - "fieldtype": "data", - "fieldname": "employee_name", - "hidden": 1, - "width": 200, - }, - { - "label": _("Timesheet"), - "fieldtype": "Link", - "fieldname": "timesheet", - "options": "Timesheet", - "width": 150, - }, - {"label": _("Working Hours"), "fieldtype": "Float", "fieldname": "total_hours", "width": 150}, - { - "label": _("Billable Hours"), - "fieldtype": "Float", - "fieldname": "total_billable_hours", - "width": 150, - }, - {"label": _("Billing Amount"), "fieldtype": "Currency", "fieldname": "amount", "width": 150}, - ] - - -def get_data(filters): - data = [] - if filters.from_date > filters.to_date: - frappe.msgprint(_("From Date can not be greater than To Date")) - return data - - timesheets = get_timesheets(filters) - - filters.from_date = frappe.utils.get_datetime(filters.from_date) - filters.to_date = frappe.utils.add_to_date( - frappe.utils.get_datetime(filters.to_date), days=1, seconds=-1 - ) - - timesheet_details = get_timesheet_details(filters, timesheets.keys()) - - for ts, ts_details in timesheet_details.items(): - total_hours = 0 - total_billing_hours = 0 - total_amount = 0 - - for row in ts_details: - from_time, to_time = filters.from_date, filters.to_date - - if row.to_time < from_time or row.from_time > to_time: - continue - - if row.from_time > from_time: - from_time = row.from_time - - if row.to_time < to_time: - to_time = row.to_time - - activity_duration, billing_duration = get_billable_and_total_duration(row, from_time, to_time) - - total_hours += activity_duration - total_billing_hours += billing_duration - total_amount += billing_duration * flt(row.billing_rate) - - if total_hours: - data.append( - { - "employee": timesheets.get(ts).employee, - "employee_name": timesheets.get(ts).employee_name, - "timesheet": ts, - "total_billable_hours": total_billing_hours, - "total_hours": total_hours, - "amount": total_amount, - } - ) - - return data - - -def get_timesheets(filters): - record_filters = [ - ["start_date", "<=", filters.to_date], - ["end_date", ">=", filters.from_date], - ] - if not filters.get("include_draft_timesheets"): - record_filters.append(["docstatus", "=", 1]) - else: - record_filters.append(["docstatus", "!=", 2]) - if "employee" in filters: - record_filters.append(["employee", "=", filters.employee]) - - timesheets = frappe.get_all( - "Timesheet", filters=record_filters, fields=["employee", "employee_name", "name"] - ) - timesheet_map = frappe._dict() - for d in timesheets: - timesheet_map.setdefault(d.name, d) - - return timesheet_map - - -def get_timesheet_details(filters, timesheet_list): - timesheet_details_filter = {"parent": ["in", timesheet_list]} - - if "project" in filters: - timesheet_details_filter["project"] = filters.project - - timesheet_details = frappe.get_all( - "Timesheet Detail", - filters=timesheet_details_filter, - fields=[ - "from_time", - "to_time", - "hours", - "is_billable", - "billing_hours", - "billing_rate", - "parent", - ], - ) - - timesheet_details_map = frappe._dict() - for d in timesheet_details: - timesheet_details_map.setdefault(d.parent, []).append(d) - - return timesheet_details_map - - -def get_billable_and_total_duration(activity, start_time, end_time): - precision = frappe.get_precision("Timesheet Detail", "hours") - activity_duration = time_diff_in_hours(end_time, start_time) - billing_duration = 0.0 - if activity.is_billable: - billing_duration = activity.billing_hours - if activity_duration != activity.billing_hours: - billing_duration = activity_duration * activity.billing_hours / activity.hours - - return flt(activity_duration, precision), flt(billing_duration, precision) diff --git a/erpnext/projects/report/employee_billing_summary/employee_billing_summary.js b/erpnext/projects/report/employee_billing_summary/employee_billing_summary.js deleted file mode 100644 index 2c25465a61..0000000000 --- a/erpnext/projects/report/employee_billing_summary/employee_billing_summary.js +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - - -frappe.query_reports["Employee Billing Summary"] = { - "filters": [ - { - fieldname: "employee", - label: __("Employee"), - fieldtype: "Link", - options: "Employee", - reqd: 1 - }, - { - fieldname:"from_date", - label: __("From Date"), - fieldtype: "Date", - default: frappe.datetime.add_months(frappe.datetime.month_start(), -1), - reqd: 1 - }, - { - fieldname:"to_date", - label: __("To Date"), - fieldtype: "Date", - default: frappe.datetime.add_days(frappe.datetime.month_start(), -1), - reqd: 1 - }, - { - fieldname:"include_draft_timesheets", - label: __("Include Timesheets in Draft Status"), - fieldtype: "Check", - }, - ] -} diff --git a/erpnext/projects/report/employee_billing_summary/employee_billing_summary.json b/erpnext/projects/report/employee_billing_summary/employee_billing_summary.json deleted file mode 100644 index e5626a0206..0000000000 --- a/erpnext/projects/report/employee_billing_summary/employee_billing_summary.json +++ /dev/null @@ -1,36 +0,0 @@ -{ - "add_total_row": 1, - "creation": "2019-03-08 15:08:19.929728", - "disable_prepared_report": 0, - "disabled": 0, - "docstatus": 0, - "doctype": "Report", - "idx": 0, - "is_standard": "Yes", - "modified": "2019-06-13 15:54:49.213973", - "modified_by": "Administrator", - "module": "Projects", - "name": "Employee Billing Summary", - "owner": "Administrator", - "prepared_report": 0, - "ref_doctype": "Timesheet", - "report_name": "Employee Billing Summary", - "report_type": "Script Report", - "roles": [ - { - "role": "Projects User" - }, - { - "role": "HR User" - }, - { - "role": "Manufacturing User" - }, - { - "role": "Employee" - }, - { - "role": "Accounts User" - } - ] -} \ No newline at end of file diff --git a/erpnext/projects/report/employee_billing_summary/employee_billing_summary.py b/erpnext/projects/report/employee_billing_summary/employee_billing_summary.py deleted file mode 100644 index a2f7378d1b..0000000000 --- a/erpnext/projects/report/employee_billing_summary/employee_billing_summary.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe - -from erpnext.projects.report.billing_summary import get_columns, get_data - - -def execute(filters=None): - filters = frappe._dict(filters or {}) - columns = get_columns() - - data = get_data(filters) - return columns, data diff --git a/erpnext/projects/report/project_billing_summary/__init__.py b/erpnext/projects/report/project_billing_summary/__init__.py deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/erpnext/projects/report/project_billing_summary/project_billing_summary.js b/erpnext/projects/report/project_billing_summary/project_billing_summary.js deleted file mode 100644 index fce0c68f11..0000000000 --- a/erpnext/projects/report/project_billing_summary/project_billing_summary.js +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - - -frappe.query_reports["Project Billing Summary"] = { - "filters": [ - { - fieldname: "project", - label: __("Project"), - fieldtype: "Link", - options: "Project", - reqd: 1 - }, - { - fieldname:"from_date", - label: __("From Date"), - fieldtype: "Date", - default: frappe.datetime.add_months(frappe.datetime.month_start(), -1), - reqd: 1 - }, - { - fieldname:"to_date", - label: __("To Date"), - fieldtype: "Date", - default: frappe.datetime.add_days(frappe.datetime.month_start(),-1), - reqd: 1 - }, - { - fieldname:"include_draft_timesheets", - label: __("Include Timesheets in Draft Status"), - fieldtype: "Check", - }, - ] -} diff --git a/erpnext/projects/report/project_billing_summary/project_billing_summary.py b/erpnext/projects/report/project_billing_summary/project_billing_summary.py deleted file mode 100644 index a2f7378d1b..0000000000 --- a/erpnext/projects/report/project_billing_summary/project_billing_summary.py +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe - -from erpnext.projects.report.billing_summary import get_columns, get_data - - -def execute(filters=None): - filters = frappe._dict(filters or {}) - columns = get_columns() - - data = get_data(filters) - return columns, data diff --git a/erpnext/projects/report/employee_billing_summary/__init__.py b/erpnext/projects/report/timesheet_billing_summary/__init__.py similarity index 100% rename from erpnext/projects/report/employee_billing_summary/__init__.py rename to erpnext/projects/report/timesheet_billing_summary/__init__.py diff --git a/erpnext/projects/report/timesheet_billing_summary/timesheet_billing_summary.js b/erpnext/projects/report/timesheet_billing_summary/timesheet_billing_summary.js new file mode 100644 index 0000000000..1efd0c6733 --- /dev/null +++ b/erpnext/projects/report/timesheet_billing_summary/timesheet_billing_summary.js @@ -0,0 +1,67 @@ +// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +frappe.query_reports["Timesheet Billing Summary"] = { + tree: true, + initial_depth: 0, + filters: [ + { + fieldname: "employee", + label: __("Employee"), + fieldtype: "Link", + options: "Employee", + on_change: function (report) { + unset_group_by(report, "employee"); + }, + }, + { + fieldname: "project", + label: __("Project"), + fieldtype: "Link", + options: "Project", + on_change: function (report) { + unset_group_by(report, "project"); + }, + }, + { + fieldname: "from_date", + label: __("From Date"), + fieldtype: "Date", + default: frappe.datetime.add_months( + frappe.datetime.month_start(), + -1 + ), + }, + { + fieldname: "to_date", + label: __("To Date"), + fieldtype: "Date", + default: frappe.datetime.add_days( + frappe.datetime.month_start(), + -1 + ), + }, + { // NOTE: `update_group_by_options` expects this filter to be the fifth in the list + fieldname: "group_by", + label: __("Group By"), + fieldtype: "Select", + options: [ + "", + { value: "employee", label: __("Employee") }, + { value: "project", label: __("Project") }, + { value: "date", label: __("Start Date") }, + ], + }, + { + fieldname: "include_draft_timesheets", + label: __("Include Timesheets in Draft Status"), + fieldtype: "Check", + }, + ], +}; + +function unset_group_by(report, fieldname) { + if (report.get_filter_value(fieldname) && report.get_filter_value("group_by") == fieldname) { + report.set_filter_value("group_by", ""); + } +} diff --git a/erpnext/projects/report/project_billing_summary/project_billing_summary.json b/erpnext/projects/report/timesheet_billing_summary/timesheet_billing_summary.json similarity index 61% rename from erpnext/projects/report/project_billing_summary/project_billing_summary.json rename to erpnext/projects/report/timesheet_billing_summary/timesheet_billing_summary.json index 817d0cdb66..0f070cb457 100644 --- a/erpnext/projects/report/project_billing_summary/project_billing_summary.json +++ b/erpnext/projects/report/timesheet_billing_summary/timesheet_billing_summary.json @@ -1,36 +1,42 @@ { "add_total_row": 1, - "creation": "2019-03-11 16:22:39.460524", - "disable_prepared_report": 0, + "columns": [], + "creation": "2023-10-10 23:53:43.692067", "disabled": 0, "docstatus": 0, "doctype": "Report", + "filters": [], "idx": 0, "is_standard": "Yes", - "modified": "2019-06-13 15:54:55.255947", + "letter_head": "ALYF GmbH", + "letterhead": null, + "modified": "2023-10-11 00:58:30.639078", "modified_by": "Administrator", "module": "Projects", - "name": "Project Billing Summary", + "name": "Timesheet Billing Summary", "owner": "Administrator", "prepared_report": 0, "ref_doctype": "Timesheet", - "report_name": "Project Billing Summary", + "report_name": "Timesheet Billing Summary", "report_type": "Script Report", "roles": [ { "role": "Projects User" }, { - "role": "HR User" + "role": "Employee" + }, + { + "role": "Accounts User" }, { "role": "Manufacturing User" }, { - "role": "Employee" + "role": "HR User" }, { - "role": "Accounts User" + "role": "Employee Self Service" } ] } \ No newline at end of file diff --git a/erpnext/projects/report/timesheet_billing_summary/timesheet_billing_summary.py b/erpnext/projects/report/timesheet_billing_summary/timesheet_billing_summary.py new file mode 100644 index 0000000000..a6e7150e41 --- /dev/null +++ b/erpnext/projects/report/timesheet_billing_summary/timesheet_billing_summary.py @@ -0,0 +1,146 @@ +import frappe +from frappe import _ +from frappe.model.docstatus import DocStatus + + +def execute(filters=None): + group_fieldname = filters.pop("group_by", None) + + filters = frappe._dict(filters or {}) + columns = get_columns(filters, group_fieldname) + + data = get_data(filters, group_fieldname) + return columns, data + + +def get_columns(filters, group_fieldname=None): + group_columns = { + "date": { + "label": _("Date"), + "fieldtype": "Date", + "fieldname": "date", + "width": 150, + }, + "project": { + "label": _("Project"), + "fieldtype": "Link", + "fieldname": "project", + "options": "Project", + "width": 200, + "hidden": int(bool(filters.get("project"))), + }, + "employee": { + "label": _("Employee ID"), + "fieldtype": "Link", + "fieldname": "employee", + "options": "Employee", + "width": 200, + "hidden": int(bool(filters.get("employee"))), + }, + } + columns = [] + if group_fieldname: + columns.append(group_columns.get(group_fieldname)) + columns.extend( + column for column in group_columns.values() if column.get("fieldname") != group_fieldname + ) + else: + columns.extend(group_columns.values()) + + columns.extend( + [ + { + "label": _("Employee Name"), + "fieldtype": "data", + "fieldname": "employee_name", + "hidden": 1, + }, + { + "label": _("Timesheet"), + "fieldtype": "Link", + "fieldname": "timesheet", + "options": "Timesheet", + "width": 150, + }, + {"label": _("Working Hours"), "fieldtype": "Float", "fieldname": "hours", "width": 150}, + { + "label": _("Billing Hours"), + "fieldtype": "Float", + "fieldname": "billing_hours", + "width": 150, + }, + { + "label": _("Billing Amount"), + "fieldtype": "Currency", + "fieldname": "billing_amount", + "width": 150, + }, + ] + ) + + return columns + + +def get_data(filters, group_fieldname=None): + _filters = [] + if filters.get("employee"): + _filters.append(("employee", "=", filters.get("employee"))) + if filters.get("project"): + _filters.append(("Timesheet Detail", "project", "=", filters.get("project"))) + if filters.get("from_date"): + _filters.append(("Timesheet Detail", "from_time", ">=", filters.get("from_date"))) + if filters.get("to_date"): + _filters.append(("Timesheet Detail", "to_time", "<=", filters.get("to_date"))) + if not filters.get("include_draft_timesheets"): + _filters.append(("docstatus", "=", DocStatus.submitted())) + else: + _filters.append(("docstatus", "in", (DocStatus.submitted(), DocStatus.draft()))) + + data = frappe.get_list( + "Timesheet", + fields=[ + "name as timesheet", + "`tabTimesheet`.employee", + "`tabTimesheet`.employee_name", + "`tabTimesheet Detail`.from_time as date", + "`tabTimesheet Detail`.project", + "`tabTimesheet Detail`.hours", + "`tabTimesheet Detail`.billing_hours", + "`tabTimesheet Detail`.billing_amount", + ], + filters=_filters, + order_by="`tabTimesheet Detail`.from_time", + ) + + return group_by(data, group_fieldname) if group_fieldname else data + + +def group_by(data, fieldname): + groups = {row.get(fieldname) for row in data} + grouped_data = [] + for group in sorted(groups): + group_row = { + fieldname: group, + "hours": sum(row.get("hours") for row in data if row.get(fieldname) == group), + "billing_hours": sum(row.get("billing_hours") for row in data if row.get(fieldname) == group), + "billing_amount": sum(row.get("billing_amount") for row in data if row.get(fieldname) == group), + "indent": 0, + "is_group": 1, + } + if fieldname == "employee": + group_row["employee_name"] = next( + row.get("employee_name") for row in data if row.get(fieldname) == group + ) + + grouped_data.append(group_row) + for row in data: + if row.get(fieldname) != group: + continue + + _row = row.copy() + _row[fieldname] = None + _row["indent"] = 1 + _row["is_group"] = 0 + grouped_data.append(_row) + + return grouped_data diff --git a/erpnext/projects/workspace/projects/projects.json b/erpnext/projects/workspace/projects/projects.json index 94ae9c04a4..e6bead9ff4 100644 --- a/erpnext/projects/workspace/projects/projects.json +++ b/erpnext/projects/workspace/projects/projects.json @@ -155,9 +155,9 @@ "dependencies": "Project", "hidden": 0, "is_query_report": 1, - "label": "Project Billing Summary", + "label": "Timesheet Billing Summary", "link_count": 0, - "link_to": "Project Billing Summary", + "link_to": "Timesheet Billing Summary", "link_type": "Report", "onboard": 0, "type": "Link" @@ -192,7 +192,7 @@ "type": "Link" } ], - "modified": "2023-07-04 14:39:08.935853", + "modified": "2023-10-10 23:54:33.082108", "modified_by": "Administrator", "module": "Projects", "name": "Projects", @@ -234,8 +234,8 @@ "type": "DocType" }, { - "label": "Project Billing Summary", - "link_to": "Project Billing Summary", + "label": "Timesheet Billing Summary", + "link_to": "Timesheet Billing Summary", "type": "Report" }, { From 6bd56d2d5f62814eb2f777934ed894bc5709e947 Mon Sep 17 00:00:00 2001 From: marination Date: Wed, 1 Nov 2023 20:11:00 +0530 Subject: [PATCH 037/130] refactor: `split_invoices_based_on_payment_terms` - Invoices were in the wrong order due to the logic. The invoices with payment terms were added first and the rest after. - Overly long function with unnecessary loops (reduced to one main loop) and complexity - The split row as per payment terms was not ordered. So the second installment was allocated first --- .../doctype/payment_entry/payment_entry.py | 145 ++++++++++-------- 1 file changed, 78 insertions(+), 67 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index e6403fddef..fc6e1f462e 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1698,13 +1698,42 @@ def get_outstanding_reference_documents(args, validate=False): return data -def split_invoices_based_on_payment_terms(outstanding_invoices, company): - invoice_ref_based_on_payment_terms = {} +def split_invoices_based_on_payment_terms(outstanding_invoices, company) -> list: + """Split a list of invoices based on their payment terms.""" + exc_rates = get_currency_data(outstanding_invoices, company) + outstanding_invoices_after_split = [] + for entry in outstanding_invoices: + if entry.voucher_type in ["Sales Invoice", "Purchase Invoice"]: + if payment_term_template := frappe.db.get_value( + entry.voucher_type, entry.voucher_no, "payment_terms_template" + ): + split_rows = get_split_invoice_rows(entry, payment_term_template, exc_rates) + if not split_rows: + continue + + frappe.msgprint( + _("Spliting {} {} into {} row(s) as per Payment Terms").format( + split_rows[0]["voucher_type"], split_rows[0]["voucher_no"], len(split_rows) + ), + alert=True, + ) + outstanding_invoices_after_split += split_rows + continue + + # If not an invoice or no payment terms template, add as it is + outstanding_invoices_after_split.append(entry) + + return outstanding_invoices_after_split + + +def get_currency_data(outstanding_invoices: list, company: str = None) -> dict: + """Get currency and conversion data for a list of invoices.""" + exc_rates = frappe._dict() company_currency = ( frappe.db.get_value("Company", company, "default_currency") if company else None ) - exc_rates = frappe._dict() + for doctype in ["Sales Invoice", "Purchase Invoice"]: invoices = [x.voucher_no for x in outstanding_invoices if x.voucher_type == doctype] for x in frappe.db.get_all( @@ -1719,72 +1748,54 @@ def split_invoices_based_on_payment_terms(outstanding_invoices, company): company_currency=company_currency, ) - for idx, d in enumerate(outstanding_invoices): - if d.voucher_type in ["Sales Invoice", "Purchase Invoice"]: - payment_term_template = frappe.db.get_value( - d.voucher_type, d.voucher_no, "payment_terms_template" + return exc_rates + + +def get_split_invoice_rows(invoice: dict, payment_term_template: str, exc_rates: dict) -> list: + """Split invoice based on its payment schedule table.""" + split_rows = [] + allocate_payment_based_on_payment_terms = frappe.db.get_value( + "Payment Terms Template", payment_term_template, "allocate_payment_based_on_payment_terms" + ) + + if not allocate_payment_based_on_payment_terms: + return [invoice] + + payment_schedule = frappe.get_all( + "Payment Schedule", filters={"parent": invoice.voucher_no}, fields=["*"], order_by="due_date" + ) + for payment_term in payment_schedule: + if not payment_term.outstanding > 0.1: + continue + + doc_details = exc_rates.get(payment_term.parent, None) + is_multi_currency_acc = (doc_details.currency != doc_details.company_currency) and ( + doc_details.party_account_currency != doc_details.company_currency + ) + payment_term_outstanding = flt(payment_term.outstanding) + if not is_multi_currency_acc: + payment_term_outstanding = doc_details.conversion_rate * flt(payment_term.outstanding) + + split_rows.append( + frappe._dict( + { + "due_date": invoice.due_date, + "currency": invoice.currency, + "voucher_no": invoice.voucher_no, + "voucher_type": invoice.voucher_type, + "posting_date": invoice.posting_date, + "invoice_amount": flt(invoice.invoice_amount), + "outstanding_amount": payment_term_outstanding + if payment_term_outstanding + else invoice.outstanding_amount, + "payment_term_outstanding": payment_term_outstanding, + "payment_amount": payment_term.payment_amount, + "payment_term": payment_term.payment_term, + } ) - if payment_term_template: - allocate_payment_based_on_payment_terms = frappe.get_cached_value( - "Payment Terms Template", payment_term_template, "allocate_payment_based_on_payment_terms" - ) - if allocate_payment_based_on_payment_terms: - payment_schedule = frappe.get_all( - "Payment Schedule", filters={"parent": d.voucher_no}, fields=["*"] - ) + ) - for payment_term in payment_schedule: - if payment_term.outstanding > 0.1: - doc_details = exc_rates.get(payment_term.parent, None) - is_multi_currency_acc = (doc_details.currency != doc_details.company_currency) and ( - doc_details.party_account_currency != doc_details.company_currency - ) - payment_term_outstanding = flt(payment_term.outstanding) - if not is_multi_currency_acc: - payment_term_outstanding = doc_details.conversion_rate * flt(payment_term.outstanding) - - invoice_ref_based_on_payment_terms.setdefault(idx, []) - invoice_ref_based_on_payment_terms[idx].append( - frappe._dict( - { - "due_date": d.due_date, - "currency": d.currency, - "voucher_no": d.voucher_no, - "voucher_type": d.voucher_type, - "posting_date": d.posting_date, - "invoice_amount": flt(d.invoice_amount), - "outstanding_amount": payment_term_outstanding - if payment_term_outstanding - else d.outstanding_amount, - "payment_term_outstanding": payment_term_outstanding, - "payment_amount": payment_term.payment_amount, - "payment_term": payment_term.payment_term, - "account": d.account, - } - ) - ) - - outstanding_invoices_after_split = [] - if invoice_ref_based_on_payment_terms: - for idx, ref in invoice_ref_based_on_payment_terms.items(): - voucher_no = ref[0]["voucher_no"] - voucher_type = ref[0]["voucher_type"] - - frappe.msgprint( - _("Spliting {} {} into {} row(s) as per Payment Terms").format( - voucher_type, voucher_no, len(ref) - ), - alert=True, - ) - - outstanding_invoices_after_split += invoice_ref_based_on_payment_terms[idx] - - existing_row = list(filter(lambda x: x.get("voucher_no") == voucher_no, outstanding_invoices)) - index = outstanding_invoices.index(existing_row[0]) - outstanding_invoices.pop(index) - - outstanding_invoices_after_split += outstanding_invoices - return outstanding_invoices_after_split + return split_rows def get_orders_to_be_billed( From 8fa677b8e8c6e4e65193ad0be7c0a6d5b4f1769e Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 1 Nov 2023 20:30:16 +0530 Subject: [PATCH 038/130] refactor: checkbox to toggle remarks in General Ledger --- erpnext/accounts/report/general_ledger/general_ledger.js | 6 ++++++ erpnext/accounts/report/general_ledger/general_ledger.py | 9 +++++++-- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js index 37d0659acf..c0b4f59579 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.js +++ b/erpnext/accounts/report/general_ledger/general_ledger.js @@ -193,7 +193,13 @@ frappe.query_reports["General Ledger"] = { "fieldname": "add_values_in_transaction_currency", "label": __("Add Columns in Transaction Currency"), "fieldtype": "Check" + }, + { + "fieldname": "show_remarks", + "label": __("Show Remarks"), + "fieldtype": "Check" } + ] } diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index 79bfd7833a..5e484cf558 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -163,6 +163,9 @@ def get_gl_entries(filters, accounting_dimensions): select_fields = """, debit, credit, debit_in_account_currency, credit_in_account_currency """ + if filters.get("show_remarks"): + select_fields += """,remarks""" + order_by_statement = "order by posting_date, account, creation" if filters.get("include_dimensions"): @@ -195,7 +198,7 @@ def get_gl_entries(filters, accounting_dimensions): voucher_type, voucher_no, {dimension_fields} cost_center, project, {transaction_currency_fields} against_voucher_type, against_voucher, account_currency, - remarks, against, is_opening, creation {select_fields} + against, is_opening, creation {select_fields} from `tabGL Entry` where company=%(company)s {conditions} {order_by_statement} @@ -631,8 +634,10 @@ def get_columns(filters): "width": 100, }, {"label": _("Supplier Invoice No"), "fieldname": "bill_no", "fieldtype": "Data", "width": 100}, - {"label": _("Remarks"), "fieldname": "remarks", "width": 400}, ] ) + if filters.get("show_remarks"): + columns.extend([{"label": _("Remarks"), "fieldname": "remarks", "width": 400}]) + return columns From 38e5e4a8930714c08471308ba47b9f063802a4b3 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Wed, 1 Nov 2023 18:05:50 +0100 Subject: [PATCH 039/130] feat(Stock Balance): add filters from route --- erpnext/stock/page/stock_balance/stock_balance.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/erpnext/stock/page/stock_balance/stock_balance.js b/erpnext/stock/page/stock_balance/stock_balance.js index f00dd3e791..90b8d45342 100644 --- a/erpnext/stock/page/stock_balance/stock_balance.js +++ b/erpnext/stock/page/stock_balance/stock_balance.js @@ -11,6 +11,7 @@ frappe.pages['stock-balance'].on_page_load = function(wrapper) { label: __('Warehouse'), fieldtype:'Link', options:'Warehouse', + default: frappe.route_options && frappe.route_options.warehouse, change: function() { page.item_dashboard.start = 0; page.item_dashboard.refresh(); @@ -22,6 +23,7 @@ frappe.pages['stock-balance'].on_page_load = function(wrapper) { label: __('Item'), fieldtype:'Link', options:'Item', + default: frappe.route_options && frappe.route_options.item_code, change: function() { page.item_dashboard.start = 0; page.item_dashboard.refresh(); @@ -33,6 +35,7 @@ frappe.pages['stock-balance'].on_page_load = function(wrapper) { label: __('Item Group'), fieldtype:'Link', options:'Item Group', + default: frappe.route_options && frappe.route_options.item_group, change: function() { page.item_dashboard.start = 0; page.item_dashboard.refresh(); From 54e8ce1ac5a0d14905050fbb630a1602a560088a Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 2 Nov 2023 09:16:26 +0530 Subject: [PATCH 040/130] refactor: pass limits to JE and PE queries in reconciliation tool --- .../payment_reconciliation/payment_reconciliation.py | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 1626f25f3e..43167be15a 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -109,6 +109,8 @@ class PaymentReconciliation(Document): "t2.against_account like %(bank_cash_account)s" if self.bank_cash_account else "1=1" ) + limit = f"limit {self.payment_limit}" if self.payment_limit else " " + # nosemgrep journal_entries = frappe.db.sql( """ @@ -132,11 +134,13 @@ class PaymentReconciliation(Document): ELSE {bank_account_condition} END) order by t1.posting_date + {limit} """.format( **{ "dr_or_cr": dr_or_cr, "bank_account_condition": bank_account_condition, "condition": condition, + "limit": limit, } ), { @@ -162,7 +166,7 @@ class PaymentReconciliation(Document): if self.payment_name: conditions.append(doc.name.like(f"%{self.payment_name}%")) - self.return_invoices = ( + self.return_invoices_query = ( qb.from_(doc) .select( ConstantColumn(voucher_type).as_("voucher_type"), @@ -170,8 +174,11 @@ class PaymentReconciliation(Document): doc.return_against, ) .where(Criterion.all(conditions)) - .run(as_dict=True) ) + if self.payment_limit: + self.return_invoices_query = self.return_invoices_query.limit(self.payment_limit) + + self.return_invoices = self.return_invoices_query.run(as_dict=True) def get_dr_or_cr_notes(self): From a9fceeb00ff266b181791dc0ca4ca334810fff0a Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 2 Nov 2023 11:32:24 +0530 Subject: [PATCH 041/130] chore: std permissions for Process Payment Reconciilation log --- .../process_payment_reconciliation_log.json | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log.json b/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log.json index 1131a0fca6..b4ac9812cb 100644 --- a/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log.json +++ b/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log.json @@ -110,7 +110,7 @@ "in_create": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2023-04-21 17:36:26.642617", + "modified": "2023-11-02 11:32:12.254018", "modified_by": "Administrator", "module": "Accounts", "name": "Process Payment Reconciliation Log", @@ -125,7 +125,19 @@ "print": 1, "read": 1, "report": 1, - "role": "System Manager", + "role": "Accounts Manager", + "share": 1, + "write": 1 + }, + { + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", "share": 1, "write": 1 } From 0104897d693ca98796536eac7a9edadc58c64fff Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 2 Nov 2023 13:14:56 +0530 Subject: [PATCH 042/130] fix: remove voucher type and no for Item and Warehouse based reposting --- erpnext/controllers/stock_controller.py | 2 -- .../stock_reposting_settings/stock_reposting_settings.json | 4 ++-- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index a7330ec63c..fc45c7ad52 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -1210,8 +1210,6 @@ def create_item_wise_repost_entries(voucher_type, voucher_no, allow_zero_rate=Fa repost_entry = frappe.new_doc("Repost Item Valuation") repost_entry.based_on = "Item and Warehouse" - repost_entry.voucher_type = voucher_type - repost_entry.voucher_no = voucher_no repost_entry.item_code = sle.item_code repost_entry.warehouse = sle.warehouse diff --git a/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json index 7c712ce225..68afd996b4 100644 --- a/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json +++ b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json @@ -50,7 +50,7 @@ "label": "Limit timeslot for Stock Reposting" }, { - "default": "0", + "default": "1", "fieldname": "item_based_reposting", "fieldtype": "Check", "label": "Use Item based reposting" @@ -70,7 +70,7 @@ "index_web_pages_for_search": 1, "issingle": 1, "links": [], - "modified": "2023-05-04 16:14:29.080697", + "modified": "2023-11-01 16:14:29.080697", "modified_by": "Administrator", "module": "Stock", "name": "Stock Reposting Settings", From 539f0251d95d80055112d72e54af9571bea987dc Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 2 Nov 2023 13:17:21 +0530 Subject: [PATCH 043/130] refactor: better output on gl and pl comparison report --- .../general_and_payment_ledger_comparison.py | 82 ++++++++++++++++--- 1 file changed, 69 insertions(+), 13 deletions(-) diff --git a/erpnext/accounts/report/general_and_payment_ledger_comparison/general_and_payment_ledger_comparison.py b/erpnext/accounts/report/general_and_payment_ledger_comparison/general_and_payment_ledger_comparison.py index 099884a48e..696a03b0a7 100644 --- a/erpnext/accounts/report/general_and_payment_ledger_comparison/general_and_payment_ledger_comparison.py +++ b/erpnext/accounts/report/general_and_payment_ledger_comparison/general_and_payment_ledger_comparison.py @@ -79,7 +79,9 @@ class General_Payment_Ledger_Comparison(object): .select( gle.company, gle.account, + gle.voucher_type, gle.voucher_no, + gle.party_type, gle.party, outstanding, ) @@ -89,7 +91,9 @@ class General_Payment_Ledger_Comparison(object): & (gle.account.isin(val.accounts)) ) .where(Criterion.all(filter_criterion)) - .groupby(gle.company, gle.account, gle.voucher_no, gle.party) + .groupby( + gle.company, gle.account, gle.voucher_type, gle.voucher_no, gle.party_type, gle.party + ) .run() ) @@ -112,7 +116,13 @@ class General_Payment_Ledger_Comparison(object): self.account_types[acc_type].ple = ( qb.from_(ple) .select( - ple.company, ple.account, ple.voucher_no, ple.party, Sum(ple.amount).as_("outstanding") + ple.company, + ple.account, + ple.voucher_type, + ple.voucher_no, + ple.party_type, + ple.party, + Sum(ple.amount).as_("outstanding"), ) .where( (ple.company == self.filters.company) @@ -120,7 +130,9 @@ class General_Payment_Ledger_Comparison(object): & (ple.account.isin(val.accounts)) ) .where(Criterion.all(filter_criterion)) - .groupby(ple.company, ple.account, ple.voucher_no, ple.party) + .groupby( + ple.company, ple.account, ple.voucher_type, ple.voucher_no, ple.party_type, ple.party + ) .run() ) @@ -138,12 +150,12 @@ class General_Payment_Ledger_Comparison(object): self.diff = frappe._dict({}) for x in self.variation_in_payment_ledger: - self.diff[(x[0], x[1], x[2], x[3])] = frappe._dict({"gl_balance": x[4]}) + self.diff[(x[0], x[1], x[2], x[3], x[4], x[5])] = frappe._dict({"gl_balance": x[6]}) for x in self.variation_in_general_ledger: - self.diff.setdefault((x[0], x[1], x[2], x[3]), frappe._dict({"gl_balance": 0.0})).update( - frappe._dict({"pl_balance": x[4]}) - ) + self.diff.setdefault( + (x[0], x[1], x[2], x[3], x[4], x[5]), frappe._dict({"gl_balance": 0.0}) + ).update(frappe._dict({"pl_balance": x[6]})) def generate_data(self): self.data = [] @@ -151,8 +163,12 @@ class General_Payment_Ledger_Comparison(object): self.data.append( frappe._dict( { - "voucher_no": key[2], - "party": key[3], + "company": key[0], + "account": key[1], + "voucher_type": key[2], + "voucher_no": key[3], + "party_type": key[4], + "party": key[5], "gl_balance": val.gl_balance, "pl_balance": val.pl_balance, } @@ -162,12 +178,52 @@ class General_Payment_Ledger_Comparison(object): def get_columns(self): self.columns = [] options = None + self.columns.append( + dict( + label=_("Company"), + fieldname="company", + fieldtype="Link", + options="Company", + width="100", + ) + ) + + self.columns.append( + dict( + label=_("Account"), + fieldname="account", + fieldtype="Link", + options="Account", + width="100", + ) + ) + + self.columns.append( + dict( + label=_("Voucher Type"), + fieldname="voucher_type", + fieldtype="Link", + options="DocType", + width="100", + ) + ) + self.columns.append( dict( label=_("Voucher No"), fieldname="voucher_no", - fieldtype="Data", - options=options, + fieldtype="Dynamic Link", + options="voucher_type", + width="100", + ) + ) + + self.columns.append( + dict( + label=_("Party Type"), + fieldname="party_type", + fieldtype="Link", + options="DocType", width="100", ) ) @@ -176,8 +232,8 @@ class General_Payment_Ledger_Comparison(object): dict( label=_("Party"), fieldname="party", - fieldtype="Data", - options=options, + fieldtype="Dynamic Link", + options="party_type", width="100", ) ) From 639f427d6d31c7d018033c2110c478f8e6ea8ce4 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 2 Nov 2023 13:40:40 +0530 Subject: [PATCH 044/130] refactor(test): for ledger comparision report --- .../test_general_and_payment_ledger_comparison.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/accounts/report/general_and_payment_ledger_comparison/test_general_and_payment_ledger_comparison.py b/erpnext/accounts/report/general_and_payment_ledger_comparison/test_general_and_payment_ledger_comparison.py index 4b0e99d712..59e906ba33 100644 --- a/erpnext/accounts/report/general_and_payment_ledger_comparison/test_general_and_payment_ledger_comparison.py +++ b/erpnext/accounts/report/general_and_payment_ledger_comparison/test_general_and_payment_ledger_comparison.py @@ -50,7 +50,11 @@ class TestGeneralAndPaymentLedger(FrappeTestCase, AccountsTestMixin): self.assertEqual(len(data), 1) expected = { + "company": sinv.company, + "account": sinv.debit_to, + "voucher_type": sinv.doctype, "voucher_no": sinv.name, + "party_type": "Customer", "party": sinv.customer, "gl_balance": sinv.grand_total, "pl_balance": sinv.grand_total - 1, From 1b808e1d7c2c2d1ad51bd18acf6a45e9b5cfc605 Mon Sep 17 00:00:00 2001 From: Gursheen Kaur Anand <40693548+GursheenK@users.noreply.github.com> Date: Thu, 2 Nov 2023 14:01:26 +0530 Subject: [PATCH 045/130] fix: standard submit perm in repost ledger for editable invoices (#37826) * fix: ignore perm while reposting ledger * fix: use flag in save * fix: remove unnecessary save --- erpnext/controllers/accounts_controller.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index 6efe631a29..38c1e820d2 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -2269,6 +2269,7 @@ class AccountsController(TransactionBase): repost_ledger = frappe.new_doc("Repost Accounting Ledger") repost_ledger.company = self.company repost_ledger.append("vouchers", {"voucher_type": self.doctype, "voucher_no": self.name}) + repost_ledger.flags.ignore_permissions = True repost_ledger.insert() repost_ledger.submit() self.db_set("repost_required", 0) From ed1c198897c31795c720f0f738c2ec40f4a70bc8 Mon Sep 17 00:00:00 2001 From: Rohit Waghchaure Date: Thu, 2 Nov 2023 14:21:16 +0530 Subject: [PATCH 046/130] chore: fix test cases --- .../repost_item_valuation/test_repost_item_valuation.py | 4 +++- erpnext/stock/doctype/stock_entry/test_stock_entry.py | 1 + .../doctype/stock_reconciliation/test_stock_reconciliation.py | 1 + 3 files changed, 5 insertions(+), 1 deletion(-) diff --git a/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py index 623e8fafe9..5b76e442f4 100644 --- a/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py +++ b/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py @@ -5,7 +5,7 @@ from unittest.mock import MagicMock, call import frappe -from frappe.tests.utils import FrappeTestCase +from frappe.tests.utils import FrappeTestCase, change_settings from frappe.utils import add_days, add_to_date, now, nowdate, today from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice @@ -196,6 +196,7 @@ class TestRepostItemValuation(FrappeTestCase, StockTestMixin): riv.set_status("Skipped") + @change_settings("Stock Reposting Settings", {"item_based_reposting": 0}) def test_prevention_of_cancelled_transaction_riv(self): frappe.flags.dont_execute_stock_reposts = True @@ -373,6 +374,7 @@ class TestRepostItemValuation(FrappeTestCase, StockTestMixin): accounts_settings.acc_frozen_upto = "" accounts_settings.save() + @change_settings("Stock Reposting Settings", {"item_based_reposting": 0}) def test_create_repost_entry_for_cancelled_document(self): pr = make_purchase_receipt( company="_Test Company with perpetual inventory", diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py index 3e0610ef6e..b640983a09 100644 --- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py @@ -1467,6 +1467,7 @@ class TestStockEntry(FrappeTestCase): self.assertEqual(se.items[0].item_name, item.item_name) self.assertEqual(se.items[0].stock_uom, item.stock_uom) + @change_settings("Stock Reposting Settings", {"item_based_reposting": 0}) def test_reposting_for_depedent_warehouse(self): from erpnext.stock.doctype.repost_item_valuation.repost_item_valuation import repost_sl_entries from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py index 4817c8d8dc..34a7cbaa72 100644 --- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py @@ -674,6 +674,7 @@ class TestStockReconciliation(FrappeTestCase, StockTestMixin): self.assertEqual(flt(sl_entry.actual_qty), 1.0) self.assertEqual(flt(sl_entry.qty_after_transaction), 1.0) + @change_settings("Stock Reposting Settings", {"item_based_reposting": 0}) def test_backdated_stock_reco_entry(self): from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry From 1f4b38174899d99be2a3ac894a328c6b7ae7475f Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Thu, 2 Nov 2023 17:38:43 +0530 Subject: [PATCH 047/130] fix: test for invoice returns --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index f98cd65950..49ca76ed4f 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1073,8 +1073,8 @@ class PaymentEntry(AccountsController): gle.update( { - dr_or_cr: allocated_amount_in_company_currency, - dr_or_cr + "_in_account_currency": d.allocated_amount, + dr_or_cr: abs(allocated_amount_in_company_currency), + dr_or_cr + "_in_account_currency": abs(d.allocated_amount), "against_voucher_type": against_voucher_type, "against_voucher": against_voucher, "cost_center": cost_center, From cd1e016163141a4851b9128a2ff008809942482e Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Thu, 2 Nov 2023 17:39:27 +0530 Subject: [PATCH 048/130] test: receive payments from payable party --- .../payment_entry/test_payment_entry.py | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index edfec41918..3e8292946c 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -1262,6 +1262,39 @@ class TestPaymentEntry(FrappeTestCase): so.reload() self.assertEqual(so.advance_paid, so.rounded_total) + def test_receive_payment_from_payable_party_type(self): + pe = create_payment_entry( + party_type="Supplier", + party="_Test Supplier", + payment_type="Receive", + paid_from="Creditors - _TC", + paid_to="_Test Cash - _TC", + save=True, + submit=True, + ) + self.voucher_no = pe.name + self.expected_gle = [ + {"account": "_Test Cash - _TC", "debit": 1000.0, "credit": 0.0}, + {"account": "Creditors - _TC", "debit": 0.0, "credit": 1000.0}, + ] + self.check_gl_entries() + + def check_gl_entries(self): + gle = frappe.qb.DocType("GL Entry") + gl_entries = ( + frappe.qb.from_(gle) + .select( + gle.account, + gle.debit, + gle.credit, + ) + .where((gle.voucher_no == self.voucher_no) & (gle.is_cancelled == 0)) + .orderby(gle.account) + ).run(as_dict=True) + for row in range(len(self.expected_gle)): + for field in ["account", "debit", "credit"]: + self.assertEqual(self.expected_gle[row][field], gl_entries[row][field]) + def create_payment_entry(**args): payment_entry = frappe.new_doc("Payment Entry") From c37e374fdd322c0f0423469ce95faecac15b18e4 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 2 Nov 2023 21:00:45 +0530 Subject: [PATCH 049/130] fix: Add index to supplier invoice field (#37861) * fix: Add index to supplier invoice field * chore: remove unintetional changes --- .../doctype/purchase_invoice/purchase_invoice.json | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 2d1f4451b6..00176c01fc 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -384,7 +384,8 @@ "label": "Supplier Invoice No", "oldfieldname": "bill_no", "oldfieldtype": "Data", - "print_hide": 1 + "print_hide": 1, + "search_index": 1 }, { "fieldname": "column_break_15", @@ -1602,7 +1603,7 @@ "idx": 204, "is_submittable": 1, "links": [], - "modified": "2023-10-16 16:24:51.886231", + "modified": "2023-11-02 18:46:16.810187", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", @@ -1665,4 +1666,4 @@ "timeline_field": "supplier", "title_field": "title", "track_changes": 1 -} \ No newline at end of file +} From e019d43d0b3fa6faa241a7b9885f65f6fccc9459 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Fri, 3 Nov 2023 00:53:30 +0530 Subject: [PATCH 050/130] fix: permission error while creating Supplier Quotation from Portal --- erpnext/controllers/buying_controller.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index a76abe2154..3a802bd26f 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -4,7 +4,7 @@ import frappe from frappe import ValidationError, _, msgprint -from frappe.contacts.doctype.address.address import get_address_display +from frappe.contacts.doctype.address.address import render_address from frappe.utils import cint, flt, getdate from frappe.utils.data import nowtime @@ -246,7 +246,9 @@ class BuyingController(SubcontractingController): for address_field, address_display_field in address_dict.items(): if self.get(address_field): - self.set(address_display_field, get_address_display(self.get(address_field))) + self.set( + address_display_field, render_address(self.get(address_field), check_permissions=False) + ) def set_total_in_words(self): from frappe.utils import money_in_words From 23beb46d15a2b96601e1f20a68dee06d7e5f0c49 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 3 Nov 2023 11:03:54 +0530 Subject: [PATCH 051/130] refactor: group only by voucher flag in AR/AP report --- .../accounts_payable/accounts_payable.js | 8 ++++++- .../accounts_receivable.js | 8 ++++++- .../accounts_receivable.py | 22 +++++++++++++++---- 3 files changed, 32 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/report/accounts_payable/accounts_payable.js b/erpnext/accounts/report/accounts_payable/accounts_payable.js index 9c73cbb344..eff705dafa 100644 --- a/erpnext/accounts/report/accounts_payable/accounts_payable.js +++ b/erpnext/accounts/report/accounts_payable/accounts_payable.js @@ -143,7 +143,13 @@ frappe.query_reports["Accounts Payable"] = { "fieldname": "show_future_payments", "label": __("Show Future Payments"), "fieldtype": "Check", + }, + { + "fieldname": "ignore_accounts", + "label": __("Group by Voucher"), + "fieldtype": "Check", } + ], "formatter": function(value, row, column, data, default_formatter) { @@ -175,4 +181,4 @@ function get_party_type_options() { }); }); return options; -} \ No newline at end of file +} diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js index 1073be0bdc..786aad601b 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js @@ -172,7 +172,13 @@ frappe.query_reports["Accounts Receivable"] = { "fieldname": "show_remarks", "label": __("Show Remarks"), "fieldtype": "Check", + }, + { + "fieldname": "ignore_accounts", + "label": __("Group by Voucher"), + "fieldtype": "Check", } + ], "formatter": function(value, row, column, data, default_formatter) { @@ -205,4 +211,4 @@ function get_party_type_options() { }); }); return options; -} \ No newline at end of file +} diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index b9c7a0bfb8..69f703d907 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -116,7 +116,12 @@ class ReceivablePayableReport(object): # build all keys, since we want to exclude vouchers beyond the report date for ple in self.ple_entries: # get the balance object for voucher_type - key = (ple.account, ple.voucher_type, ple.voucher_no, ple.party) + + if self.filters.get("ingore_accounts"): + key = (ple.voucher_type, ple.voucher_no, ple.party) + else: + key = (ple.account, ple.voucher_type, ple.voucher_no, ple.party) + if not key in self.voucher_balance: self.voucher_balance[key] = frappe._dict( voucher_type=ple.voucher_type, @@ -183,7 +188,10 @@ class ReceivablePayableReport(object): ): return - key = (ple.account, ple.against_voucher_type, ple.against_voucher_no, ple.party) + if self.filters.get("ingore_accounts"): + key = (ple.against_voucher_type, ple.against_voucher_no, ple.party) + else: + key = (ple.account, ple.against_voucher_type, ple.against_voucher_no, ple.party) # If payment is made against credit note # and credit note is made against a Sales Invoice @@ -192,13 +200,19 @@ class ReceivablePayableReport(object): if ple.against_voucher_no in self.return_entries: return_against = self.return_entries.get(ple.against_voucher_no) if return_against: - key = (ple.account, ple.against_voucher_type, return_against, ple.party) + if self.filters.get("ingore_accounts"): + key = (ple.against_voucher_type, return_against, ple.party) + else: + key = (ple.account, ple.against_voucher_type, return_against, ple.party) row = self.voucher_balance.get(key) if not row: # no invoice, this is an invoice / stand-alone payment / credit note - row = self.voucher_balance.get((ple.account, ple.voucher_type, ple.voucher_no, ple.party)) + if self.filters.get("ingore_accounts"): + row = self.voucher_balance.get((ple.voucher_type, ple.voucher_no, ple.party)) + else: + row = self.voucher_balance.get((ple.account, ple.voucher_type, ple.voucher_no, ple.party)) row.party_type = ple.party_type return row From 40157235916578ae88d2ec9a85f481c92fec9278 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Fri, 3 Nov 2023 11:58:25 +0530 Subject: [PATCH 052/130] fix: credit note receive payment entry --- .../doctype/payment_entry/payment_entry.py | 19 +++++++++++++++++-- erpnext/accounts/utils.py | 4 ++++ 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 49ca76ed4f..23b58947d3 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -33,6 +33,7 @@ from erpnext.accounts.utils import ( get_account_currency, get_balance_on, get_outstanding_invoices, + get_party_types_from_account_type, ) from erpnext.controllers.accounts_controller import ( AccountsController, @@ -1071,10 +1072,24 @@ class PaymentEntry(AccountsController): against_voucher_type = d.reference_doctype against_voucher = d.reference_name + reverse_dr_or_cr = 0 + if d.reference_doctype in ["Sales Invoice", "Purchase Invoice"]: + is_return = frappe.db.get_value(d.reference_doctype, d.reference_name, "is_return") + payable_party_types = get_party_types_from_account_type("Payable") + receivable_party_types = get_party_types_from_account_type("Receivable") + if is_return and self.party_type in receivable_party_types and self.payment_type == "Pay": + reverse_dr_or_cr = 1 + elif is_return and self.party_type in payable_party_types and self.payment_type == "Receive": + reverse_dr_or_cr = 1 + gle.update( { - dr_or_cr: abs(allocated_amount_in_company_currency), - dr_or_cr + "_in_account_currency": abs(d.allocated_amount), + dr_or_cr: abs(allocated_amount_in_company_currency) + if reverse_dr_or_cr + else allocated_amount_in_company_currency, + dr_or_cr + "_in_account_currency": abs(d.allocated_amount) + if reverse_dr_or_cr + else d.allocated_amount, "against_voucher_type": against_voucher_type, "against_voucher": against_voucher, "cost_center": cost_center, diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 1c7052f8ff..0a116a7345 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -2041,3 +2041,7 @@ def create_gain_loss_journal( journal_entry.save() journal_entry.submit() return journal_entry.name + + +def get_party_types_from_account_type(account_type): + return frappe.db.get_list("Party Type", {"account_type": account_type}, pluck="name") From 4867ca353c1ba199844830eb539d9dca5455949b Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Fri, 3 Nov 2023 12:00:56 +0530 Subject: [PATCH 053/130] refactor: move common util for fetching party types using account type --- .../report/accounts_receivable/accounts_receivable.py | 6 ++---- .../accounts_receivable_summary.py | 5 ++--- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 20444f9496..4cc0f0c6d1 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -14,7 +14,7 @@ from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( get_accounting_dimensions, get_dimension_with_children, ) -from erpnext.accounts.utils import get_currency_precision +from erpnext.accounts.utils import get_currency_precision, get_party_types_from_account_type # This report gives a summary of all Outstanding Invoices considering the following @@ -72,9 +72,7 @@ class ReceivablePayableReport(object): self.currency_precision = get_currency_precision() or 2 self.dr_or_cr = "debit" if self.filters.account_type == "Receivable" else "credit" self.account_type = self.filters.account_type - self.party_type = frappe.db.get_all( - "Party Type", {"account_type": self.account_type}, pluck="name" - ) + self.party_type = get_party_types_from_account_type(self.account_type) self.party_details = {} self.invoices = set() self.skip_total_row = 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 60274cd8b1..d50cf0708e 100644 --- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py +++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py @@ -8,6 +8,7 @@ from frappe.utils import cint, flt from erpnext.accounts.party import get_partywise_advanced_payment_amount from erpnext.accounts.report.accounts_receivable.accounts_receivable import ReceivablePayableReport +from erpnext.accounts.utils import get_party_types_from_account_type def execute(filters=None): @@ -22,9 +23,7 @@ def execute(filters=None): class AccountsReceivableSummary(ReceivablePayableReport): def run(self, args): self.account_type = args.get("account_type") - self.party_type = frappe.db.get_all( - "Party Type", {"account_type": self.account_type}, pluck="name" - ) + self.party_type = get_party_types_from_account_type(self.account_type) self.party_naming_by = frappe.db.get_value( args.get("naming_by")[0], None, args.get("naming_by")[1] ) From 469ae2c7f1edb6dbd2d770b65c48bad5a0293ffe Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Fri, 3 Nov 2023 15:57:52 +0530 Subject: [PATCH 054/130] perf: index return against for purchase invoice (#37881) --- .../accounts/doctype/purchase_invoice/purchase_invoice.json | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 00176c01fc..09bffff6da 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -408,7 +408,8 @@ "no_copy": 1, "options": "Purchase Invoice", "print_hide": 1, - "read_only": 1 + "read_only": 1, + "search_index": 1 }, { "fieldname": "section_addresses", @@ -1603,7 +1604,7 @@ "idx": 204, "is_submittable": 1, "links": [], - "modified": "2023-11-02 18:46:16.810187", + "modified": "2023-11-03 15:47:30.319200", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", From d4c0dbfacc7e99da6cba2c5d389f0a662490b0eb Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Fri, 3 Nov 2023 17:19:06 +0530 Subject: [PATCH 055/130] fix: incorrect available qty for backdated stock reco with batch (#37858) * fix: incorrect available qty for backdated stock reco with batch * test: added test case --- .../serial_and_batch_bundle.py | 13 +- .../stock_reconciliation.py | 135 +++++++++++++----- .../test_stock_reconciliation.py | 69 ++++++++- .../stock_reconciliation_item.json | 3 +- .../stock/report/stock_ledger/stock_ledger.py | 8 ++ .../stock_ledger_invariant_check.py | 13 +- erpnext/stock/stock_ledger.py | 68 +++++---- 7 files changed, 229 insertions(+), 80 deletions(-) diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py index f96c184c88..f2bbf2b211 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py @@ -121,7 +121,7 @@ class SerialandBatchBundle(Document): def throw_error_message(self, message, exception=frappe.ValidationError): frappe.throw(_(message), exception, title=_("Error")) - def set_incoming_rate(self, row=None, save=False): + def set_incoming_rate(self, row=None, save=False, allow_negative_stock=False): if self.type_of_transaction not in ["Inward", "Outward"] or self.voucher_type in [ "Installation Note", "Job Card", @@ -131,7 +131,9 @@ class SerialandBatchBundle(Document): return if self.type_of_transaction == "Outward": - self.set_incoming_rate_for_outward_transaction(row, save) + self.set_incoming_rate_for_outward_transaction( + row, save, allow_negative_stock=allow_negative_stock + ) else: self.set_incoming_rate_for_inward_transaction(row, save) @@ -152,7 +154,9 @@ class SerialandBatchBundle(Document): def get_serial_nos(self): return [d.serial_no for d in self.entries if d.serial_no] - def set_incoming_rate_for_outward_transaction(self, row=None, save=False): + def set_incoming_rate_for_outward_transaction( + self, row=None, save=False, allow_negative_stock=False + ): sle = self.get_sle_for_outward_transaction() if self.has_serial_no: @@ -181,7 +185,8 @@ class SerialandBatchBundle(Document): if self.docstatus == 1: available_qty += flt(d.qty) - self.validate_negative_batch(d.batch_no, available_qty) + if not allow_negative_stock: + self.validate_negative_batch(d.batch_no, available_qty) d.stock_value_difference = flt(d.qty) * flt(d.incoming_rate) diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index 98b4ffdfcf..323ad4f57d 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -6,7 +6,7 @@ from typing import Optional import frappe from frappe import _, bold, msgprint from frappe.query_builder.functions import CombineDatetime, Sum -from frappe.utils import cint, cstr, flt +from frappe.utils import add_to_date, cint, cstr, flt import erpnext from erpnext.accounts.utils import get_company_default @@ -88,9 +88,12 @@ class StockReconciliation(StockController): self.repost_future_sle_and_gle() self.delete_auto_created_batches() - def set_current_serial_and_batch_bundle(self): + def set_current_serial_and_batch_bundle(self, voucher_detail_no=None, save=False) -> None: """Set Serial and Batch Bundle for each item""" for item in self.items: + if voucher_detail_no and voucher_detail_no != item.name: + continue + item_details = frappe.get_cached_value( "Item", item.item_code, ["has_serial_no", "has_batch_no"], as_dict=1 ) @@ -148,6 +151,7 @@ class StockReconciliation(StockController): "warehouse": item.warehouse, "posting_date": self.posting_date, "posting_time": self.posting_time, + "ignore_voucher_nos": [self.name], } ) ) @@ -163,11 +167,36 @@ class StockReconciliation(StockController): ) if not serial_and_batch_bundle.entries: + if voucher_detail_no: + return + continue - item.current_serial_and_batch_bundle = serial_and_batch_bundle.save().name + serial_and_batch_bundle.save() + item.current_serial_and_batch_bundle = serial_and_batch_bundle.name item.current_qty = abs(serial_and_batch_bundle.total_qty) item.current_valuation_rate = abs(serial_and_batch_bundle.avg_rate) + if save: + sle_creation = frappe.db.get_value( + "Serial and Batch Bundle", item.serial_and_batch_bundle, "creation" + ) + creation = add_to_date(sle_creation, seconds=-1) + item.db_set( + { + "current_serial_and_batch_bundle": item.current_serial_and_batch_bundle, + "current_qty": item.current_qty, + "current_valuation_rate": item.current_valuation_rate, + "creation": creation, + } + ) + + serial_and_batch_bundle.db_set( + { + "creation": creation, + "voucher_no": self.name, + "voucher_detail_no": voucher_detail_no, + } + ) def set_new_serial_and_batch_bundle(self): for item in self.items: @@ -689,56 +718,84 @@ class StockReconciliation(StockController): else: self._cancel() - def recalculate_current_qty(self, item_code, batch_no): + def recalculate_current_qty(self, voucher_detail_no, sle_creation, add_new_sle=False): from erpnext.stock.stock_ledger import get_valuation_rate sl_entries = [] + for row in self.items: - if ( - not (row.item_code == item_code and row.batch_no == batch_no) - and not row.serial_and_batch_bundle - ): + if voucher_detail_no != row.name: continue + current_qty = 0.0 if row.current_serial_and_batch_bundle: - self.recalculate_qty_for_serial_and_batch_bundle(row) - continue - - current_qty = get_batch_qty_for_stock_reco( - item_code, row.warehouse, batch_no, self.posting_date, self.posting_time, self.name - ) + current_qty = self.get_qty_for_serial_and_batch_bundle(row) + elif row.batch_no: + current_qty = get_batch_qty_for_stock_reco( + row.item_code, row.warehouse, row.batch_no, self.posting_date, self.posting_time, self.name + ) precesion = row.precision("current_qty") - if flt(current_qty, precesion) == flt(row.current_qty, precesion): - continue + if flt(current_qty, precesion) != flt(row.current_qty, precesion): + val_rate = get_valuation_rate( + row.item_code, + row.warehouse, + self.doctype, + self.name, + company=self.company, + batch_no=row.batch_no, + serial_and_batch_bundle=row.current_serial_and_batch_bundle, + ) - val_rate = get_valuation_rate( - item_code, row.warehouse, self.doctype, self.name, company=self.company, batch_no=batch_no - ) + row.current_valuation_rate = val_rate + row.current_qty = current_qty + row.db_set( + { + "current_qty": row.current_qty, + "current_valuation_rate": row.current_valuation_rate, + "current_amount": flt(row.current_qty * row.current_valuation_rate), + } + ) - row.current_valuation_rate = val_rate - if not row.current_qty and current_qty: - sle = self.get_sle_for_items(row) - sle.actual_qty = current_qty * -1 - sle.valuation_rate = val_rate - sl_entries.append(sle) + if ( + add_new_sle + and not frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_detail_no": row.name, "actual_qty": ("<", 0), "is_cancelled": 0}, + "name", + ) + and (not row.current_serial_and_batch_bundle and not row.batch_no) + ): + self.set_current_serial_and_batch_bundle(voucher_detail_no, save=True) + row.reload() - row.current_qty = current_qty - row.db_set( - { - "current_qty": row.current_qty, - "current_valuation_rate": row.current_valuation_rate, - "current_amount": flt(row.current_qty * row.current_valuation_rate), - } - ) + if row.current_qty > 0 and row.current_serial_and_batch_bundle: + new_sle = self.get_sle_for_items(row) + new_sle.actual_qty = row.current_qty * -1 + new_sle.valuation_rate = row.current_valuation_rate + new_sle.creation_time = add_to_date(sle_creation, seconds=-1) + new_sle.serial_and_batch_bundle = row.current_serial_and_batch_bundle + new_sle.qty_after_transaction = 0.0 + sl_entries.append(new_sle) if sl_entries: - self.make_sl_entries(sl_entries, allow_negative_stock=True) + self.make_sl_entries(sl_entries, allow_negative_stock=self.has_negative_stock_allowed()) + if not frappe.db.exists("Repost Item Valuation", {"voucher_no": self.name, "status": "Queued"}): + self.repost_future_sle_and_gle(force=True) - def recalculate_qty_for_serial_and_batch_bundle(self, row): + def has_negative_stock_allowed(self): + allow_negative_stock = cint(frappe.db.get_single_value("Stock Settings", "allow_negative_stock")) + + if all(d.serial_and_batch_bundle and flt(d.qty) == flt(d.current_qty) for d in self.items): + allow_negative_stock = True + + return allow_negative_stock + + def get_qty_for_serial_and_batch_bundle(self, row): doc = frappe.get_doc("Serial and Batch Bundle", row.current_serial_and_batch_bundle) precision = doc.entries[0].precision("qty") + current_qty = 0 for d in doc.entries: qty = ( get_batch_qty( @@ -751,10 +808,12 @@ class StockReconciliation(StockController): or 0 ) * -1 - if flt(d.qty, precision) == flt(qty, precision): - continue + if flt(d.qty, precision) != flt(qty, precision): + d.db_set("qty", qty) - d.db_set("qty", qty) + current_qty += qty + + return abs(current_qty) def get_batch_qty_for_stock_reco( diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py index 34a7cbaa72..1ec99bf9a5 100644 --- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py @@ -742,13 +742,6 @@ class TestStockReconciliation(FrappeTestCase, StockTestMixin): se2.cancel() - self.assertTrue(frappe.db.exists("Repost Item Valuation", {"voucher_no": stock_reco.name})) - - self.assertEqual( - frappe.db.get_value("Repost Item Valuation", {"voucher_no": stock_reco.name}, "status"), - "Completed", - ) - sle = frappe.get_all( "Stock Ledger Entry", filters={"item_code": item_code, "warehouse": warehouse, "is_cancelled": 0}, @@ -766,6 +759,68 @@ class TestStockReconciliation(FrappeTestCase, StockTestMixin): self.assertEqual(flt(sle[0].actual_qty), flt(-100.0)) + def test_backdated_stock_reco_entry_with_batch(self): + from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry + + item_code = self.make_item( + "Test New Batch Item ABCVSD", + { + "is_stock_item": 1, + "has_batch_no": 1, + "batch_number_series": "BNS9.####", + "create_new_batch": 1, + }, + ).name + + warehouse = "_Test Warehouse - _TC" + + # Stock Reco for 100, Balace Qty 100 + stock_reco = create_stock_reconciliation( + item_code=item_code, + posting_date=nowdate(), + posting_time="11:00:00", + warehouse=warehouse, + qty=100, + rate=100, + ) + + sles = frappe.get_all( + "Stock Ledger Entry", + fields=["actual_qty"], + filters={"voucher_no": stock_reco.name, "is_cancelled": 0}, + ) + + self.assertEqual(len(sles), 1) + + stock_reco.reload() + batch_no = get_batch_from_bundle(stock_reco.items[0].serial_and_batch_bundle) + + # Stock Reco for 100, Balace Qty 100 + stock_reco1 = create_stock_reconciliation( + item_code=item_code, + posting_date=add_days(nowdate(), -1), + posting_time="11:00:00", + batch_no=batch_no, + warehouse=warehouse, + qty=60, + rate=100, + ) + + sles = frappe.get_all( + "Stock Ledger Entry", + fields=["actual_qty"], + filters={"voucher_no": stock_reco.name, "is_cancelled": 0}, + ) + + stock_reco1.reload() + new_batch_no = get_batch_from_bundle(stock_reco1.items[0].serial_and_batch_bundle) + + self.assertEqual(len(sles), 2) + + for row in sles: + if row.actual_qty < 0: + self.assertEqual(row.actual_qty, -60) + def test_update_stock_reconciliation_while_reposting(self): from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry diff --git a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json index ca19bbb96a..d9cbf95710 100644 --- a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json +++ b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json @@ -205,6 +205,7 @@ "fieldname": "current_serial_and_batch_bundle", "fieldtype": "Link", "label": "Current Serial / Batch Bundle", + "no_copy": 1, "options": "Serial and Batch Bundle", "read_only": 1 }, @@ -216,7 +217,7 @@ ], "istable": 1, "links": [], - "modified": "2023-07-26 12:54:34.011915", + "modified": "2023-11-02 15:47:07.929550", "modified_by": "Administrator", "module": "Stock", "name": "Stock Reconciliation Item", diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py index eeef39641b..e59f2fe644 100644 --- a/erpnext/stock/report/stock_ledger/stock_ledger.py +++ b/erpnext/stock/report/stock_ledger/stock_ledger.py @@ -249,6 +249,13 @@ def get_columns(filters): "options": "Serial No", "width": 100, }, + { + "label": _("Serial and Batch Bundle"), + "fieldname": "serial_and_batch_bundle", + "fieldtype": "Link", + "options": "Serial and Batch Bundle", + "width": 100, + }, {"label": _("Balance Serial No"), "fieldname": "balance_serial_no", "width": 100}, { "label": _("Project"), @@ -287,6 +294,7 @@ def get_stock_ledger_entries(filters, items): sle.voucher_type, sle.qty_after_transaction, sle.stock_value_difference, + sle.serial_and_batch_bundle, sle.voucher_no, sle.stock_value, sle.batch_no, diff --git a/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py b/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py index ca15afe444..fb392f7e36 100644 --- a/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py +++ b/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py @@ -24,6 +24,7 @@ SLE_FIELDS = ( "stock_value_difference", "valuation_rate", "voucher_detail_no", + "serial_and_batch_bundle", ) @@ -64,7 +65,11 @@ def add_invariant_check_fields(sles): balance_qty += sle.actual_qty balance_stock_value += sle.stock_value_difference - if sle.voucher_type == "Stock Reconciliation" and not sle.batch_no: + if ( + sle.voucher_type == "Stock Reconciliation" + and not sle.batch_no + and not sle.serial_and_batch_bundle + ): balance_qty = frappe.db.get_value("Stock Reconciliation Item", sle.voucher_detail_no, "qty") if balance_qty is None: balance_qty = sle.qty_after_transaction @@ -143,6 +148,12 @@ def get_columns(): "label": _("Batch"), "options": "Batch", }, + { + "fieldname": "serial_and_batch_bundle", + "fieldtype": "Link", + "label": _("Serial and Batch Bundle"), + "options": "Serial and Batch Bundle", + }, { "fieldname": "use_batchwise_valuation", "fieldtype": "Check", diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index b950f18810..551701b47a 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -203,6 +203,11 @@ def make_entry(args, allow_negative_stock=False, via_landed_cost_voucher=False): sle.allow_negative_stock = allow_negative_stock sle.via_landed_cost_voucher = via_landed_cost_voucher sle.submit() + + # Added to handle the case when the stock ledger entry is created from the repostig + if args.get("creation_time") and args.get("voucher_type") == "Stock Reconciliation": + sle.db_set("creation", args.get("creation_time")) + return sle @@ -689,9 +694,11 @@ class update_entries_after(object): if ( sle.voucher_type == "Stock Reconciliation" - and (sle.batch_no or (sle.has_batch_no and sle.serial_and_batch_bundle)) + and ( + sle.batch_no or (sle.has_batch_no and sle.serial_and_batch_bundle and not sle.has_serial_no) + ) and sle.voucher_detail_no - and sle.actual_qty < 0 + and not self.args.get("sle_id") ): self.reset_actual_qty_for_stock_reco(sle) @@ -754,27 +761,22 @@ class update_entries_after(object): self.update_outgoing_rate_on_transaction(sle) def reset_actual_qty_for_stock_reco(self, sle): - if sle.serial_and_batch_bundle: - current_qty = frappe.get_cached_value( - "Serial and Batch Bundle", sle.serial_and_batch_bundle, "total_qty" + doc = frappe.get_cached_doc("Stock Reconciliation", sle.voucher_no) + doc.recalculate_current_qty(sle.voucher_detail_no, sle.creation, sle.actual_qty > 0) + + if sle.actual_qty < 0: + sle.actual_qty = ( + flt(frappe.db.get_value("Stock Reconciliation Item", sle.voucher_detail_no, "current_qty")) + * -1 ) - if current_qty is not None: - current_qty = abs(current_qty) - else: - current_qty = frappe.get_cached_value( - "Stock Reconciliation Item", sle.voucher_detail_no, "current_qty" - ) - - if current_qty: - sle.actual_qty = current_qty * -1 - elif current_qty == 0: - sle.is_cancelled = 1 + if abs(sle.actual_qty) == 0.0: + sle.is_cancelled = 1 def calculate_valuation_for_serial_batch_bundle(self, sle): doc = frappe.get_cached_doc("Serial and Batch Bundle", sle.serial_and_batch_bundle) - doc.set_incoming_rate(save=True) + doc.set_incoming_rate(save=True, allow_negative_stock=self.allow_negative_stock) doc.calculate_qty_and_amount(save=True) self.wh_data.stock_value = round_off_if_near_zero(self.wh_data.stock_value + doc.total_amount) @@ -1461,6 +1463,7 @@ def get_valuation_rate( currency=None, company=None, raise_error_if_no_rate=True, + batch_no=None, serial_and_batch_bundle=None, ): @@ -1469,6 +1472,25 @@ def get_valuation_rate( if not company: company = frappe.get_cached_value("Warehouse", warehouse, "company") + if warehouse and batch_no and frappe.db.get_value("Batch", batch_no, "use_batchwise_valuation"): + table = frappe.qb.DocType("Stock Ledger Entry") + query = ( + frappe.qb.from_(table) + .select(Sum(table.stock_value_difference) / Sum(table.actual_qty)) + .where( + (table.item_code == item_code) + & (table.warehouse == warehouse) + & (table.batch_no == batch_no) + & (table.is_cancelled == 0) + & (table.voucher_no != voucher_no) + & (table.voucher_type != voucher_type) + ) + ) + + last_valuation_rate = query.run() + if last_valuation_rate: + return flt(last_valuation_rate[0][0]) + # Get moving average rate of a specific batch number if warehouse and serial_and_batch_bundle: batch_obj = BatchNoValuation( @@ -1563,8 +1585,6 @@ def update_qty_in_future_sle(args, allow_negative_stock=False): next_stock_reco_detail = get_next_stock_reco(args) if next_stock_reco_detail: detail = next_stock_reco_detail[0] - if detail.batch_no or (detail.serial_and_batch_bundle and detail.has_batch_no): - regenerate_sle_for_batch_stock_reco(detail) # add condition to update SLEs before this date & time datetime_limit_condition = get_datetime_limit_condition(detail) @@ -1593,16 +1613,6 @@ def update_qty_in_future_sle(args, allow_negative_stock=False): validate_negative_qty_in_future_sle(args, allow_negative_stock) -def regenerate_sle_for_batch_stock_reco(detail): - doc = frappe.get_cached_doc("Stock Reconciliation", detail.voucher_no) - doc.recalculate_current_qty(detail.item_code, detail.batch_no) - - if not frappe.db.exists( - "Repost Item Valuation", {"voucher_no": doc.name, "status": "Queued", "docstatus": "1"} - ): - doc.repost_future_sle_and_gle(force=True) - - def get_stock_reco_qty_shift(args): stock_reco_qty_shift = 0 if args.get("is_cancelled"): From f14d1eb8716e1a816af4ed69f9dd1f6a4d30f035 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 3 Nov 2023 17:56:40 +0530 Subject: [PATCH 056/130] chore: performance optimization on payment ledger entry doctype --- .../payment_ledger_entry.json | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json b/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json index 4ae813571a..28c9529995 100644 --- a/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json +++ b/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json @@ -65,7 +65,8 @@ "fieldtype": "Link", "in_standard_filter": 1, "label": "Voucher Type", - "options": "DocType" + "options": "DocType", + "search_index": 1 }, { "fieldname": "voucher_no", @@ -73,14 +74,16 @@ "in_list_view": 1, "in_standard_filter": 1, "label": "Voucher No", - "options": "voucher_type" + "options": "voucher_type", + "search_index": 1 }, { "fieldname": "against_voucher_type", "fieldtype": "Link", "in_standard_filter": 1, "label": "Against Voucher Type", - "options": "DocType" + "options": "DocType", + "search_index": 1 }, { "fieldname": "against_voucher_no", @@ -88,7 +91,8 @@ "in_list_view": 1, "in_standard_filter": 1, "label": "Against Voucher No", - "options": "against_voucher_type" + "options": "against_voucher_type", + "search_index": 1 }, { "fieldname": "amount", @@ -148,13 +152,14 @@ { "fieldname": "voucher_detail_no", "fieldtype": "Data", - "label": "Voucher Detail No" + "label": "Voucher Detail No", + "search_index": 1 } ], "in_create": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2023-10-30 16:15:00.470283", + "modified": "2023-11-03 16:39:58.904113", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Ledger Entry", From 60435daba37c97b88a635ed2449d842abc32582e Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 3 Nov 2023 17:58:19 +0530 Subject: [PATCH 057/130] refactor: avoid precision based validation error while reconciling --- erpnext/accounts/utils.py | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 1c7052f8ff..e0adac412b 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -10,7 +10,7 @@ import frappe.defaults from frappe import _, qb, throw from frappe.model.meta import get_field_precision from frappe.query_builder import AliasedQuery, Criterion, Table -from frappe.query_builder.functions import Sum +from frappe.query_builder.functions import Round, Sum from frappe.query_builder.utils import DocType from frappe.utils import ( cint, @@ -536,6 +536,8 @@ def check_if_advance_entry_modified(args): ) else: + precision = frappe.get_precision("Payment Entry", "unallocated_amount") + payment_entry = frappe.qb.DocType("Payment Entry") payment_ref = frappe.qb.DocType("Payment Entry Reference") @@ -557,7 +559,10 @@ def check_if_advance_entry_modified(args): .where(payment_ref.allocated_amount == args.get("unreconciled_amount")) ) else: - q = q.where(payment_entry.unallocated_amount == args.get("unreconciled_amount")) + q = q.where( + Round(payment_entry.unallocated_amount, precision) + == Round(args.get("unreconciled_amount"), precision) + ) ret = q.run(as_dict=True) From 568d5bfbe82088bb1aea52b4541d16f23ccec931 Mon Sep 17 00:00:00 2001 From: Anand Baburajan Date: Fri, 3 Nov 2023 22:35:08 +0530 Subject: [PATCH 058/130] chore: rename daily_depreciation in asset to depreciation_amount_based_on_num_days_in_month [dev] (#37893) * chore: rename daily_depreciation to depreciation_based_on_num_days_in_month * chore: add patch * chore: remove unnecessary files * chore: add amount in field name * chore: add amount in label --- erpnext/assets/doctype/asset/asset.py | 2 +- erpnext/assets/doctype/asset/test_asset.py | 9 +++++--- .../asset_depreciation_schedule.json | 8 +++---- .../asset_depreciation_schedule.py | 8 ++++--- .../asset_finance_book.json | 18 ++++++++-------- erpnext/patches.txt | 1 + ...ation_amount_based_on_num_days_in_month.py | 21 +++++++++++++++++++ 7 files changed, 47 insertions(+), 20 deletions(-) create mode 100644 erpnext/patches/v15_0/rename_daily_depreciation_to_depreciation_amount_based_on_num_days_in_month.py diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index 9d35634933..c003afe118 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -818,7 +818,7 @@ def get_item_details(item_code, asset_category, gross_purchase_amount): "depreciation_method": d.depreciation_method, "total_number_of_depreciations": d.total_number_of_depreciations, "frequency_of_depreciation": d.frequency_of_depreciation, - "daily_depreciation": d.daily_depreciation, + "depreciation_amount_based_on_num_days_in_month": d.depreciation_amount_based_on_num_days_in_month, "salvage_value_percentage": d.salvage_value_percentage, "expected_value_after_useful_life": flt(gross_purchase_amount) * flt(d.salvage_value_percentage / 100), diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index d69f5ef0b7..cdaccbbfbe 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -755,7 +755,9 @@ class TestDepreciationMethods(AssetSetup): self.assertEqual(schedules, expected_schedules) - def test_schedule_for_straight_line_method_with_daily_depreciation(self): + def test_schedule_for_straight_line_method_with_depreciation_amount_based_on_num_days_in_month( + self, + ): asset = create_asset( calculate_depreciation=1, available_for_use_date="2023-01-01", @@ -764,7 +766,7 @@ class TestDepreciationMethods(AssetSetup): depreciation_start_date="2023-01-31", total_number_of_depreciations=12, frequency_of_depreciation=1, - daily_depreciation=1, + depreciation_amount_based_on_num_days_in_month=1, ) expected_schedules = [ @@ -1760,7 +1762,8 @@ def create_asset(**args): "total_number_of_depreciations": args.total_number_of_depreciations or 5, "expected_value_after_useful_life": args.expected_value_after_useful_life or 0, "depreciation_start_date": args.depreciation_start_date, - "daily_depreciation": args.daily_depreciation or 0, + "depreciation_amount_based_on_num_days_in_month": args.depreciation_amount_based_on_num_days_in_month + or 0, }, ) diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json index 3772ef4d68..6f07d84bcb 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json @@ -19,7 +19,7 @@ "depreciation_method", "total_number_of_depreciations", "rate_of_depreciation", - "daily_depreciation", + "depreciation_amount_based_on_num_days_in_month", "column_break_8", "frequency_of_depreciation", "expected_value_after_useful_life", @@ -179,9 +179,9 @@ { "default": "0", "depends_on": "eval:doc.depreciation_method == \"Straight Line\" || doc.depreciation_method == \"Manual\"", - "fieldname": "daily_depreciation", + "fieldname": "depreciation_amount_based_on_num_days_in_month", "fieldtype": "Check", - "label": "Daily Depreciation", + "label": "Depreciation amount based on number of days in the month", "print_hide": 1, "read_only": 1 } @@ -189,7 +189,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2023-08-10 22:22:09.722968", + "modified": "2023-11-03 21:32:15.021796", "modified_by": "Administrator", "module": "Assets", "name": "Asset Depreciation Schedule", diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py index 7a88ffc5b7..109f96f9f4 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py @@ -153,7 +153,9 @@ class AssetDepreciationSchedule(Document): self.frequency_of_depreciation = row.frequency_of_depreciation self.rate_of_depreciation = row.rate_of_depreciation self.expected_value_after_useful_life = row.expected_value_after_useful_life - self.daily_depreciation = row.daily_depreciation + self.depreciation_amount_based_on_num_days_in_month = ( + row.depreciation_amount_based_on_num_days_in_month + ) self.status = "Draft" def make_depr_schedule( @@ -573,7 +575,7 @@ def get_straight_line_or_manual_depr_amount( ) # if the Depreciation Schedule is being modified after Asset Value Adjustment due to decrease in asset value elif asset.flags.decrease_in_asset_value_due_to_value_adjustment: - if row.daily_depreciation: + if row.depreciation_amount_based_on_num_days_in_month: daily_depr_amount = ( flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life) ) / date_diff( @@ -618,7 +620,7 @@ def get_straight_line_or_manual_depr_amount( ) / number_of_pending_depreciations # if the Depreciation Schedule is being prepared for the first time else: - if row.daily_depreciation: + if row.depreciation_amount_based_on_num_days_in_month: daily_depr_amount = ( flt(asset.gross_purchase_amount) - flt(asset.opening_accumulated_depreciation) diff --git a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json index 2c27dc9aca..df560692c5 100644 --- a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json +++ b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json @@ -8,7 +8,7 @@ "finance_book", "depreciation_method", "total_number_of_depreciations", - "daily_depreciation", + "depreciation_amount_based_on_num_days_in_month", "column_break_5", "frequency_of_depreciation", "depreciation_start_date", @@ -86,23 +86,23 @@ "fieldtype": "Percent", "label": "Rate of Depreciation" }, - { - "default": "0", - "depends_on": "eval:doc.depreciation_method == \"Straight Line\" || doc.depreciation_method == \"Manual\"", - "fieldname": "daily_depreciation", - "fieldtype": "Check", - "label": "Daily Depreciation" - }, { "fieldname": "salvage_value_percentage", "fieldtype": "Percent", "label": "Salvage Value Percentage" + }, + { + "default": "0", + "depends_on": "eval:doc.depreciation_method == \"Straight Line\" || doc.depreciation_method == \"Manual\"", + "fieldname": "depreciation_amount_based_on_num_days_in_month", + "fieldtype": "Check", + "label": "Depreciation amount based on number of days in the month" } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-09-29 15:39:52.740594", + "modified": "2023-11-03 21:30:24.266601", "modified_by": "Administrator", "module": "Assets", "name": "Asset Finance Book", diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 78d2c2c340..ae2caa71ee 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -345,5 +345,6 @@ erpnext.patches.v14_0.rename_over_order_allowance_field erpnext.patches.v14_0.migrate_delivery_stop_lock_field execute:frappe.db.set_single_value("Payment Reconciliation", "invoice_limit", 50) execute:frappe.db.set_single_value("Payment Reconciliation", "payment_limit", 50) +erpnext.patches.v15_0.rename_daily_depreciation_to_depreciation_amount_based_on_num_days_in_month # below migration patch should always run last erpnext.patches.v14_0.migrate_gl_to_payment_ledger diff --git a/erpnext/patches/v15_0/rename_daily_depreciation_to_depreciation_amount_based_on_num_days_in_month.py b/erpnext/patches/v15_0/rename_daily_depreciation_to_depreciation_amount_based_on_num_days_in_month.py new file mode 100644 index 0000000000..63dc0e09bc --- /dev/null +++ b/erpnext/patches/v15_0/rename_daily_depreciation_to_depreciation_amount_based_on_num_days_in_month.py @@ -0,0 +1,21 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + + +from frappe.model.utils.rename_field import rename_field + + +def execute(): + try: + rename_field( + "Asset Finance Book", "daily_depreciation", "depreciation_amount_based_on_num_days_in_month" + ) + rename_field( + "Asset Depreciation Schedule", + "daily_depreciation", + "depreciation_amount_based_on_num_days_in_month", + ) + + except Exception as e: + if e.args[0] != 1054: + raise From 2ce6bbf291d656542492106555ef10ac920731c1 Mon Sep 17 00:00:00 2001 From: Anand Baburajan Date: Sat, 4 Nov 2023 01:41:46 +0530 Subject: [PATCH 059/130] chore: rename depreciation_amount_based_on_num_days_in_month to daily_prorata_based [dev] (#37897) chore: rename depreciation_amount_based_on_num_days_in_month to daily_prorata_based --- erpnext/assets/doctype/asset/asset.py | 2 +- erpnext/assets/doctype/asset/test_asset.py | 7 +++---- .../asset_depreciation_schedule.json | 6 +++--- .../asset_depreciation_schedule.py | 8 +++---- .../asset_finance_book.json | 6 +++--- erpnext/patches.txt | 1 + ...um_days_in_month_to_daily_prorata_based.py | 21 +++++++++++++++++++ 7 files changed, 35 insertions(+), 16 deletions(-) create mode 100644 erpnext/patches/v15_0/rename_depreciation_amount_based_on_num_days_in_month_to_daily_prorata_based.py diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py index c003afe118..3c570d1af0 100644 --- a/erpnext/assets/doctype/asset/asset.py +++ b/erpnext/assets/doctype/asset/asset.py @@ -818,7 +818,7 @@ def get_item_details(item_code, asset_category, gross_purchase_amount): "depreciation_method": d.depreciation_method, "total_number_of_depreciations": d.total_number_of_depreciations, "frequency_of_depreciation": d.frequency_of_depreciation, - "depreciation_amount_based_on_num_days_in_month": d.depreciation_amount_based_on_num_days_in_month, + "daily_prorata_based": d.daily_prorata_based, "salvage_value_percentage": d.salvage_value_percentage, "expected_value_after_useful_life": flt(gross_purchase_amount) * flt(d.salvage_value_percentage / 100), diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py index cdaccbbfbe..9e3ec6faa8 100644 --- a/erpnext/assets/doctype/asset/test_asset.py +++ b/erpnext/assets/doctype/asset/test_asset.py @@ -755,7 +755,7 @@ class TestDepreciationMethods(AssetSetup): self.assertEqual(schedules, expected_schedules) - def test_schedule_for_straight_line_method_with_depreciation_amount_based_on_num_days_in_month( + def test_schedule_for_straight_line_method_with_daily_prorata_based( self, ): asset = create_asset( @@ -766,7 +766,7 @@ class TestDepreciationMethods(AssetSetup): depreciation_start_date="2023-01-31", total_number_of_depreciations=12, frequency_of_depreciation=1, - depreciation_amount_based_on_num_days_in_month=1, + daily_prorata_based=1, ) expected_schedules = [ @@ -1762,8 +1762,7 @@ def create_asset(**args): "total_number_of_depreciations": args.total_number_of_depreciations or 5, "expected_value_after_useful_life": args.expected_value_after_useful_life or 0, "depreciation_start_date": args.depreciation_start_date, - "depreciation_amount_based_on_num_days_in_month": args.depreciation_amount_based_on_num_days_in_month - or 0, + "daily_prorata_based": args.daily_prorata_based or 0, }, ) diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json index 6f07d84bcb..8d8b46321f 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json @@ -19,7 +19,7 @@ "depreciation_method", "total_number_of_depreciations", "rate_of_depreciation", - "depreciation_amount_based_on_num_days_in_month", + "daily_prorata_based", "column_break_8", "frequency_of_depreciation", "expected_value_after_useful_life", @@ -179,9 +179,9 @@ { "default": "0", "depends_on": "eval:doc.depreciation_method == \"Straight Line\" || doc.depreciation_method == \"Manual\"", - "fieldname": "depreciation_amount_based_on_num_days_in_month", + "fieldname": "daily_prorata_based", "fieldtype": "Check", - "label": "Depreciation amount based on number of days in the month", + "label": "Depreciate based on daily pro-rata", "print_hide": 1, "read_only": 1 } diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py index 109f96f9f4..7305691f97 100644 --- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py +++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py @@ -153,9 +153,7 @@ class AssetDepreciationSchedule(Document): self.frequency_of_depreciation = row.frequency_of_depreciation self.rate_of_depreciation = row.rate_of_depreciation self.expected_value_after_useful_life = row.expected_value_after_useful_life - self.depreciation_amount_based_on_num_days_in_month = ( - row.depreciation_amount_based_on_num_days_in_month - ) + self.daily_prorata_based = row.daily_prorata_based self.status = "Draft" def make_depr_schedule( @@ -575,7 +573,7 @@ def get_straight_line_or_manual_depr_amount( ) # if the Depreciation Schedule is being modified after Asset Value Adjustment due to decrease in asset value elif asset.flags.decrease_in_asset_value_due_to_value_adjustment: - if row.depreciation_amount_based_on_num_days_in_month: + if row.daily_prorata_based: daily_depr_amount = ( flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life) ) / date_diff( @@ -620,7 +618,7 @@ def get_straight_line_or_manual_depr_amount( ) / number_of_pending_depreciations # if the Depreciation Schedule is being prepared for the first time else: - if row.depreciation_amount_based_on_num_days_in_month: + if row.daily_prorata_based: daily_depr_amount = ( flt(asset.gross_purchase_amount) - flt(asset.opening_accumulated_depreciation) diff --git a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json index df560692c5..e597d5fe31 100644 --- a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json +++ b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json @@ -8,7 +8,7 @@ "finance_book", "depreciation_method", "total_number_of_depreciations", - "depreciation_amount_based_on_num_days_in_month", + "daily_prorata_based", "column_break_5", "frequency_of_depreciation", "depreciation_start_date", @@ -94,9 +94,9 @@ { "default": "0", "depends_on": "eval:doc.depreciation_method == \"Straight Line\" || doc.depreciation_method == \"Manual\"", - "fieldname": "depreciation_amount_based_on_num_days_in_month", + "fieldname": "daily_prorata_based", "fieldtype": "Check", - "label": "Depreciation amount based on number of days in the month" + "label": "Depreciate based on daily pro-rata" } ], "index_web_pages_for_search": 1, diff --git a/erpnext/patches.txt b/erpnext/patches.txt index ae2caa71ee..1e5b08bf36 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -346,5 +346,6 @@ erpnext.patches.v14_0.migrate_delivery_stop_lock_field execute:frappe.db.set_single_value("Payment Reconciliation", "invoice_limit", 50) execute:frappe.db.set_single_value("Payment Reconciliation", "payment_limit", 50) erpnext.patches.v15_0.rename_daily_depreciation_to_depreciation_amount_based_on_num_days_in_month +erpnext.patches.v15_0.rename_depreciation_amount_based_on_num_days_in_month_to_daily_prorata_based # below migration patch should always run last erpnext.patches.v14_0.migrate_gl_to_payment_ledger diff --git a/erpnext/patches/v15_0/rename_depreciation_amount_based_on_num_days_in_month_to_daily_prorata_based.py b/erpnext/patches/v15_0/rename_depreciation_amount_based_on_num_days_in_month_to_daily_prorata_based.py new file mode 100644 index 0000000000..2c03c23d87 --- /dev/null +++ b/erpnext/patches/v15_0/rename_depreciation_amount_based_on_num_days_in_month_to_daily_prorata_based.py @@ -0,0 +1,21 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors +# License: GNU General Public License v3. See license.txt + + +from frappe.model.utils.rename_field import rename_field + + +def execute(): + try: + rename_field( + "Asset Finance Book", "depreciation_amount_based_on_num_days_in_month", "daily_prorata_based" + ) + rename_field( + "Asset Depreciation Schedule", + "depreciation_amount_based_on_num_days_in_month", + "daily_prorata_based", + ) + + except Exception as e: + if e.args[0] != 1054: + raise From d9e284366d6c67ebd41b914b248c6ac94e973b7e Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Fri, 27 Oct 2023 16:35:35 +0530 Subject: [PATCH 060/130] fix: consider reserved serial nos while cancelling a stock transaction --- .../stock_reservation_entry.py | 29 ++++++++ erpnext/stock/stock_ledger.py | 74 +++++++++++++++---- 2 files changed, 88 insertions(+), 15 deletions(-) diff --git a/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py b/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py index 6b39965f9b..9e79702d82 100644 --- a/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py +++ b/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py @@ -681,6 +681,35 @@ def get_sre_reserved_qty_for_voucher_detail_no( return flt(reserved_qty[0][0]) +def get_sre_reserved_serial_nos_details( + item_code: str, warehouse: str, serial_nos: list = None +) -> dict: + """Returns a dict of `Serial No` reserved in Stock Reservation Entry. The dict is like {serial_no: sre_name, ...}""" + + sre = frappe.qb.DocType("Stock Reservation Entry") + sb_entry = frappe.qb.DocType("Serial and Batch Entry") + query = ( + frappe.qb.from_(sre) + .inner_join(sb_entry) + .on(sre.name == sb_entry.parent) + .select(sb_entry.serial_no, sre.name) + .where( + (sre.docstatus == 1) + & (sre.item_code == item_code) + & (sre.warehouse == warehouse) + & (sre.reserved_qty > sre.delivered_qty) + & (sre.status.notin(["Delivered", "Cancelled"])) + & (sre.reservation_based_on == "Serial and Batch") + ) + .orderby(sb_entry.creation) + ) + + if serial_nos: + query = query.where(sb_entry.serial_no.isin(serial_nos)) + + return frappe._dict(query.run()) + + def get_sre_details_for_voucher(voucher_type: str, voucher_no: str) -> list[dict]: """Returns a list of SREs for the provided voucher.""" diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 551701b47a..ab88381d65 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -19,6 +19,9 @@ from erpnext.stock.doctype.inventory_dimension.inventory_dimension import get_in from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import ( get_sre_reserved_qty_for_item_and_warehouse as get_reserved_stock, ) +from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import ( + get_sre_reserved_serial_nos_details, +) from erpnext.stock.utils import ( get_incoming_outgoing_rate_for_cancel, get_or_make_bin, @@ -1719,22 +1722,22 @@ def validate_negative_qty_in_future_sle(args, allow_negative_stock=False): frappe.throw(message, NegativeStockError, title=_("Insufficient Stock")) - if not args.batch_no: - return + if args.batch_no: + neg_batch_sle = get_future_sle_with_negative_batch_qty(args) + if is_negative_with_precision(neg_batch_sle, is_batch=True): + message = _( + "{0} units of {1} needed in {2} on {3} {4} for {5} to complete this transaction." + ).format( + abs(neg_batch_sle[0]["cumulative_total"]), + frappe.get_desk_link("Batch", args.batch_no), + frappe.get_desk_link("Warehouse", args.warehouse), + neg_batch_sle[0]["posting_date"], + neg_batch_sle[0]["posting_time"], + frappe.get_desk_link(neg_batch_sle[0]["voucher_type"], neg_batch_sle[0]["voucher_no"]), + ) + frappe.throw(message, NegativeStockError, title=_("Insufficient Stock for Batch")) - neg_batch_sle = get_future_sle_with_negative_batch_qty(args) - if is_negative_with_precision(neg_batch_sle, is_batch=True): - message = _( - "{0} units of {1} needed in {2} on {3} {4} for {5} to complete this transaction." - ).format( - abs(neg_batch_sle[0]["cumulative_total"]), - frappe.get_desk_link("Batch", args.batch_no), - frappe.get_desk_link("Warehouse", args.warehouse), - neg_batch_sle[0]["posting_date"], - neg_batch_sle[0]["posting_time"], - frappe.get_desk_link(neg_batch_sle[0]["voucher_type"], neg_batch_sle[0]["voucher_no"]), - ) - frappe.throw(message, NegativeStockError, title=_("Insufficient Stock for Batch")) + validate_reserved_stock(args) def is_negative_with_precision(neg_sle, is_batch=False): @@ -1801,6 +1804,47 @@ def get_future_sle_with_negative_batch_qty(args): ) +def validate_reserved_stock(kwargs): + if kwargs.serial_no: + serial_nos = kwargs.serial_no.split("\n") + validate_reserved_serial_nos(kwargs.item_code, kwargs.warehouse, serial_nos) + + elif kwargs.serial_and_batch_bundle: + sbb_entries = frappe.db.get_all( + "Serial and Batch Entry", + { + "parenttype": "Serial and Batch Bundle", + "parent": kwargs.serial_and_batch_bundle, + "docstatus": 1, + }, + ["batch_no", "serial_no", "qty"], + ) + serial_nos = [entry.serial_no for entry in sbb_entries if entry.serial_no] + + if serial_nos: + validate_reserved_serial_nos(kwargs.item_code, kwargs.warehouse, serial_nos) + + +def validate_reserved_serial_nos(item_code, warehouse, serial_nos): + if reserved_serial_nos_details := get_sre_reserved_serial_nos_details( + item_code, warehouse, serial_nos + ): + if common_serial_nos := list( + set(serial_nos).intersection(set(reserved_serial_nos_details.keys())) + ): + msg = _( + "Serial Nos are reserved in Stock Reservation Entries, you need to unreserve them before proceeding." + ) + msg += "
" + msg += _("Example: Serial No {0} reserved in {1}.").format( + frappe.bold(common_serial_nos[0]), + frappe.get_desk_link( + "Stock Reservation Entry", reserved_serial_nos_details[common_serial_nos[0]] + ), + ) + frappe.throw(msg, title=_("Reserved Serial No.")) + + def is_negative_stock_allowed(*, item_code: Optional[str] = None) -> bool: if cint(frappe.db.get_single_value("Stock Settings", "allow_negative_stock", cache=True)): return True From e1a87a802d18f1fb33c3d9f1066da0cb7b3d4210 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Tue, 31 Oct 2023 18:41:58 +0530 Subject: [PATCH 061/130] fix: consider reserved batches while cancelling a stock transaction --- .../stock_reservation_entry.py | 33 +++++++++++++ erpnext/stock/stock_ledger.py | 48 +++++++++++++++++-- 2 files changed, 77 insertions(+), 4 deletions(-) diff --git a/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py b/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py index 9e79702d82..8063ad508f 100644 --- a/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py +++ b/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py @@ -710,6 +710,39 @@ def get_sre_reserved_serial_nos_details( return frappe._dict(query.run()) +def get_sre_reserved_batch_nos_details( + item_code: str, warehouse: str, batch_nos: list = None +) -> dict: + """Returns a dict of `Batch Qty` reserved in Stock Reservation Entry. The dict is like {batch_no: qty, ...}""" + + sre = frappe.qb.DocType("Stock Reservation Entry") + sb_entry = frappe.qb.DocType("Serial and Batch Entry") + query = ( + frappe.qb.from_(sre) + .inner_join(sb_entry) + .on(sre.name == sb_entry.parent) + .select( + sb_entry.batch_no, + Sum(sb_entry.qty - sb_entry.delivered_qty), + ) + .where( + (sre.docstatus == 1) + & (sre.item_code == item_code) + & (sre.warehouse == warehouse) + & ((sre.reserved_qty - sre.delivered_qty) > 0) + & (sre.status.notin(["Delivered", "Cancelled"])) + & (sre.reservation_based_on == "Serial and Batch") + ) + .groupby(sb_entry.batch_no) + .orderby(sb_entry.creation) + ) + + if batch_nos: + query = query.where(sb_entry.batch_no.isin(batch_nos)) + + return frappe._dict(query.run()) + + def get_sre_details_for_voucher(voucher_type: str, voucher_no: str) -> list[dict]: """Returns a list of SREs for the provided voucher.""" diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index ab88381d65..d8cfdaaaca 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -11,11 +11,17 @@ from frappe import _, scrub from frappe.model.meta import get_field_precision from frappe.query_builder import Case from frappe.query_builder.functions import CombineDatetime, Sum -from frappe.utils import cint, flt, get_link_to_form, getdate, now, nowdate, parse_json +from frappe.utils import cint, flt, get_link_to_form, getdate, now, nowdate, nowtime, parse_json import erpnext from erpnext.stock.doctype.bin.bin import update_qty as update_bin_qty from erpnext.stock.doctype.inventory_dimension.inventory_dimension import get_inventory_dimensions +from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import ( + get_available_batches, +) +from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import ( + get_sre_reserved_batch_nos_details, +) from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import ( get_sre_reserved_qty_for_item_and_warehouse as get_reserved_stock, ) @@ -1809,6 +1815,9 @@ def validate_reserved_stock(kwargs): serial_nos = kwargs.serial_no.split("\n") validate_reserved_serial_nos(kwargs.item_code, kwargs.warehouse, serial_nos) + elif kwargs.batch_no: + validate_reserved_batch_nos(kwargs.item_code, kwargs.warehouse, [kwargs.batch_no]) + elif kwargs.serial_and_batch_bundle: sbb_entries = frappe.db.get_all( "Serial and Batch Entry", @@ -1817,12 +1826,13 @@ def validate_reserved_stock(kwargs): "parent": kwargs.serial_and_batch_bundle, "docstatus": 1, }, - ["batch_no", "serial_no", "qty"], + ["batch_no", "serial_no"], ) - serial_nos = [entry.serial_no for entry in sbb_entries if entry.serial_no] - if serial_nos: + if serial_nos := [entry.serial_no for entry in sbb_entries if entry.serial_no]: validate_reserved_serial_nos(kwargs.item_code, kwargs.warehouse, serial_nos) + elif batch_nos := [entry.batch_no for entry in sbb_entries if entry.batch_no]: + validate_reserved_batch_nos(kwargs.item_code, kwargs.warehouse, batch_nos) def validate_reserved_serial_nos(item_code, warehouse, serial_nos): @@ -1845,6 +1855,36 @@ def validate_reserved_serial_nos(item_code, warehouse, serial_nos): frappe.throw(msg, title=_("Reserved Serial No.")) +def validate_reserved_batch_nos(item_code, warehouse, batch_nos): + if reserved_batches_map := get_sre_reserved_batch_nos_details(item_code, warehouse, batch_nos): + available_batches = get_available_batches( + frappe._dict( + { + "item_code": item_code, + "warehouse": warehouse, + "posting_date": nowdate(), + "posting_time": nowtime(), + } + ) + ) + available_batches_map = {row.batch_no: row.qty for row in available_batches} + precision = cint(frappe.db.get_default("float_precision")) or 2 + + for batch_no in batch_nos: + diff = flt( + available_batches_map.get(batch_no, 0) - reserved_batches_map.get(batch_no, 0), precision + ) + if diff < 0 and abs(diff) > 0.0001: + msg = _("{0} units of {1} needed in {2} on {3} {4} to complete this transaction.").format( + abs(diff), + frappe.get_desk_link("Batch", batch_no), + frappe.get_desk_link("Warehouse", warehouse), + nowdate(), + nowtime(), + ) + frappe.throw(msg, title=_("Reserved Stock for Batch")) + + def is_negative_stock_allowed(*, item_code: Optional[str] = None) -> bool: if cint(frappe.db.get_single_value("Stock Settings", "allow_negative_stock", cache=True)): return True From 98d6cdd53c98b2244031f6b55a7658f1b21b7337 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Wed, 1 Nov 2023 16:52:42 +0530 Subject: [PATCH 062/130] feat: add field `reserved_stock` in Bin --- erpnext/stock/doctype/bin/bin.json | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/bin/bin.json b/erpnext/stock/doctype/bin/bin.json index 02684a7241..312470d50e 100644 --- a/erpnext/stock/doctype/bin/bin.json +++ b/erpnext/stock/doctype/bin/bin.json @@ -13,12 +13,13 @@ "planned_qty", "indented_qty", "ordered_qty", + "projected_qty", "column_break_xn5j", "reserved_qty", "reserved_qty_for_production", "reserved_qty_for_sub_contract", "reserved_qty_for_production_plan", - "projected_qty", + "reserved_stock", "section_break_pmrs", "stock_uom", "column_break_0slj", @@ -173,13 +174,20 @@ { "fieldname": "column_break_0slj", "fieldtype": "Column Break" + }, + { + "default": "0", + "fieldname": "reserved_stock", + "fieldtype": "Float", + "label": "Reserved Stock", + "read_only": 1 } ], "hide_toolbar": 1, "idx": 1, "in_create": 1, "links": [], - "modified": "2023-11-01 15:35:51.722534", + "modified": "2023-11-01 16:51:17.079107", "modified_by": "Administrator", "module": "Stock", "name": "Bin", From f52916a2c360dd6befdd1f3dcc6c184cc181662b Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Wed, 1 Nov 2023 17:37:17 +0530 Subject: [PATCH 063/130] feat: maintain `Reserved Stock` in Bin --- erpnext/stock/doctype/bin/bin.py | 11 +++++++++++ erpnext/stock/doctype/delivery_note/delivery_note.py | 6 ++++++ .../stock_reservation_entry.py | 12 ++++++++++++ 3 files changed, 29 insertions(+) diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py index 5abea9e69f..df466ede68 100644 --- a/erpnext/stock/doctype/bin/bin.py +++ b/erpnext/stock/doctype/bin/bin.py @@ -148,6 +148,17 @@ class Bin(Document): self.set_projected_qty() self.db_set("projected_qty", self.projected_qty, update_modified=True) + def update_reserved_stock(self): + """Update `Reserved Stock` on change in Reserved Qty of Stock Reservation Entry""" + + from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import ( + get_sre_reserved_qty_for_item_and_warehouse, + ) + + reserved_stock = get_sre_reserved_qty_for_item_and_warehouse(self.item_code, self.warehouse) + + self.db_set("reserved_stock", flt(reserved_stock), update_modified=True) + def on_doctype_update(): frappe.db.add_unique("Bin", ["item_code", "warehouse"], constraint_name="unique_item_warehouse") diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index 190575eb94..66dd33a400 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -365,6 +365,9 @@ class DeliveryNote(SellingController): # Update Stock Reservation Entry `Status` based on `Delivered Qty`. sre_doc.update_status() + # Update Reserved Stock in Bin. + sre_doc.update_reserved_stock_in_bin() + qty_to_deliver -= qty_can_be_deliver if self._action == "cancel": @@ -427,6 +430,9 @@ class DeliveryNote(SellingController): # Update Stock Reservation Entry `Status` based on `Delivered Qty`. sre_doc.update_status() + # Update Reserved Stock in Bin. + sre_doc.update_reserved_stock_in_bin() + qty_to_undelivered -= qty_can_be_undelivered def validate_against_stock_reservation_entries(self): diff --git a/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py b/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py index 8063ad508f..09542826f3 100644 --- a/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py +++ b/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py @@ -9,6 +9,8 @@ from frappe.model.document import Document from frappe.query_builder.functions import Sum from frappe.utils import cint, flt +from erpnext.stock.utils import get_or_make_bin + class StockReservationEntry(Document): def validate(self) -> None: @@ -31,6 +33,7 @@ class StockReservationEntry(Document): self.update_reserved_qty_in_voucher() self.update_reserved_qty_in_pick_list() self.update_status() + self.update_reserved_stock_in_bin() def on_update_after_submit(self) -> None: self.can_be_updated() @@ -40,12 +43,14 @@ class StockReservationEntry(Document): self.validate_reservation_based_on_serial_and_batch() self.update_reserved_qty_in_voucher() self.update_status() + self.update_reserved_stock_in_bin() self.reload() def on_cancel(self) -> None: self.update_reserved_qty_in_voucher() self.update_reserved_qty_in_pick_list() self.update_status() + self.update_reserved_stock_in_bin() def validate_amended_doc(self) -> None: """Raises an exception if document is amended.""" @@ -341,6 +346,13 @@ class StockReservationEntry(Document): update_modified=update_modified, ) + def update_reserved_stock_in_bin(self) -> None: + """Updates `Reserved Stock` in Bin.""" + + bin_name = get_or_make_bin(self.item_code, self.warehouse) + bin_doc = frappe.get_cached_doc("Bin", bin_name) + bin_doc.update_reserved_stock() + def update_status(self, status: str = None, update_modified: bool = True) -> None: """Updates status based on Voucher Qty, Reserved Qty and Delivered Qty.""" From 73b65ac82ec11698605720bf37437fab853f3120 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Wed, 1 Nov 2023 18:35:07 +0530 Subject: [PATCH 064/130] fix: consider reserved stock while cancelling a stock transaction --- erpnext/stock/stock_ledger.py | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index d8cfdaaaca..1ed1c4759d 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -21,16 +21,12 @@ from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle impor ) from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import ( get_sre_reserved_batch_nos_details, -) -from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import ( - get_sre_reserved_qty_for_item_and_warehouse as get_reserved_stock, -) -from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import ( get_sre_reserved_serial_nos_details, ) from erpnext.stock.utils import ( get_incoming_outgoing_rate_for_cancel, get_or_make_bin, + get_stock_balance, get_valuation_method, ) from erpnext.stock.valuation import FIFOValuation, LIFOValuation, round_off_if_near_zero @@ -97,6 +93,7 @@ def make_sl_entries(sl_entries, allow_negative_stock=False, via_landed_cost_vouc is_stock_item = frappe.get_cached_value("Item", args.get("item_code"), "is_stock_item") if is_stock_item: bin_name = get_or_make_bin(args.get("item_code"), args.get("warehouse")) + args.reserved_stock = flt(frappe.db.get_value("Bin", bin_name, "reserved_stock")) repost_current_voucher(args, allow_negative_stock, via_landed_cost_voucher) update_bin_qty(bin_name, args) else: @@ -123,6 +120,7 @@ def repost_current_voucher(args, allow_negative_stock=False, via_landed_cost_vou "voucher_no": args.get("voucher_no"), "sle_id": args.get("name"), "creation": args.get("creation"), + "reserved_stock": args.get("reserved_stock"), }, allow_negative_stock=allow_negative_stock, via_landed_cost_voucher=via_landed_cost_voucher, @@ -520,7 +518,7 @@ class update_entries_after(object): self.new_items_found = False self.distinct_item_warehouses = args.get("distinct_item_warehouses", frappe._dict()) self.affected_transactions: Set[Tuple[str, str]] = set() - self.reserved_stock = get_reserved_stock(self.args.item_code, self.args.warehouse) + self.reserved_stock = flt(self.args.reserved_stock) self.data = frappe._dict() self.initialize_previous_data(self.args) @@ -1743,7 +1741,8 @@ def validate_negative_qty_in_future_sle(args, allow_negative_stock=False): ) frappe.throw(message, NegativeStockError, title=_("Insufficient Stock for Batch")) - validate_reserved_stock(args) + if args.reserved_stock: + validate_reserved_stock(args) def is_negative_with_precision(neg_sle, is_batch=False): @@ -1834,6 +1833,21 @@ def validate_reserved_stock(kwargs): elif batch_nos := [entry.batch_no for entry in sbb_entries if entry.batch_no]: validate_reserved_batch_nos(kwargs.item_code, kwargs.warehouse, batch_nos) + else: + precision = cint(frappe.db.get_default("float_precision")) or 2 + balance_qty = get_stock_balance(kwargs.item_code, kwargs.warehouse) + + diff = flt(balance_qty - kwargs.get("reserved_stock", 0), precision) + if diff < 0 and abs(diff) > 0.0001: + msg = _("{0} units of {1} needed in {2} on {3} {4} to complete this transaction.").format( + abs(diff), + frappe.get_desk_link("Item", kwargs.item_code), + frappe.get_desk_link("Warehouse", kwargs.warehouse), + nowdate(), + nowtime(), + ) + frappe.throw(msg, title=_("Reserved Stock")) + def validate_reserved_serial_nos(item_code, warehouse, serial_nos): if reserved_serial_nos_details := get_sre_reserved_serial_nos_details( From 10242235bc6bb9081abd4b8c48c23541e33ae646 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Wed, 1 Nov 2023 21:02:32 +0530 Subject: [PATCH 065/130] fix(test): `test_stock_reservation_against_sales_order` --- .../stock_reservation_entry/test_stock_reservation_entry.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/stock/doctype/stock_reservation_entry/test_stock_reservation_entry.py b/erpnext/stock/doctype/stock_reservation_entry/test_stock_reservation_entry.py index f4c74a8aac..21dbf3030e 100644 --- a/erpnext/stock/doctype/stock_reservation_entry/test_stock_reservation_entry.py +++ b/erpnext/stock/doctype/stock_reservation_entry/test_stock_reservation_entry.py @@ -286,6 +286,7 @@ class TestStockReservationEntry(FrappeTestCase): self.assertEqual(item.stock_reserved_qty, sre_details.reserved_qty) self.assertEqual(sre_details.status, "Partially Reserved") + cancel_stock_reservation_entries("Sales Order", so.name) se.cancel() # Test - 3: Stock should be fully Reserved if the Available Qty to Reserve is greater than the Un-reserved Qty. From 1f88b1ef84c826fc35ce3d60a562a3fe8a75b9f2 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Wed, 1 Nov 2023 20:48:09 +0530 Subject: [PATCH 066/130] chore: patch to set reserved stock in Bin --- erpnext/patches.txt | 1 + .../v15_0/set_reserved_stock_in_bin.py | 24 +++++++++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 erpnext/patches/v15_0/set_reserved_stock_in_bin.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 1e5b08bf36..e0f32c55da 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -347,5 +347,6 @@ execute:frappe.db.set_single_value("Payment Reconciliation", "invoice_limit", 50 execute:frappe.db.set_single_value("Payment Reconciliation", "payment_limit", 50) erpnext.patches.v15_0.rename_daily_depreciation_to_depreciation_amount_based_on_num_days_in_month erpnext.patches.v15_0.rename_depreciation_amount_based_on_num_days_in_month_to_daily_prorata_based +erpnext.patches.v15_0.set_reserved_stock_in_bin # below migration patch should always run last erpnext.patches.v14_0.migrate_gl_to_payment_ledger diff --git a/erpnext/patches/v15_0/set_reserved_stock_in_bin.py b/erpnext/patches/v15_0/set_reserved_stock_in_bin.py new file mode 100644 index 0000000000..fd0a23333e --- /dev/null +++ b/erpnext/patches/v15_0/set_reserved_stock_in_bin.py @@ -0,0 +1,24 @@ +import frappe +from frappe.query_builder.functions import Sum + + +def execute(): + sre = frappe.qb.DocType("Stock Reservation Entry") + query = ( + frappe.qb.from_(sre) + .select( + sre.item_code, + sre.warehouse, + Sum(sre.reserved_qty - sre.delivered_qty).as_("reserved_stock"), + ) + .where((sre.docstatus == 1) & (sre.status.notin(["Delivered", "Cancelled"]))) + .groupby(sre.item_code, sre.warehouse) + ) + + for d in query.run(as_dict=True): + frappe.db.set_value( + "Bin", + {"item_code": d.item_code, "warehouse": d.warehouse}, + "reserved_stock", + d.reserved_stock, + ) From 9231706227977951cb69d765f77793f47a6f5c77 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Thu, 2 Nov 2023 10:36:00 +0530 Subject: [PATCH 067/130] fix: qty based check for stock reservation of serial-batch items based on qty --- erpnext/stock/stock_ledger.py | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 1ed1c4759d..e9381d42b9 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -1833,20 +1833,20 @@ def validate_reserved_stock(kwargs): elif batch_nos := [entry.batch_no for entry in sbb_entries if entry.batch_no]: validate_reserved_batch_nos(kwargs.item_code, kwargs.warehouse, batch_nos) - else: - precision = cint(frappe.db.get_default("float_precision")) or 2 - balance_qty = get_stock_balance(kwargs.item_code, kwargs.warehouse) + # Qty based validation for non-serial-batch items OR SRE with Reservation Based On Qty. + precision = cint(frappe.db.get_default("float_precision")) or 2 + balance_qty = get_stock_balance(kwargs.item_code, kwargs.warehouse) - diff = flt(balance_qty - kwargs.get("reserved_stock", 0), precision) - if diff < 0 and abs(diff) > 0.0001: - msg = _("{0} units of {1} needed in {2} on {3} {4} to complete this transaction.").format( - abs(diff), - frappe.get_desk_link("Item", kwargs.item_code), - frappe.get_desk_link("Warehouse", kwargs.warehouse), - nowdate(), - nowtime(), - ) - frappe.throw(msg, title=_("Reserved Stock")) + diff = flt(balance_qty - kwargs.get("reserved_stock", 0), precision) + if diff < 0 and abs(diff) > 0.0001: + msg = _("{0} units of {1} needed in {2} on {3} {4} to complete this transaction.").format( + abs(diff), + frappe.get_desk_link("Item", kwargs.item_code), + frappe.get_desk_link("Warehouse", kwargs.warehouse), + nowdate(), + nowtime(), + ) + frappe.throw(msg, title=_("Reserved Stock")) def validate_reserved_serial_nos(item_code, warehouse, serial_nos): From 54b323e557f605294e8bcdd9eddf2fd4dd66ab38 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Fri, 3 Nov 2023 18:00:49 +0530 Subject: [PATCH 068/130] test: add test case for stock stock reservation --- .../test_stock_reservation_entry.py | 38 ++++++++++++++++++- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/stock_reservation_entry/test_stock_reservation_entry.py b/erpnext/stock/doctype/stock_reservation_entry/test_stock_reservation_entry.py index 21dbf3030e..dd023e2080 100644 --- a/erpnext/stock/doctype/stock_reservation_entry/test_stock_reservation_entry.py +++ b/erpnext/stock/doctype/stock_reservation_entry/test_stock_reservation_entry.py @@ -494,7 +494,7 @@ class TestStockReservationEntry(FrappeTestCase): "pick_serial_and_batch_based_on": "FIFO", }, ) - def test_stock_reservation_from_pick_list(self): + def test_stock_reservation_from_pick_list(self) -> None: items_details = create_items() create_material_receipt(items_details, self.warehouse, qty=100) @@ -576,7 +576,7 @@ class TestStockReservationEntry(FrappeTestCase): "auto_reserve_stock_for_sales_order_on_purchase": 1, }, ) - def test_stock_reservation_from_purchase_receipt(self): + def test_stock_reservation_from_purchase_receipt(self) -> None: from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt from erpnext.selling.doctype.sales_order.sales_order import make_material_request from erpnext.stock.doctype.material_request.material_request import make_purchase_order @@ -646,6 +646,40 @@ class TestStockReservationEntry(FrappeTestCase): # Test - 3: Reserved Serial/Batch Nos should be equal to PR Item Serial/Batch Nos. self.assertEqual(set(sb_details), set(reserved_sb_details)) + @change_settings( + "Stock Settings", + { + "allow_negative_stock": 0, + "enable_stock_reservation": 1, + "auto_reserve_serial_and_batch": 1, + "pick_serial_and_batch_based_on": "FIFO", + }, + ) + def test_consider_reserved_stock_while_cancelling_an_inward_transaction(self) -> None: + items_details = create_items() + se = create_material_receipt(items_details, self.warehouse, qty=100) + + item_list = [] + for item_code, properties in items_details.items(): + item_list.append( + { + "item_code": item_code, + "warehouse": self.warehouse, + "qty": randint(11, 100), + "uom": properties.stock_uom, + "rate": randint(10, 400), + } + ) + + so = make_sales_order( + item_list=item_list, + warehouse=self.warehouse, + ) + so.create_stock_reservation_entries() + + # Test - 1: ValidationError should be thrown as the inwarded stock is reserved. + self.assertRaises(frappe.ValidationError, se.cancel) + def tearDown(self) -> None: cancel_all_stock_reservation_entries() return super().tearDown() From 05f24ede96a30d194db9334a55706689f63462cb Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Sat, 4 Nov 2023 11:23:15 +0530 Subject: [PATCH 069/130] fix: link between parent and child procedure --- .../quality_procedure/quality_procedure.py | 62 ++++++++++++++----- 1 file changed, 46 insertions(+), 16 deletions(-) diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py index e8604080fb..6834abc9d4 100644 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py +++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py @@ -16,16 +16,13 @@ class QualityProcedure(NestedSet): def on_update(self): NestedSet.on_update(self) self.set_parent() + self.remove_parent_from_old_child() + self.add_child_to_parent() + self.remove_child_from_old_parent() def after_insert(self): self.set_parent() - - # add child to parent if missing - if self.parent_quality_procedure: - parent = frappe.get_doc("Quality Procedure", self.parent_quality_procedure) - if not [d for d in parent.processes if d.procedure == self.name]: - parent.append("processes", {"procedure": self.name, "process_description": self.name}) - parent.save() + self.add_child_to_parent() def on_trash(self): # clear from child table (sub procedures) @@ -36,15 +33,6 @@ class QualityProcedure(NestedSet): ) NestedSet.on_trash(self, allow_root_deletion=True) - def set_parent(self): - for process in self.processes: - # Set parent for only those children who don't have a parent - has_parent = frappe.db.get_value( - "Quality Procedure", process.procedure, "parent_quality_procedure" - ) - if not has_parent and process.procedure: - frappe.db.set_value(self.doctype, process.procedure, "parent_quality_procedure", self.name) - def check_for_incorrect_child(self): for process in self.processes: if process.procedure: @@ -61,6 +49,48 @@ class QualityProcedure(NestedSet): title=_("Invalid Child Procedure"), ) + def set_parent(self): + """Set `Parent Procedure` in `Child Procedures`""" + + for process in self.processes: + if process.procedure: + if not frappe.db.get_value("Quality Procedure", process.procedure, "parent_quality_procedure"): + frappe.db.set_value( + "Quality Procedure", process.procedure, "parent_quality_procedure", self.name + ) + + def remove_parent_from_old_child(self): + """Remove `Parent Procedure` from `Old Child Procedures`""" + + if old_doc := self.get_doc_before_save(): + if old_child_procedures := set([d.procedure for d in old_doc.processes if d.procedure]): + current_child_procedures = set([d.procedure for d in self.processes if d.procedure]) + + if removed_child_procedures := list(old_child_procedures.difference(current_child_procedures)): + for child_procedure in removed_child_procedures: + frappe.db.set_value("Quality Procedure", child_procedure, "parent_quality_procedure", None) + + def add_child_to_parent(self): + """Add `Child Procedure` to `Parent Procedure`""" + + if self.parent_quality_procedure: + parent = frappe.get_doc("Quality Procedure", self.parent_quality_procedure) + if not [d for d in parent.processes if d.procedure == self.name]: + parent.append("processes", {"procedure": self.name, "process_description": self.name}) + parent.save() + + def remove_child_from_old_parent(self): + """Remove `Child Procedure` from `Old Parent Procedure`""" + + if old_doc := self.get_doc_before_save(): + if old_parent := old_doc.parent_quality_procedure: + if self.parent_quality_procedure != old_parent: + parent = frappe.get_doc("Quality Procedure", old_parent) + for process in parent.processes: + if process.procedure == self.name: + parent.remove(process) + parent.save() + @frappe.whitelist() def get_children(doctype, parent=None, parent_quality_procedure=None, is_root=False): From 8fbd4cea5b374ddbd611f1c0ada26d9998d82b27 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Sat, 4 Nov 2023 12:46:06 +0530 Subject: [PATCH 070/130] chore: add missing filters for `Parent Procedure` --- .../doctype/quality_procedure/quality_procedure.js | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js index fd2b6a4eaa..79fd2ebdbe 100644 --- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js +++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js @@ -3,10 +3,10 @@ frappe.ui.form.on('Quality Procedure', { refresh: function(frm) { - frm.set_query("procedure","processes", (frm) =>{ + frm.set_query('procedure', 'processes', (frm) =>{ return { filters: { - name: ["not in", [frm.parent_quality_procedure, frm.name]] + name: ['not in', [frm.parent_quality_procedure, frm.name]] } }; }); @@ -14,7 +14,8 @@ frappe.ui.form.on('Quality Procedure', { frm.set_query('parent_quality_procedure', function(){ return { filters: { - is_group: 1 + is_group: 1, + name: ['!=', frm.doc.name] } }; }); From a3191f1c8c08c5723d35f4cf05d44f123ec4ca2e Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 3 Nov 2023 14:37:23 +0530 Subject: [PATCH 071/130] refactor: flag to toggle billed amy update in DN for Credit Note --- .../accounts/doctype/sales_invoice/sales_invoice.json | 10 +++++++++- .../accounts/doctype/sales_invoice/sales_invoice.py | 3 +++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index e5adeae501..cd725b9862 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -26,6 +26,7 @@ "is_return", "return_against", "update_billed_amount_in_sales_order", + "update_billed_amount_in_delivery_note", "is_debit_note", "amended_from", "accounting_dimensions_section", @@ -2153,6 +2154,13 @@ "fieldname": "use_company_roundoff_cost_center", "fieldtype": "Check", "label": "Use Company default Cost Center for Round off" + }, + { + "default": "0", + "depends_on": "eval: doc.is_return", + "fieldname": "update_billed_amount_in_delivery_note", + "fieldtype": "Check", + "label": "Update Billed Amount in Delivery Note" } ], "icon": "fa fa-file-text", @@ -2165,7 +2173,7 @@ "link_fieldname": "consolidated_invoice" } ], - "modified": "2023-07-25 16:02:18.988799", + "modified": "2023-11-03 14:39:38.012346", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index f6d9c93261..45a65278d6 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -253,6 +253,7 @@ class SalesInvoice(SellingController): self.update_status_updater_args() self.update_prevdoc_status() + self.update_billing_status_in_dn() self.clear_unallocated_mode_of_payments() @@ -1429,6 +1430,8 @@ class SalesInvoice(SellingController): ) def update_billing_status_in_dn(self, update_modified=True): + if self.is_return and not self.update_billed_amount_in_delivery_note: + return updated_delivery_notes = [] for d in self.get("items"): if d.dn_detail: From 0c5bdbdcf3fd408b793c755900ba7e3bf958d482 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Sun, 5 Nov 2023 08:32:27 +0530 Subject: [PATCH 072/130] refactor(test): enable billed amt update on Sales Return(Cr Note) --- erpnext/stock/doctype/delivery_note/test_delivery_note.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index 1eecf6dc2a..137c352e99 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -1029,6 +1029,7 @@ class TestDeliveryNote(FrappeTestCase): dn1 = create_delivery_note(is_return=1, return_against=dn.name, qty=-3) si1 = make_sales_invoice(dn1.name) + si1.update_billed_amount_in_delivery_note = True si1.insert() si1.submit() dn1.reload() @@ -1037,6 +1038,7 @@ class TestDeliveryNote(FrappeTestCase): dn2 = create_delivery_note(is_return=1, return_against=dn.name, qty=-4) si2 = make_sales_invoice(dn2.name) + si2.update_billed_amount_in_delivery_note = True si2.insert() si2.submit() dn2.reload() From e5bc8fccb17d96df81e10d3b4b565b430c1b5374 Mon Sep 17 00:00:00 2001 From: viralkansodiya15 <98073516+viralpatel15@users.noreply.github.com> Date: Sun, 5 Nov 2023 11:54:35 +0530 Subject: [PATCH 073/130] fix: list index out of range (#37890) * fix: list index out of range * fix: solve linter test failing --- erpnext/assets/doctype/asset/depreciation.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py index e2a4b2909a..84a428ca54 100644 --- a/erpnext/assets/doctype/asset/depreciation.py +++ b/erpnext/assets/doctype/asset/depreciation.py @@ -780,6 +780,15 @@ def get_disposal_account_and_cost_center(company): def get_value_after_depreciation_on_disposal_date(asset, disposal_date, finance_book=None): asset_doc = frappe.get_doc("Asset", asset) + if asset_doc.available_for_use_date > getdate(disposal_date): + frappe.throw( + "Disposal date {0} cannot be before available for use date {1} of the asset.".format( + disposal_date, asset_doc.available_for_use_date + ) + ) + elif asset_doc.available_for_use_date == getdate(disposal_date): + return flt(asset_doc.gross_purchase_amount - asset_doc.opening_accumulated_depreciation) + if not asset_doc.calculate_depreciation: return flt(asset_doc.value_after_depreciation) From 2b02ef00664b12812a99059bd9a29827231fdd94 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 5 Nov 2023 17:25:05 +0530 Subject: [PATCH 074/130] fix: POS change amount gl entry with no amount (#37799) --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index f6d9c93261..6f49339668 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -1019,7 +1019,7 @@ class SalesInvoice(SellingController): def make_customer_gl_entry(self, gl_entries): # Checked both rounding_adjustment and rounded_total - # because rounded_total had value even before introcution of posting GLE based on rounded total + # because rounded_total had value even before introduction of posting GLE based on rounded total grand_total = ( self.rounded_total if (self.rounding_adjustment and self.rounded_total) else self.grand_total ) @@ -1267,7 +1267,7 @@ class SalesInvoice(SellingController): if skip_change_gl_entries and payment_mode.account == self.account_for_change_amount: payment_mode.base_amount -= flt(self.change_amount) - if payment_mode.amount: + if payment_mode.base_amount: # POS, make payment entries gl_entries.append( self.get_gl_dict( From 0e100cd451892f3024d031ca7b45964c533cdc26 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Mon, 6 Nov 2023 09:51:26 +0530 Subject: [PATCH 075/130] fix: skip check for removed validation --- .../doctype/payment_entry/test_payment_entry.py | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index 3e8292946c..b6b93b6b11 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -683,17 +683,6 @@ class TestPaymentEntry(FrappeTestCase): self.validate_gl_entries(pe.name, expected_gle) def test_payment_against_negative_sales_invoice(self): - pe1 = frappe.new_doc("Payment Entry") - pe1.payment_type = "Pay" - pe1.company = "_Test Company" - pe1.party_type = "Customer" - pe1.party = "_Test Customer" - pe1.paid_from = "_Test Cash - _TC" - pe1.paid_amount = 100 - pe1.received_amount = 100 - - self.assertRaises(InvalidPaymentEntry, pe1.validate) - si1 = create_sales_invoice() # create full payment entry against si1 @@ -751,8 +740,6 @@ class TestPaymentEntry(FrappeTestCase): # pay more than outstanding against si1 pe3 = get_payment_entry("Sales Invoice", si1.name, bank_account="_Test Cash - _TC") - pe3.paid_amount = pe3.received_amount = 300 - self.assertRaises(InvalidPaymentEntry, pe3.validate) # pay negative outstanding against si1 pe3.paid_to = "Debtors - _TC" From 98a8288da2e2516153e30a2e028104f220df3644 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Mon, 6 Nov 2023 09:53:11 +0530 Subject: [PATCH 076/130] fix: handle gle for standalone credit and debit notes --- .../doctype/payment_entry/payment_entry.py | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 23b58947d3..c0e3ab3ed4 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1072,24 +1072,27 @@ class PaymentEntry(AccountsController): against_voucher_type = d.reference_doctype against_voucher = d.reference_name - reverse_dr_or_cr = 0 + reverse_dr_or_cr, standalone_note = 0, 0 if d.reference_doctype in ["Sales Invoice", "Purchase Invoice"]: - is_return = frappe.db.get_value(d.reference_doctype, d.reference_name, "is_return") + is_return, return_against = frappe.db.get_value( + d.reference_doctype, d.reference_name, ["is_return", "return_against"] + ) payable_party_types = get_party_types_from_account_type("Payable") receivable_party_types = get_party_types_from_account_type("Receivable") - if is_return and self.party_type in receivable_party_types and self.payment_type == "Pay": + if is_return and self.party_type in receivable_party_types and (self.payment_type == "Pay"): reverse_dr_or_cr = 1 - elif is_return and self.party_type in payable_party_types and self.payment_type == "Receive": + elif ( + is_return and self.party_type in payable_party_types and (self.payment_type == "Receive") + ): reverse_dr_or_cr = 1 + if is_return and not return_against and not reverse_dr_or_cr: + dr_or_cr = "debit" if dr_or_cr == "credit" else "credit" + gle.update( { - dr_or_cr: abs(allocated_amount_in_company_currency) - if reverse_dr_or_cr - else allocated_amount_in_company_currency, - dr_or_cr + "_in_account_currency": abs(d.allocated_amount) - if reverse_dr_or_cr - else d.allocated_amount, + dr_or_cr: abs(allocated_amount_in_company_currency), + dr_or_cr + "_in_account_currency": abs(d.allocated_amount), "against_voucher_type": against_voucher_type, "against_voucher": against_voucher, "cost_center": cost_center, From 84f0d1ff1ff231f0388033d2304d977b07411253 Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Mon, 6 Nov 2023 10:30:49 +0530 Subject: [PATCH 077/130] chore: linting issues --- .../report/tax_withholding_details/tax_withholding_details.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py b/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py index e842d2e8dc..06c9e44b45 100644 --- a/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py +++ b/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py @@ -316,7 +316,7 @@ def get_tds_docs_query(filters, bank_accounts, tds_accounts): if not tds_accounts: frappe.throw( _("No {0} Accounts found for this company.").format(frappe.bold("Tax Withholding")), - title="Accounts Missing Error", + title=_("Accounts Missing Error"), ) gle = frappe.qb.DocType("GL Entry") query = ( From a9d91189b0e23ccb860e9f954e882ee0ebebbc2c Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Mon, 6 Nov 2023 11:05:04 +0530 Subject: [PATCH 078/130] fix: make `Material Request Item` required if `Material Request` is set in PO --- .../doctype/purchase_order_item/purchase_order_item.json | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json index b1da97d634..2b6ffb752f 100644 --- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json +++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json @@ -470,6 +470,7 @@ "fieldname": "material_request", "fieldtype": "Link", "label": "Material Request", + "mandatory_depends_on": "eval: doc.material_request_item", "no_copy": 1, "oldfieldname": "prevdoc_docname", "oldfieldtype": "Link", @@ -485,6 +486,7 @@ "fieldtype": "Data", "hidden": 1, "label": "Material Request Item", + "mandatory_depends_on": "eval: doc.material_request", "no_copy": 1, "oldfieldname": "prevdoc_detail_docname", "oldfieldtype": "Data", @@ -914,7 +916,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-10-27 15:50:42.655573", + "modified": "2023-11-06 11:00:53.596417", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order Item", From 34d3eb88b39ffb437fc111ed03527f2c3c4baf9b Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Mon, 6 Nov 2023 11:07:09 +0530 Subject: [PATCH 079/130] feat: reserved production plan sub assembly items (#37884) --- .../production_plan/production_plan.json | 9 +- .../production_plan/production_plan.py | 44 ++++++++- .../production_plan/test_production_plan.py | 91 ++++++++++++++++++- .../production_plan_sub_assembly_item.json | 27 ++---- .../doctype/work_order/work_order.py | 36 +++++++- erpnext/stock/doctype/bin/bin.py | 30 +++++- 6 files changed, 212 insertions(+), 25 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.json b/erpnext/manufacturing/doctype/production_plan/production_plan.json index 4a0041662b..49386c4ebc 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.json +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.json @@ -36,6 +36,7 @@ "prod_plan_references", "section_break_24", "combine_sub_items", + "sub_assembly_warehouse", "section_break_ucc4", "skip_available_sub_assembly_item", "column_break_igxl", @@ -416,13 +417,19 @@ { "fieldname": "column_break_igxl", "fieldtype": "Column Break" + }, + { + "fieldname": "sub_assembly_warehouse", + "fieldtype": "Link", + "label": "Sub Assembly Warehouse", + "options": "Warehouse" } ], "icon": "fa fa-calendar", "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2023-09-29 11:41:03.246059", + "modified": "2023-11-03 14:08:11.928027", "modified_by": "Administrator", "module": "Manufacturing", "name": "Production Plan", diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 1850d1e09e..6b12a29b50 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -490,6 +490,12 @@ class ProductionPlan(Document): bin = frappe.get_doc("Bin", bin_name, for_update=True) bin.update_reserved_qty_for_production_plan() + for d in self.sub_assembly_items: + if d.fg_warehouse and d.type_of_manufacturing == "In House": + bin_name = get_or_make_bin(d.production_item, d.fg_warehouse) + bin = frappe.get_doc("Bin", bin_name, for_update=True) + bin.update_reserved_qty_for_for_sub_assembly() + def delete_draft_work_order(self): for d in frappe.get_all( "Work Order", fields=["name"], filters={"docstatus": 0, "production_plan": ("=", self.name)} @@ -809,7 +815,11 @@ class ProductionPlan(Document): bom_data = [] - warehouse = row.warehouse if self.skip_available_sub_assembly_item else None + warehouse = ( + (self.sub_assembly_warehouse or row.warehouse) + if self.skip_available_sub_assembly_item + else None + ) get_sub_assembly_items(row.bom_no, bom_data, row.planned_qty, self.company, warehouse=warehouse) self.set_sub_assembly_items_based_on_level(row, bom_data, manufacturing_type) sub_assembly_items_store.extend(bom_data) @@ -831,7 +841,7 @@ class ProductionPlan(Document): for data in bom_data: data.qty = data.stock_qty data.production_plan_item = row.name - data.fg_warehouse = row.warehouse + data.fg_warehouse = self.sub_assembly_warehouse or row.warehouse data.schedule_date = row.planned_start_date data.type_of_manufacturing = manufacturing_type or ( "Subcontract" if data.is_sub_contracted_item else "In House" @@ -1637,8 +1647,8 @@ def get_reserved_qty_for_production_plan(item_code, warehouse): query = query.run() - if not query: - return 0.0 + if not query or query[0][0] is None: + return None reserved_qty_for_production_plan = flt(query[0][0]) @@ -1780,3 +1790,29 @@ def sales_order_query( query = query.offset(start) return query.run() + + +def get_reserved_qty_for_sub_assembly(item_code, warehouse): + table = frappe.qb.DocType("Production Plan") + child = frappe.qb.DocType("Production Plan Sub Assembly Item") + + query = ( + frappe.qb.from_(table) + .inner_join(child) + .on(table.name == child.parent) + .select(Sum(child.qty - IfNull(child.wo_produced_qty, 0))) + .where( + (table.docstatus == 1) + & (child.production_item == item_code) + & (child.fg_warehouse == warehouse) + & (table.status.notin(["Completed", "Closed"])) + ) + ) + + query = query.run() + + if not query or query[0][0] is None: + return None + + qty = flt(query[0][0]) + return qty if qty > 0 else 0.0 diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py index d414988f41..e9c6ee3af2 100644 --- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py @@ -1042,13 +1042,14 @@ class TestProductionPlan(FrappeTestCase): after_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan")) self.assertEqual(after_qty - before_qty, 1) - pln = frappe.get_doc("Production Plan", pln.name) pln.cancel() bin_name = get_or_make_bin("Raw Material Item 1", "_Test Warehouse - _TC") after_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan")) + pln.reload() + self.assertEqual(pln.docstatus, 2) self.assertEqual(after_qty, before_qty) def test_resered_qty_for_production_plan_for_work_order(self): @@ -1359,6 +1360,93 @@ class TestProductionPlan(FrappeTestCase): if row.item_code == "ChildPart2 For SUB Test": self.assertEqual(row.quantity, 2) + def test_reserve_sub_assembly_items(self): + from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom + from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse + + bom_tree = { + "Fininshed Goods Bicycle": { + "Frame Assembly": {"Frame": {}}, + "Chain Assembly": {"Chain": {}}, + } + } + parent_bom = create_nested_bom(bom_tree, prefix="") + + warehouse = "_Test Warehouse - _TC" + company = "_Test Company" + + sub_assembly_warehouse = create_warehouse("SUB ASSEMBLY WH", company=company) + + for item_code in ["Frame", "Chain"]: + make_stock_entry(item_code=item_code, target=warehouse, qty=2, basic_rate=100) + + before_qty = flt( + frappe.db.get_value( + "Bin", + {"item_code": "Frame Assembly", "warehouse": sub_assembly_warehouse}, + "reserved_qty_for_production_plan", + ) + ) + + plan = create_production_plan( + item_code=parent_bom.item, + planned_qty=2, + ignore_existing_ordered_qty=1, + do_not_submit=1, + skip_available_sub_assembly_item=1, + warehouse=warehouse, + sub_assembly_warehouse=sub_assembly_warehouse, + ) + + plan.get_sub_assembly_items() + plan.submit() + + after_qty = flt( + frappe.db.get_value( + "Bin", + {"item_code": "Frame Assembly", "warehouse": sub_assembly_warehouse}, + "reserved_qty_for_production_plan", + ) + ) + + self.assertEqual(after_qty, before_qty + 2) + + plan.make_work_order() + work_orders = frappe.get_all( + "Work Order", + fields=["name", "production_item"], + filters={"production_plan": plan.name}, + order_by="creation desc", + ) + + for d in work_orders: + wo_doc = frappe.get_doc("Work Order", d.name) + wo_doc.skip_transfer = 1 + wo_doc.from_wip_warehouse = 1 + + wo_doc.wip_warehouse = ( + warehouse + if d.production_item in ["Frame Assembly", "Chain Assembly"] + else sub_assembly_warehouse + ) + + wo_doc.submit() + + if d.production_item == "Frame Assembly": + self.assertEqual(wo_doc.fg_warehouse, sub_assembly_warehouse) + se_doc = frappe.get_doc(make_se_from_wo(wo_doc.name, "Manufacture", 2)) + se_doc.submit() + + after_qty = flt( + frappe.db.get_value( + "Bin", + {"item_code": "Frame Assembly", "warehouse": sub_assembly_warehouse}, + "reserved_qty_for_production_plan", + ) + ) + + self.assertEqual(after_qty, before_qty) + def create_production_plan(**args): """ @@ -1379,6 +1467,7 @@ def create_production_plan(**args): "ignore_existing_ordered_qty": args.ignore_existing_ordered_qty or 0, "get_items_from": "Sales Order", "skip_available_sub_assembly_item": args.skip_available_sub_assembly_item or 0, + "sub_assembly_warehouse": args.sub_assembly_warehouse, } ) diff --git a/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json b/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json index fde0404c01..aff740b732 100644 --- a/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json +++ b/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json @@ -17,11 +17,10 @@ "type_of_manufacturing", "supplier", "work_order_details_section", - "work_order", + "wo_produced_qty", "purchase_order", "production_plan_item", "column_break_7", - "produced_qty", "received_qty", "indent", "section_break_19", @@ -52,13 +51,6 @@ "fieldtype": "Section Break", "label": "Reference" }, - { - "fieldname": "work_order", - "fieldtype": "Link", - "label": "Work Order", - "options": "Work Order", - "read_only": 1 - }, { "fieldname": "column_break_7", "fieldtype": "Column Break" @@ -81,7 +73,8 @@ { "fieldname": "received_qty", "fieldtype": "Float", - "label": "Received Qty" + "label": "Received Qty", + "read_only": 1 }, { "fieldname": "bom_no", @@ -161,12 +154,6 @@ "label": "Target Warehouse", "options": "Warehouse" }, - { - "fieldname": "produced_qty", - "fieldtype": "Data", - "label": "Produced Quantity", - "read_only": 1 - }, { "default": "In House", "fieldname": "type_of_manufacturing", @@ -209,12 +196,18 @@ "label": "Projected Qty", "no_copy": 1, "read_only": 1 + }, + { + "fieldname": "wo_produced_qty", + "fieldtype": "Float", + "label": "Produced Qty", + "read_only": 1 } ], "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2023-05-22 17:52:34.708879", + "modified": "2023-11-03 13:33:42.959387", "modified_by": "Administrator", "module": "Manufacturing", "name": "Production Plan Sub Assembly Item", diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index f9fddcbb5e..36a0cae5cc 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -293,6 +293,7 @@ class WorkOrder(Document): update_produced_qty_in_so_item(self.sales_order, self.sales_order_item) if self.production_plan: + self.set_produced_qty_for_sub_assembly_item() self.update_production_plan_status() def get_transferred_or_manufactured_qty(self, purpose): @@ -569,16 +570,49 @@ class WorkOrder(Document): ) def update_planned_qty(self): + from erpnext.manufacturing.doctype.production_plan.production_plan import ( + get_reserved_qty_for_sub_assembly, + ) + + qty_dict = {"planned_qty": get_planned_qty(self.production_item, self.fg_warehouse)} + + if self.production_plan_sub_assembly_item and self.production_plan: + qty_dict["reserved_qty_for_production_plan"] = get_reserved_qty_for_sub_assembly( + self.production_item, self.fg_warehouse + ) + update_bin_qty( self.production_item, self.fg_warehouse, - {"planned_qty": get_planned_qty(self.production_item, self.fg_warehouse)}, + qty_dict, ) if self.material_request: mr_obj = frappe.get_doc("Material Request", self.material_request) mr_obj.update_requested_qty([self.material_request_item]) + def set_produced_qty_for_sub_assembly_item(self): + table = frappe.qb.DocType("Work Order") + + query = ( + frappe.qb.from_(table) + .select(Sum(table.produced_qty)) + .where( + (table.production_plan == self.production_plan) + & (table.production_plan_sub_assembly_item == self.production_plan_sub_assembly_item) + & (table.docstatus == 1) + ) + ).run() + + produced_qty = flt(query[0][0]) if query else 0 + + frappe.db.set_value( + "Production Plan Sub Assembly Item", + self.production_plan_sub_assembly_item, + "wo_produced_qty", + produced_qty, + ) + def update_ordered_qty(self): if ( self.production_plan diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py index df466ede68..8b2e5cf9ec 100644 --- a/erpnext/stock/doctype/bin/bin.py +++ b/erpnext/stock/doctype/bin/bin.py @@ -34,10 +34,15 @@ class Bin(Document): get_reserved_qty_for_production_plan, ) - self.reserved_qty_for_production_plan = get_reserved_qty_for_production_plan( + reserved_qty_for_production_plan = get_reserved_qty_for_production_plan( self.item_code, self.warehouse ) + if reserved_qty_for_production_plan is None and not self.reserved_qty_for_production_plan: + return + + self.reserved_qty_for_production_plan = flt(reserved_qty_for_production_plan) + self.db_set( "reserved_qty_for_production_plan", flt(self.reserved_qty_for_production_plan), @@ -48,6 +53,29 @@ class Bin(Document): self.set_projected_qty() self.db_set("projected_qty", self.projected_qty, update_modified=True) + def update_reserved_qty_for_for_sub_assembly(self): + from erpnext.manufacturing.doctype.production_plan.production_plan import ( + get_reserved_qty_for_sub_assembly, + ) + + reserved_qty_for_production_plan = get_reserved_qty_for_sub_assembly( + self.item_code, self.warehouse + ) + + if reserved_qty_for_production_plan is None and not self.reserved_qty_for_production_plan: + return + + self.reserved_qty_for_production_plan = flt(reserved_qty_for_production_plan) + self.set_projected_qty() + + self.db_set( + { + "projected_qty": self.projected_qty, + "reserved_qty_for_production_plan": flt(self.reserved_qty_for_production_plan), + }, + update_modified=True, + ) + def update_reserved_qty_for_production(self): """Update qty reserved for production from Production Item tables in open work orders""" From de445b32f5a13d115ce3f49c190f23b1efdf6632 Mon Sep 17 00:00:00 2001 From: vishal Date: Mon, 6 Nov 2023 12:21:47 +0530 Subject: [PATCH 080/130] feat(accounts_receivable): test_case added for multi-select customer group --- .../test_accounts_receivable.py | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py index cbeb6d3106..3582fe0c4a 100644 --- a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py @@ -475,6 +475,30 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): report = execute(filters)[1] self.assertEqual(len(report), 0) + def test_multi_customer_group_filter(self): + si = self.create_sales_invoice() + cus_group = frappe.db.get_value("Customer", self.customer, "customer_group") + # Create a list of customer groups, e.g., ["Group1", "Group2"] + cus_groups_list = [cus_group, "Group2"] + + filters = { + "company": self.company, + "report_date": today(), + "range1": 30, + "range2": 60, + "range3": 90, + "range4": 120, + "customer_group": cus_groups_list, # Use the list of customer groups + } + report = execute(filters)[1] + + # Assert that the report contains data for the specified customer groups + self.assertTrue(len(report) > 0) + + for row in report: + # Assert that the customer group of each row is in the list of customer groups + self.assertIn(row.customer_group, cus_groups_list) + def test_party_account_filter(self): si1 = self.create_sales_invoice() self.customer2 = ( From 30402033bc06296a32ffaf98fd28f18974be2248 Mon Sep 17 00:00:00 2001 From: vishal Date: Mon, 6 Nov 2023 13:02:04 +0530 Subject: [PATCH 081/130] fix: minor change added to test_case --- .../report/accounts_receivable/test_accounts_receivable.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py index 3582fe0c4a..f83285a1a7 100644 --- a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py @@ -479,7 +479,7 @@ class TestAccountsReceivable(AccountsTestMixin, FrappeTestCase): si = self.create_sales_invoice() cus_group = frappe.db.get_value("Customer", self.customer, "customer_group") # Create a list of customer groups, e.g., ["Group1", "Group2"] - cus_groups_list = [cus_group, "Group2"] + cus_groups_list = [cus_group, "_Test Customer Group 1"] filters = { "company": self.company, From 5cce522ecdb7f13a001d4abbf6c3682088edb1b6 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Mon, 6 Nov 2023 17:13:29 +0530 Subject: [PATCH 082/130] fix: don't reset rate if greater than zero in standalone debit note --- erpnext/controllers/buying_controller.py | 38 ++++++++++++------------ 1 file changed, 19 insertions(+), 19 deletions(-) diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index 3a802bd26f..ece08d83f9 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -105,26 +105,26 @@ class BuyingController(SubcontractingController): def set_rate_for_standalone_debit_note(self): if self.get("is_return") and self.get("update_stock") and not self.return_against: for row in self.items: + if row.rate <= 0: + # override the rate with valuation rate + row.rate = get_incoming_rate( + { + "item_code": row.item_code, + "warehouse": row.warehouse, + "posting_date": self.get("posting_date"), + "posting_time": self.get("posting_time"), + "qty": row.qty, + "serial_and_batch_bundle": row.get("serial_and_batch_bundle"), + "company": self.company, + "voucher_type": self.doctype, + "voucher_no": self.name, + }, + raise_error_if_no_rate=False, + ) - # override the rate with valuation rate - row.rate = get_incoming_rate( - { - "item_code": row.item_code, - "warehouse": row.warehouse, - "posting_date": self.get("posting_date"), - "posting_time": self.get("posting_time"), - "qty": row.qty, - "serial_and_batch_bundle": row.get("serial_and_batch_bundle"), - "company": self.company, - "voucher_type": self.doctype, - "voucher_no": self.name, - }, - raise_error_if_no_rate=False, - ) - - row.discount_percentage = 0.0 - row.discount_amount = 0.0 - row.margin_rate_or_amount = 0.0 + row.discount_percentage = 0.0 + row.discount_amount = 0.0 + row.margin_rate_or_amount = 0.0 def set_missing_values(self, for_validate=False): super(BuyingController, self).set_missing_values(for_validate) From 30c6b83a103b262e7bd80601076fdb78a358c068 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Mon, 6 Nov 2023 18:55:06 +0530 Subject: [PATCH 083/130] test: add test case for Quality Procedure` --- .../test_quality_procedure.py | 143 ++++++++++++------ 1 file changed, 97 insertions(+), 46 deletions(-) diff --git a/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py b/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py index 04e8211214..467186debd 100644 --- a/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py +++ b/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py @@ -1,56 +1,107 @@ # Copyright (c) 2018, Frappe and Contributors # See license.txt -import unittest - import frappe +from frappe.tests.utils import FrappeTestCase from .quality_procedure import add_node -class TestQualityProcedure(unittest.TestCase): +class TestQualityProcedure(FrappeTestCase): def test_add_node(self): - try: - procedure = frappe.get_doc( - dict( - doctype="Quality Procedure", - quality_procedure_name="Test Procedure 1", - processes=[dict(process_description="Test Step 1")], - ) - ).insert() - - frappe.local.form_dict = frappe._dict( - doctype="Quality Procedure", - quality_procedure_name="Test Child 1", - parent_quality_procedure=procedure.name, - cmd="test", - is_root="false", - ) - node = add_node() - - procedure.reload() - - self.assertEqual(procedure.is_group, 1) - - # child row created - self.assertTrue([d for d in procedure.processes if d.procedure == node.name]) - - node.delete() - procedure.reload() - - # child unset - self.assertFalse([d for d in procedure.processes if d.name == node.name]) - - finally: - procedure.delete() - - -def create_procedure(): - return frappe.get_doc( - dict( - doctype="Quality Procedure", - quality_procedure_name="Test Procedure 1", - is_group=1, - processes=[dict(process_description="Test Step 1")], + procedure = create_procedure( + { + "quality_procedure_name": "Test Procedure 1", + "is_group": 1, + "processes": [dict(process_description="Test Step 1")], + } ) - ).insert() + + frappe.local.form_dict = frappe._dict( + doctype="Quality Procedure", + quality_procedure_name="Test Child 1", + parent_quality_procedure=procedure.name, + cmd="test", + is_root="false", + ) + node = add_node() + + procedure.reload() + + self.assertEqual(procedure.is_group, 1) + + # child row created + self.assertTrue([d for d in procedure.processes if d.procedure == node.name]) + + node.delete() + procedure.reload() + + # child unset + self.assertFalse([d for d in procedure.processes if d.name == node.name]) + + def test_remove_parent_from_old_child(self): + child_qp = create_procedure( + { + "quality_procedure_name": "Test Child 1", + "is_group": 0, + } + ) + group_qp = create_procedure( + { + "quality_procedure_name": "Test Group", + "is_group": 1, + "processes": [dict(procedure=child_qp.name)], + } + ) + + child_qp.reload() + self.assertEqual(child_qp.parent_quality_procedure, group_qp.name) + + group_qp.reload() + del group_qp.processes[0] + group_qp.save() + + child_qp.reload() + self.assertEqual(child_qp.parent_quality_procedure, None) + + def remove_child_from_old_parent(self): + child_qp = create_procedure( + { + "quality_procedure_name": "Test Child 1", + "is_group": 0, + } + ) + group_qp = create_procedure( + { + "quality_procedure_name": "Test Group", + "is_group": 1, + "processes": [dict(procedure=child_qp.name)], + } + ) + + group_qp.reload() + self.assertTrue([d for d in group_qp.processes if d.procedure == child_qp.name]) + + child_qp.reload() + self.assertEqual(child_qp.parent_quality_procedure, group_qp.name) + + child_qp.parent_quality_procedure = None + child_qp.save() + + group_qp.reload() + self.assertFalse([d for d in group_qp.processes if d.procedure == child_qp.name]) + + +def create_procedure(kwargs=None): + kwargs = frappe._dict(kwargs or {}) + + doc = frappe.new_doc("Quality Procedure") + doc.quality_procedure_name = kwargs.quality_procedure_name or "_Test Procedure" + doc.is_group = kwargs.is_group or 0 + + for process in kwargs.processes or []: + doc.append("processes", process) + + doc.insert() + + return doc From f9fc6c9c9d5faef90f345df6e04ea4d2b8b8b69b Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Mon, 6 Nov 2023 18:01:05 +0530 Subject: [PATCH 084/130] fix(test): `test_gl_entries_for_standalone_debit_note` --- .../doctype/purchase_invoice/test_purchase_invoice.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 13593bcf9b..ae1f2d8570 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1783,9 +1783,14 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin): set_advance_flag(company="_Test Company", flag=0, default_account="") def test_gl_entries_for_standalone_debit_note(self): - make_purchase_invoice(qty=5, rate=500, update_stock=True) + from erpnext.stock.doctype.item.test_item import make_item - returned_inv = make_purchase_invoice(qty=-5, rate=5, update_stock=True, is_return=True) + item_code = make_item(properties={"is_stock_item": 1}) + make_purchase_invoice(item_code=item_code, qty=5, rate=500, update_stock=True) + + returned_inv = make_purchase_invoice( + item_code=item_code, qty=-5, rate=5, update_stock=True, is_return=True + ) # override the rate with valuation rate sle = frappe.get_all( @@ -1795,7 +1800,7 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin): )[0] rate = flt(sle.stock_value_difference) / flt(sle.actual_qty) - self.assertAlmostEqual(returned_inv.items[0].rate, rate) + self.assertAlmostEqual(rate, 500) def test_payment_allocation_for_payment_terms(self): from erpnext.buying.doctype.purchase_order.test_purchase_order import ( From 8722318081e30544b4cc76c97bdd1fe3362373d6 Mon Sep 17 00:00:00 2001 From: hyaray Date: Mon, 6 Nov 2023 22:19:45 +0800 Subject: [PATCH 085/130] fix: add translation wrapper --- erpnext/manufacturing/doctype/work_order/work_order.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js index 58945bba77..d9cc212e8c 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.js +++ b/erpnext/manufacturing/doctype/work_order/work_order.js @@ -710,7 +710,7 @@ erpnext.work_order = { return new Promise((resolve, reject) => { frappe.prompt({ fieldtype: 'Float', - label: __('Qty for {0}', [purpose]), + label: __('Qty for {0}', [__(purpose)]), fieldname: 'qty', description: __('Max: {0}', [max]), default: max From 67e74d03edb5bd3190a44df811a30b2b130eeb39 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 6 Nov 2023 20:23:26 +0530 Subject: [PATCH 086/130] fix: typo in AR report --- .../report/accounts_receivable/accounts_receivable.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index f24a24e42e..12e40037f1 100755 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -117,7 +117,7 @@ class ReceivablePayableReport(object): for ple in self.ple_entries: # get the balance object for voucher_type - if self.filters.get("ingore_accounts"): + if self.filters.get("ignore_accounts"): key = (ple.voucher_type, ple.voucher_no, ple.party) else: key = (ple.account, ple.voucher_type, ple.voucher_no, ple.party) @@ -188,7 +188,7 @@ class ReceivablePayableReport(object): ): return - if self.filters.get("ingore_accounts"): + if self.filters.get("ignore_accounts"): key = (ple.against_voucher_type, ple.against_voucher_no, ple.party) else: key = (ple.account, ple.against_voucher_type, ple.against_voucher_no, ple.party) @@ -200,7 +200,7 @@ class ReceivablePayableReport(object): if ple.against_voucher_no in self.return_entries: return_against = self.return_entries.get(ple.against_voucher_no) if return_against: - if self.filters.get("ingore_accounts"): + if self.filters.get("ignore_accounts"): key = (ple.against_voucher_type, return_against, ple.party) else: key = (ple.account, ple.against_voucher_type, return_against, ple.party) @@ -209,7 +209,7 @@ class ReceivablePayableReport(object): if not row: # no invoice, this is an invoice / stand-alone payment / credit note - if self.filters.get("ingore_accounts"): + if self.filters.get("ignore_accounts"): row = self.voucher_balance.get((ple.voucher_type, ple.voucher_no, ple.party)) else: row = self.voucher_balance.get((ple.account, ple.voucher_type, ple.voucher_no, ple.party)) From d582a7379580367e7d81bed58ed02773bd15d00c Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 7 Nov 2023 10:02:08 +0530 Subject: [PATCH 087/130] feat: settings page for repost --- .../__init__.py | 0 .../repost_accounting_ledger_settings.js | 8 ++++ .../repost_accounting_ledger_settings.json | 41 +++++++++++++++++ .../repost_accounting_ledger_settings.py | 9 ++++ .../test_repost_accounting_ledger_settings.py | 9 ++++ .../doctype/repost_allowed_types/__init__.py | 0 .../repost_allowed_types.json | 45 +++++++++++++++++++ .../repost_allowed_types.py | 9 ++++ 8 files changed, 121 insertions(+) create mode 100644 erpnext/accounts/doctype/repost_accounting_ledger_settings/__init__.py create mode 100644 erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.js create mode 100644 erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.json create mode 100644 erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.py create mode 100644 erpnext/accounts/doctype/repost_accounting_ledger_settings/test_repost_accounting_ledger_settings.py create mode 100644 erpnext/accounts/doctype/repost_allowed_types/__init__.py create mode 100644 erpnext/accounts/doctype/repost_allowed_types/repost_allowed_types.json create mode 100644 erpnext/accounts/doctype/repost_allowed_types/repost_allowed_types.py diff --git a/erpnext/accounts/doctype/repost_accounting_ledger_settings/__init__.py b/erpnext/accounts/doctype/repost_accounting_ledger_settings/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.js b/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.js new file mode 100644 index 0000000000..8c83ca5043 --- /dev/null +++ b/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.js @@ -0,0 +1,8 @@ +// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt + +// frappe.ui.form.on("Repost Accounting Ledger Settings", { +// refresh(frm) { + +// }, +// }); diff --git a/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.json b/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.json new file mode 100644 index 0000000000..8aaa6e8f0a --- /dev/null +++ b/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.json @@ -0,0 +1,41 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2023-11-07 09:57:20.619939", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "allowed_types" + ], + "fields": [ + { + "fieldname": "allowed_types", + "fieldtype": "Table", + "label": "Allowed Doctypes", + "options": "Repost Allowed Types" + } + ], + "in_create": 1, + "issingle": 1, + "links": [], + "modified": "2023-11-07 10:12:07.519155", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Repost Accounting Ledger Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.py b/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.py new file mode 100644 index 0000000000..2b8230df86 --- /dev/null +++ b/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.py @@ -0,0 +1,9 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class RepostAccountingLedgerSettings(Document): + pass diff --git a/erpnext/accounts/doctype/repost_accounting_ledger_settings/test_repost_accounting_ledger_settings.py b/erpnext/accounts/doctype/repost_accounting_ledger_settings/test_repost_accounting_ledger_settings.py new file mode 100644 index 0000000000..ec4e87ffc0 --- /dev/null +++ b/erpnext/accounts/doctype/repost_accounting_ledger_settings/test_repost_accounting_ledger_settings.py @@ -0,0 +1,9 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestRepostAccountingLedgerSettings(FrappeTestCase): + pass diff --git a/erpnext/accounts/doctype/repost_allowed_types/__init__.py b/erpnext/accounts/doctype/repost_allowed_types/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/erpnext/accounts/doctype/repost_allowed_types/repost_allowed_types.json b/erpnext/accounts/doctype/repost_allowed_types/repost_allowed_types.json new file mode 100644 index 0000000000..ede12fbc18 --- /dev/null +++ b/erpnext/accounts/doctype/repost_allowed_types/repost_allowed_types.json @@ -0,0 +1,45 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2023-11-07 09:58:03.595382", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "document_type", + "column_break_sfzb", + "allowed" + ], + "fields": [ + { + "fieldname": "document_type", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Doctype", + "options": "DocType" + }, + { + "default": "0", + "fieldname": "allowed", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Allowed" + }, + { + "fieldname": "column_break_sfzb", + "fieldtype": "Column Break" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2023-11-07 10:01:39.217861", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Repost Allowed Types", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/repost_allowed_types/repost_allowed_types.py b/erpnext/accounts/doctype/repost_allowed_types/repost_allowed_types.py new file mode 100644 index 0000000000..0e4883b0c9 --- /dev/null +++ b/erpnext/accounts/doctype/repost_allowed_types/repost_allowed_types.py @@ -0,0 +1,9 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + + +class RepostAllowedTypes(Document): + pass From ebb186c8df6cd713800475d490962e75ea6ab6a6 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 7 Nov 2023 10:12:18 +0530 Subject: [PATCH 088/130] chore: patch to update default repost settings value --- erpnext/patches.txt | 1 + .../patches/v14_0/add_default_for_repost_settings.py | 12 ++++++++++++ 2 files changed, 13 insertions(+) create mode 100644 erpnext/patches/v14_0/add_default_for_repost_settings.py diff --git a/erpnext/patches.txt b/erpnext/patches.txt index e0f32c55da..d394db6d96 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -345,6 +345,7 @@ erpnext.patches.v14_0.rename_over_order_allowance_field erpnext.patches.v14_0.migrate_delivery_stop_lock_field execute:frappe.db.set_single_value("Payment Reconciliation", "invoice_limit", 50) execute:frappe.db.set_single_value("Payment Reconciliation", "payment_limit", 50) +erpnext.patches.v14_0.add_default_for_repost_settings erpnext.patches.v15_0.rename_daily_depreciation_to_depreciation_amount_based_on_num_days_in_month erpnext.patches.v15_0.rename_depreciation_amount_based_on_num_days_in_month_to_daily_prorata_based erpnext.patches.v15_0.set_reserved_stock_in_bin diff --git a/erpnext/patches/v14_0/add_default_for_repost_settings.py b/erpnext/patches/v14_0/add_default_for_repost_settings.py new file mode 100644 index 0000000000..6cafc66aab --- /dev/null +++ b/erpnext/patches/v14_0/add_default_for_repost_settings.py @@ -0,0 +1,12 @@ +import frappe + + +def execute(): + """ + Update Repost Accounting Ledger Settings with default values + """ + allowed_types = ["Sales Invoice", "Purchase Invoice", "Payment Entry", "Journal Entry"] + repost_settings = frappe.get_doc("Repost Accounting Ledger Settings") + for x in allowed_types: + repost_settings.append("allowed_types", {"document_type": x, "allowed": True}) + repost_settings.save() From 5a068410c6ff4bc4ac100098c712d09bb70854df Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 7 Nov 2023 11:03:03 +0530 Subject: [PATCH 089/130] refactor: configurable repost settings --- .../repost_accounting_ledger.js | 4 +--- .../repost_accounting_ledger.py | 24 ++++++++++++++++--- 2 files changed, 22 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.js b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.js index 3a87a380d1..c7b7a148cf 100644 --- a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.js +++ b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.js @@ -5,9 +5,7 @@ frappe.ui.form.on("Repost Accounting Ledger", { setup: function(frm) { frm.fields_dict['vouchers'].grid.get_field('voucher_type').get_query = function(doc) { return { - filters: { - name: ['in', ['Purchase Invoice', 'Sales Invoice', 'Payment Entry', 'Journal Entry']], - } + query: "erpnext.accounts.doctype.repost_accounting_ledger.repost_accounting_ledger.get_repost_allowed_types" } } diff --git a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py index dbb0971fde..1d17a1c20e 100644 --- a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py +++ b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py @@ -10,9 +10,12 @@ from frappe.utils.data import comma_and class RepostAccountingLedger(Document): def __init__(self, *args, **kwargs): super(RepostAccountingLedger, self).__init__(*args, **kwargs) - self._allowed_types = set( - ["Purchase Invoice", "Sales Invoice", "Payment Entry", "Journal Entry"] - ) + self._allowed_types = [ + x.document_type + for x in frappe.db.get_all( + "Repost Allowed Types", filters={"allowed": True}, fields=["document_type"] + ) + ] def validate(self): self.validate_vouchers() @@ -186,3 +189,18 @@ def validate_docs_for_deferred_accounting(sales_docs, purchase_docs): frappe.bold(comma_and([x[0] for x in docs_with_deferred_expense + docs_with_deferred_revenue])) ) ) + + +@frappe.whitelist() +@frappe.validate_and_sanitize_search_inputs +def get_repost_allowed_types(doctype, txt, searchfield, start, page_len, filters): + filters = {"allowed": True} + + if txt: + filters.update({"document_type": ("like", f"%{txt}%")}) + + if allowed_types := frappe.db.get_all( + "Repost Allowed Types", filters=filters, fields=["document_type"], as_list=1 + ): + return allowed_types + return [] From b651b36fff496a92713f634868a63a4ebe24f8c9 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 7 Nov 2023 11:10:42 +0530 Subject: [PATCH 090/130] refactor: support for expense claim repost --- .../repost_accounting_ledger/repost_accounting_ledger.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py index 1d17a1c20e..8aaafd0b16 100644 --- a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py +++ b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py @@ -160,7 +160,7 @@ def start_repost(account_repost_doc=str) -> None: doc.docstatus = 1 doc.make_gl_entries() - elif doc.doctype in ["Payment Entry", "Journal Entry"]: + elif doc.doctype in ["Payment Entry", "Journal Entry", "Expense Claim"]: if not repost_doc.delete_cancelled_entries: doc.make_gl_entries(1) doc.make_gl_entries() From adff2871606c8f0ed51971cd65db355d38032fb9 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 7 Nov 2023 12:12:27 +0530 Subject: [PATCH 091/130] fix: type error on new payment entry --- erpnext/accounts/doctype/payment_entry/payment_entry.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js index 0203c45058..b3ae627c6e 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.js +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js @@ -154,7 +154,7 @@ frappe.ui.form.on('Payment Entry', { frm.events.set_dynamic_labels(frm); frm.events.show_general_ledger(frm); erpnext.accounts.ledger_preview.show_accounting_ledger_preview(frm); - if(frm.doc.references.find((elem) => {return elem.exchange_gain_loss != 0})) { + if((frm.doc.references) && (frm.doc.references.find((elem) => {return elem.exchange_gain_loss != 0}))) { frm.add_custom_button(__("View Exchange Gain/Loss Journals"), function() { frappe.set_route("List", "Journal Entry", {"voucher_type": "Exchange Gain Or Loss", "reference_name": frm.doc.name}); }, __('Actions')); From ac79b8483f0769ab7fd3dc4975b7be34e15b32b2 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 7 Nov 2023 12:46:06 +0530 Subject: [PATCH 092/130] refactor(test): update repost settings for test cases --- .../test_repost_accounting_ledger.py | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/erpnext/accounts/doctype/repost_accounting_ledger/test_repost_accounting_ledger.py b/erpnext/accounts/doctype/repost_accounting_ledger/test_repost_accounting_ledger.py index 0e75dd2e3e..dda0ec778f 100644 --- a/erpnext/accounts/doctype/repost_accounting_ledger/test_repost_accounting_ledger.py +++ b/erpnext/accounts/doctype/repost_accounting_ledger/test_repost_accounting_ledger.py @@ -20,10 +20,18 @@ class TestRepostAccountingLedger(AccountsTestMixin, FrappeTestCase): self.create_company() self.create_customer() self.create_item() + self.update_repost_settings() def teadDown(self): frappe.db.rollback() + def update_repost_settings(self): + allowed_types = ["Sales Invoice", "Purchase Invoice", "Payment Entry", "Journal Entry"] + repost_settings = frappe.get_doc("Repost Accounting Ledger Settings") + for x in allowed_types: + repost_settings.append("allowed_types", {"document_type": x, "allowed": True}) + repost_settings.save() + def test_01_basic_functions(self): si = create_sales_invoice( item=self.item, From 61705047b037f91a735207d18635de6263af804d Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 7 Nov 2023 13:42:09 +0530 Subject: [PATCH 093/130] refactor: select distinct types --- .../repost_accounting_ledger/repost_accounting_ledger.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py index 8aaafd0b16..69cfe9fcd7 100644 --- a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py +++ b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py @@ -13,7 +13,7 @@ class RepostAccountingLedger(Document): self._allowed_types = [ x.document_type for x in frappe.db.get_all( - "Repost Allowed Types", filters={"allowed": True}, fields=["document_type"] + "Repost Allowed Types", filters={"allowed": True}, fields=["distinct(document_type)"] ) ] @@ -200,7 +200,7 @@ def get_repost_allowed_types(doctype, txt, searchfield, start, page_len, filters filters.update({"document_type": ("like", f"%{txt}%")}) if allowed_types := frappe.db.get_all( - "Repost Allowed Types", filters=filters, fields=["document_type"], as_list=1 + "Repost Allowed Types", filters=filters, fields=["distinct(document_type)"], as_list=1 ): return allowed_types return [] From 11c8d9fcf1e7eef1890b719b37c823822b6a108d Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 7 Nov 2023 14:18:07 +0530 Subject: [PATCH 094/130] refactor(test): repost test case for purchase invoice --- .../doctype/purchase_invoice/test_purchase_invoice.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index 13593bcf9b..d2adc514b0 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -1898,6 +1898,12 @@ class TestPurchaseInvoice(FrappeTestCase, StockTestMixin): disable_dimension() def test_repost_accounting_entries(self): + # update repost settings + settings = frappe.get_doc("Repost Accounting Ledger Settings") + if not [x for x in settings.allowed_types if x.document_type == "Purchase Invoice"]: + settings.append("allowed_types", {"document_type": "Purchase Invoice", "allowed": True}) + settings.save() + pi = make_purchase_invoice( rate=1000, price_list_rate=1000, From 10b9570429e16906f3f879228c065b668f63882b Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 7 Nov 2023 14:30:06 +0530 Subject: [PATCH 095/130] refactor: update permissions for repost settings --- .../repost_accounting_ledger_settings.json | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.json b/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.json index 8aaa6e8f0a..8aa0a840c7 100644 --- a/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.json +++ b/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.json @@ -1,6 +1,5 @@ { "actions": [], - "allow_rename": 1, "creation": "2023-11-07 09:57:20.619939", "doctype": "DocType", "engine": "InnoDB", @@ -18,7 +17,7 @@ "in_create": 1, "issingle": 1, "links": [], - "modified": "2023-11-07 10:12:07.519155", + "modified": "2023-11-07 14:24:13.321522", "modified_by": "Administrator", "module": "Accounts", "name": "Repost Accounting Ledger Settings", @@ -30,12 +29,18 @@ "email": 1, "print": 1, "read": 1, - "role": "System Manager", + "role": "Administrator", "share": 1, "write": 1 + }, + { + "read": 1, + "role": "System Manager", + "select": 1 } ], "sort_field": "modified", "sort_order": "DESC", - "states": [] + "states": [], + "track_changes": 1 } \ No newline at end of file From ee60fa940cdcd6c2bd6c47d875e055275535d216 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Tue, 7 Nov 2023 17:33:29 +0530 Subject: [PATCH 096/130] chore: typo in `Stock Entry` enqueue msg --- erpnext/stock/doctype/stock_entry/stock_entry.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py index c41349fcfb..b06de2e2fc 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.py +++ b/erpnext/stock/doctype/stock_entry/stock_entry.py @@ -161,7 +161,7 @@ class StockEntry(StockController): if self.is_enqueue_action(): frappe.msgprint( _( - "The task has been enqueued as a background job. In case there is any issue on processing in background, the system will add a comment about the error on this Stock Reconciliation and revert to the Draft stage" + "The task has been enqueued as a background job. In case there is any issue on processing in background, the system will add a comment about the error on this Stock Entry and revert to the Draft stage" ) ) self.queue_action("submit", timeout=2000) @@ -172,7 +172,7 @@ class StockEntry(StockController): if self.is_enqueue_action(): frappe.msgprint( _( - "The task has been enqueued as a background job. In case there is any issue on processing in background, the system will add a comment about the error on this Stock Reconciliation and revert to the Submitted stage" + "The task has been enqueued as a background job. In case there is any issue on processing in background, the system will add a comment about the error on this Stock Entry and revert to the Submitted stage" ) ) self.queue_action("cancel", timeout=2000) From 416bd400bb94c1fe115f6929e9252c4ce9f3cd40 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Tue, 7 Nov 2023 17:38:07 +0530 Subject: [PATCH 097/130] refactor: optimize for speed --- erpnext/utilities/bulk_transaction.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/erpnext/utilities/bulk_transaction.py b/erpnext/utilities/bulk_transaction.py index 5e57b31793..fcee265644 100644 --- a/erpnext/utilities/bulk_transaction.py +++ b/erpnext/utilities/bulk_transaction.py @@ -192,9 +192,7 @@ def mark_retrired_transaction(log_doc, doc_name): record = 0 for d in log_doc.get("logger_data"): if d.transaction_name == doc_name and d.transaction_status == "Failed": - d.retried = 1 + frappe.db.set_value("Bulk Transaction Log Detail", d.name, "retried", 1) record = record + 1 - log_doc.save() - return record From 162c0497d1db69e3af5f142459e3a875f03962af Mon Sep 17 00:00:00 2001 From: marination Date: Tue, 7 Nov 2023 14:44:04 +0100 Subject: [PATCH 098/130] test: `get_outstanding_reference_documents` (triggered via UI) --- .../payment_entry/test_payment_entry.py | 42 ++++++++++++++++++- 1 file changed, 41 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index edfec41918..b3511f0f52 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -6,10 +6,11 @@ import unittest import frappe from frappe import qb from frappe.tests.utils import FrappeTestCase, change_settings -from frappe.utils import flt, nowdate +from frappe.utils import add_days, flt, nowdate from erpnext.accounts.doctype.payment_entry.payment_entry import ( InvalidPaymentEntry, + get_outstanding_reference_documents, get_payment_entry, get_reference_details, ) @@ -1262,6 +1263,42 @@ class TestPaymentEntry(FrappeTestCase): so.reload() self.assertEqual(so.advance_paid, so.rounded_total) + def test_outstanding_invoices_api(self): + """ + Test if `get_outstanding_reference_documents` fetches invoices in the right order. + """ + customer = create_customer("Max Mustermann", "INR") + create_payment_terms_template() + + si = create_sales_invoice(qty=1, rate=100, customer=customer) + si2 = create_sales_invoice(do_not_save=1, qty=1, rate=100, customer=customer) + si2.payment_terms_template = "Test Receivable Template" + si2.submit() + + args = { + "posting_date": nowdate(), + "company": "_Test Company", + "party_type": "Customer", + "payment_type": "Pay", + "party": customer, + "party_account": "Debtors - _TC", + } + args.update( + { + "get_outstanding_invoices": True, + "from_posting_date": nowdate(), + "to_posting_date": add_days(nowdate(), 7), + } + ) + references = get_outstanding_reference_documents(args) + + self.assertEqual(len(references), 3) + self.assertEqual(references[0].voucher_no, si.name) + self.assertEqual(references[1].voucher_no, si2.name) + self.assertEqual(references[2].voucher_no, si2.name) + self.assertEqual(references[1].payment_term, "Basic Amount Receivable") + self.assertEqual(references[2].payment_term, "Tax Receivable") + def create_payment_entry(**args): payment_entry = frappe.new_doc("Payment Entry") @@ -1322,6 +1359,9 @@ def create_payment_terms_template(): def create_payment_terms_template_with_discount( name=None, discount_type=None, discount=None, template_name=None ): + """ + Create a Payment Terms Template with % or amount discount. + """ create_payment_term(name or "30 Credit Days with 10% Discount") template_name = template_name or "Test Discount Template" From 2984a86f37e72bbdefd0348d7c456bd6ada540bb Mon Sep 17 00:00:00 2001 From: Gursheen Anand Date: Wed, 8 Nov 2023 12:30:24 +0530 Subject: [PATCH 099/130] fix: use get_all instead of get_list --- erpnext/accounts/utils.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 0a116a7345..aef0c38c63 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -2044,4 +2044,4 @@ def create_gain_loss_journal( def get_party_types_from_account_type(account_type): - return frappe.db.get_list("Party Type", {"account_type": account_type}, pluck="name") + return frappe.db.get_all("Party Type", {"account_type": account_type}, pluck="name") From 33eedb97dcb531cc8e14813d742266f2c5168fac Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Wed, 8 Nov 2023 12:38:09 +0530 Subject: [PATCH 100/130] fix: ignore Stock Reservation for future dated PR --- .../doctype/purchase_receipt/purchase_receipt.py | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py index cbc1693eaa..d905fe5ea6 100644 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py @@ -7,7 +7,7 @@ from frappe import _, throw from frappe.desk.notifications import clear_doctype_notifications from frappe.model.mapper import get_mapped_doc from frappe.query_builder.functions import CombineDatetime -from frappe.utils import cint, flt, getdate, nowdate +from frappe.utils import cint, flt, get_datetime, getdate, nowdate from pypika import functions as fn import erpnext @@ -760,8 +760,12 @@ class PurchaseReceipt(BuyingController): update_billing_percentage(pr_doc, update_modified=update_modified) def reserve_stock_for_sales_order(self): - if self.is_return or not cint( - frappe.db.get_single_value("Stock Settings", "auto_reserve_stock_for_sales_order_on_purchase") + if ( + self.is_return + or not frappe.db.get_single_value("Stock Settings", "enable_stock_reservation") + or not frappe.db.get_single_value( + "Stock Settings", "auto_reserve_stock_for_sales_order_on_purchase" + ) ): return @@ -782,6 +786,11 @@ class PurchaseReceipt(BuyingController): so_items_details_map.setdefault(item.sales_order, []).append(item_details) if so_items_details_map: + if get_datetime("{} {}".format(self.posting_date, self.posting_time)) > get_datetime(): + return frappe.msgprint( + _("Cannot create Stock Reservation Entries for future dated Purchase Receipts.") + ) + for so, items_details in so_items_details_map.items(): so_doc = frappe.get_doc("Sales Order", so) so_doc.create_stock_reservation_entries( From 4783e4beee88674aa96eca52321e4b8677a24c17 Mon Sep 17 00:00:00 2001 From: Wolfram Schmidt Date: Wed, 8 Nov 2023 10:42:47 +0100 Subject: [PATCH 101/130] Update de.csv (#37981) added translation for DocType Competitor https://www.duden.de/rechtschreibung/Konkurrent --- erpnext/translations/de.csv | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index c627f81984..c856fefcc0 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -533,6 +533,7 @@ Company currencies of both the companies should match for Inter Company Transact Company is manadatory for company account,Bitte gib ein Unternehmen für dieses Unternehmenskonto an., Company name not same,Firma nicht gleich, Company {0} does not exist,Unternehmen {0} existiert nicht, +Competitor,Konkurrent, Compensatory leave request days not in valid holidays,"Tage des Ausgleichsurlaubs, die nicht in den gültigen Feiertagen sind", Complaint,Beschwerde, Completion Date,Fertigstellungstermin, From 70d99eebc065c9454674101a112e6824e5c057f1 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Wed, 8 Nov 2023 18:38:39 +0530 Subject: [PATCH 102/130] Revert "fix: set empty value for tax template in item details (#37496)" This reverts commit b0d440c34b9cb4d0e0d75153c279ccaa6206253d. --- .../doctype/pos_invoice/test_pos_invoice.py | 115 +++++++++--------- .../sales_invoice/test_sales_invoice.py | 112 +++++++++-------- erpnext/stock/doctype/item/test_item.py | 10 +- erpnext/stock/get_item_details.py | 1 - 4 files changed, 120 insertions(+), 118 deletions(-) diff --git a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py index 982bdc198a..200b82a447 100644 --- a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py @@ -6,7 +6,6 @@ import unittest import frappe from frappe import _ -from frappe.utils import add_days, nowdate from erpnext.accounts.doctype.pos_invoice.pos_invoice import make_sales_return from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile @@ -126,64 +125,70 @@ class TestPOSInvoice(unittest.TestCase): self.assertEqual(inv.grand_total, 5474.0) def test_tax_calculation_with_item_tax_template(self): - import json + inv = create_pos_invoice(qty=84, rate=4.6, do_not_save=1) + item_row = inv.get("items")[0] - from erpnext.stock.get_item_details import get_item_details - - # set tax template in item - item = frappe.get_cached_doc("Item", "_Test Item") - item.set( - "taxes", - [ - { - "item_tax_template": "_Test Account Excise Duty @ 15 - _TC", - "valid_from": add_days(nowdate(), -5), - } - ], - ) - item.save() - - # create POS invoice with item - pos_inv = create_pos_invoice(qty=84, rate=4.6, do_not_save=True) - item_details = get_item_details( - doc=pos_inv, - args={ - "item_code": item.item_code, - "company": pos_inv.company, - "doctype": "POS Invoice", - "conversion_rate": 1.0, - }, - ) - tax_map = json.loads(item_details.item_tax_rate) - for tax in tax_map: - pos_inv.append( - "taxes", - { - "charge_type": "On Net Total", - "account_head": tax, - "rate": tax_map[tax], - "description": "Test", - "cost_center": "_Test Cost Center - _TC", - }, - ) - pos_inv.submit() - pos_inv.load_from_db() - - # check if correct tax values are applied from tax template - self.assertEqual(pos_inv.net_total, 386.4) - - expected_taxes = [ - { - "tax_amount": 57.96, - "total": 444.36, - }, + add_items = [ + (54, "_Test Account Excise Duty @ 12 - _TC"), + (288, "_Test Account Excise Duty @ 15 - _TC"), + (144, "_Test Account Excise Duty @ 20 - _TC"), + (430, "_Test Item Tax Template 1 - _TC"), ] + for qty, item_tax_template in add_items: + item_row_copy = copy.deepcopy(item_row) + item_row_copy.qty = qty + item_row_copy.item_tax_template = item_tax_template + inv.append("items", item_row_copy) - for i in range(len(expected_taxes)): - for key in expected_taxes[i]: - self.assertEqual(expected_taxes[i][key], pos_inv.get("taxes")[i].get(key)) + inv.append( + "taxes", + { + "account_head": "_Test Account Excise Duty - _TC", + "charge_type": "On Net Total", + "cost_center": "_Test Cost Center - _TC", + "description": "Excise Duty", + "doctype": "Sales Taxes and Charges", + "rate": 11, + }, + ) + inv.append( + "taxes", + { + "account_head": "_Test Account Education Cess - _TC", + "charge_type": "On Net Total", + "cost_center": "_Test Cost Center - _TC", + "description": "Education Cess", + "doctype": "Sales Taxes and Charges", + "rate": 0, + }, + ) + inv.append( + "taxes", + { + "account_head": "_Test Account S&H Education Cess - _TC", + "charge_type": "On Net Total", + "cost_center": "_Test Cost Center - _TC", + "description": "S&H Education Cess", + "doctype": "Sales Taxes and Charges", + "rate": 3, + }, + ) + inv.insert() - self.assertEqual(pos_inv.get("base_total_taxes_and_charges"), 57.96) + self.assertEqual(inv.net_total, 4600) + + self.assertEqual(inv.get("taxes")[0].tax_amount, 502.41) + self.assertEqual(inv.get("taxes")[0].total, 5102.41) + + self.assertEqual(inv.get("taxes")[1].tax_amount, 197.80) + self.assertEqual(inv.get("taxes")[1].total, 5300.21) + + self.assertEqual(inv.get("taxes")[2].tax_amount, 375.36) + self.assertEqual(inv.get("taxes")[2].total, 5675.57) + + self.assertEqual(inv.grand_total, 5675.57) + self.assertEqual(inv.rounding_adjustment, 0.43) + self.assertEqual(inv.rounded_total, 5676.0) def test_tax_calculation_with_multiple_items_and_discount(self): inv = create_pos_invoice(qty=1, rate=75, do_not_save=True) diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index 21cc253959..5a41336b2f 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -516,72 +516,70 @@ class TestSalesInvoice(FrappeTestCase): self.assertEqual(si.grand_total, 5474.0) def test_tax_calculation_with_item_tax_template(self): - import json - - from erpnext.stock.get_item_details import get_item_details - - # set tax template in item - item = frappe.get_cached_doc("Item", "_Test Item") - item.set( - "taxes", - [ - { - "item_tax_template": "_Test Item Tax Template 1 - _TC", - "valid_from": add_days(nowdate(), -5), - } - ], - ) - item.save() - - # create sales invoice with item si = create_sales_invoice(qty=84, rate=4.6, do_not_save=True) - item_details = get_item_details( - doc=si, - args={ - "item_code": item.item_code, - "company": si.company, - "doctype": "Sales Invoice", - "conversion_rate": 1.0, + item_row = si.get("items")[0] + + add_items = [ + (54, "_Test Account Excise Duty @ 12 - _TC"), + (288, "_Test Account Excise Duty @ 15 - _TC"), + (144, "_Test Account Excise Duty @ 20 - _TC"), + (430, "_Test Item Tax Template 1 - _TC"), + ] + for qty, item_tax_template in add_items: + item_row_copy = copy.deepcopy(item_row) + item_row_copy.qty = qty + item_row_copy.item_tax_template = item_tax_template + si.append("items", item_row_copy) + + si.append( + "taxes", + { + "account_head": "_Test Account Excise Duty - _TC", + "charge_type": "On Net Total", + "cost_center": "_Test Cost Center - _TC", + "description": "Excise Duty", + "doctype": "Sales Taxes and Charges", + "rate": 11, }, ) - tax_map = json.loads(item_details.item_tax_rate) - for tax in tax_map: - si.append( - "taxes", - { - "charge_type": "On Net Total", - "account_head": tax, - "rate": tax_map[tax], - "description": "Test", - "cost_center": "_Test Cost Center - _TC", - }, - ) - si.submit() - si.load_from_db() - - # check if correct tax values are applied from tax template - self.assertEqual(si.net_total, 386.4) - - expected_taxes = [ + si.append( + "taxes", { - "tax_amount": 19.32, - "total": 405.72, + "account_head": "_Test Account Education Cess - _TC", + "charge_type": "On Net Total", + "cost_center": "_Test Cost Center - _TC", + "description": "Education Cess", + "doctype": "Sales Taxes and Charges", + "rate": 0, }, + ) + si.append( + "taxes", { - "tax_amount": 38.64, - "total": 444.36, + "account_head": "_Test Account S&H Education Cess - _TC", + "charge_type": "On Net Total", + "cost_center": "_Test Cost Center - _TC", + "description": "S&H Education Cess", + "doctype": "Sales Taxes and Charges", + "rate": 3, }, - { - "tax_amount": 57.96, - "total": 502.32, - }, - ] + ) + si.insert() - for i in range(len(expected_taxes)): - for key in expected_taxes[i]: - self.assertEqual(expected_taxes[i][key], si.get("taxes")[i].get(key)) + self.assertEqual(si.net_total, 4600) - self.assertEqual(si.get("base_total_taxes_and_charges"), 115.92) + self.assertEqual(si.get("taxes")[0].tax_amount, 502.41) + self.assertEqual(si.get("taxes")[0].total, 5102.41) + + self.assertEqual(si.get("taxes")[1].tax_amount, 197.80) + self.assertEqual(si.get("taxes")[1].total, 5300.21) + + self.assertEqual(si.get("taxes")[2].tax_amount, 375.36) + self.assertEqual(si.get("taxes")[2].total, 5675.57) + + self.assertEqual(si.grand_total, 5675.57) + self.assertEqual(si.rounding_adjustment, 0.43) + self.assertEqual(si.rounded_total, 5676.0) def test_tax_calculation_with_multiple_items_and_discount(self): si = create_sales_invoice(qty=1, rate=75, do_not_save=True) diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index 09d3dd1dad..a942f58bd6 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -163,7 +163,7 @@ class TestItem(FrappeTestCase): { "item_code": "_Test Item With Item Tax Template", "tax_category": "_Test Tax Category 2", - "item_tax_template": "", + "item_tax_template": None, }, { "item_code": "_Test Item Inherit Group Item Tax Template 1", @@ -178,7 +178,7 @@ class TestItem(FrappeTestCase): { "item_code": "_Test Item Inherit Group Item Tax Template 1", "tax_category": "_Test Tax Category 2", - "item_tax_template": "", + "item_tax_template": None, }, { "item_code": "_Test Item Inherit Group Item Tax Template 2", @@ -193,7 +193,7 @@ class TestItem(FrappeTestCase): { "item_code": "_Test Item Inherit Group Item Tax Template 2", "tax_category": "_Test Tax Category 2", - "item_tax_template": "", + "item_tax_template": None, }, { "item_code": "_Test Item Override Group Item Tax Template", @@ -208,12 +208,12 @@ class TestItem(FrappeTestCase): { "item_code": "_Test Item Override Group Item Tax Template", "tax_category": "_Test Tax Category 2", - "item_tax_template": "", + "item_tax_template": None, }, ] expected_item_tax_map = { - "": {}, + None: {}, "_Test Account Excise Duty @ 10 - _TC": {"_Test Account Excise Duty - _TC": 10}, "_Test Account Excise Duty @ 12 - _TC": {"_Test Account Excise Duty - _TC": 12}, "_Test Account Excise Duty @ 15 - _TC": {"_Test Account Excise Duty - _TC": 15}, diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py index e29fc882ce..c766cab0a1 100644 --- a/erpnext/stock/get_item_details.py +++ b/erpnext/stock/get_item_details.py @@ -610,7 +610,6 @@ def _get_item_tax_template(args, taxes, out=None, for_validate=False): # all templates have validity and no template is valid if not taxes_with_validity and (not taxes_with_no_validity): - out["item_tax_template"] = "" return None # do not change if already a valid template From 9a171db97f67f88fad1589016afb4e808571d04c Mon Sep 17 00:00:00 2001 From: Anand Baburajan Date: Wed, 8 Nov 2023 22:02:09 +0530 Subject: [PATCH 103/130] fix: asset depreciation ledger (#37991) * fix: include opening acc depr while calculating asset depr ledger report * chore: include opening acc depr properly in acc depr amt * chore: add cost_center in asset depr ledger report * fix: handle finance books properly in asset depr ledger report * chore: rename 'include default book entries' to 'include default FB entries' --- .../asset_depreciation_ledger.js | 22 ++++++-- .../asset_depreciation_ledger.json | 6 +-- .../asset_depreciation_ledger.py | 51 ++++++++++++++----- .../report/balance_sheet/balance_sheet.js | 2 +- .../accounts/report/cash_flow/cash_flow.js | 2 +- .../consolidated_financial_statement.js | 2 +- .../accounts/report/financial_statements.py | 4 +- .../report/general_ledger/general_ledger.js | 2 +- .../report/general_ledger/general_ledger.py | 4 +- .../report/trial_balance/trial_balance.js | 2 +- .../report/trial_balance/trial_balance.py | 4 +- .../fixed_asset_register.js | 2 +- .../fixed_asset_register.py | 2 +- erpnext/translations/af.csv | 2 +- erpnext/translations/am.csv | 2 +- erpnext/translations/ar.csv | 2 +- erpnext/translations/bg.csv | 2 +- erpnext/translations/bn.csv | 2 +- erpnext/translations/bs.csv | 2 +- erpnext/translations/ca.csv | 2 +- erpnext/translations/cs.csv | 2 +- erpnext/translations/da.csv | 2 +- erpnext/translations/de.csv | 2 +- erpnext/translations/el.csv | 2 +- erpnext/translations/es.csv | 2 +- erpnext/translations/et.csv | 2 +- erpnext/translations/fa.csv | 2 +- erpnext/translations/fi.csv | 2 +- erpnext/translations/fr.csv | 2 +- erpnext/translations/gu.csv | 2 +- erpnext/translations/he.csv | 2 +- erpnext/translations/hi.csv | 2 +- erpnext/translations/hr.csv | 2 +- erpnext/translations/hu.csv | 2 +- erpnext/translations/id.csv | 2 +- erpnext/translations/is.csv | 2 +- erpnext/translations/it.csv | 2 +- erpnext/translations/ja.csv | 2 +- erpnext/translations/km.csv | 2 +- erpnext/translations/kn.csv | 2 +- erpnext/translations/ko.csv | 2 +- erpnext/translations/ku.csv | 2 +- erpnext/translations/lo.csv | 2 +- erpnext/translations/lt.csv | 2 +- erpnext/translations/lv.csv | 2 +- erpnext/translations/mk.csv | 2 +- erpnext/translations/ml.csv | 2 +- erpnext/translations/mr.csv | 2 +- erpnext/translations/ms.csv | 2 +- erpnext/translations/my.csv | 2 +- erpnext/translations/nl.csv | 2 +- erpnext/translations/no.csv | 2 +- erpnext/translations/pl.csv | 2 +- erpnext/translations/ps.csv | 2 +- erpnext/translations/pt-BR.csv | 2 +- erpnext/translations/pt.csv | 2 +- erpnext/translations/ro.csv | 2 +- erpnext/translations/ru.csv | 2 +- erpnext/translations/rw.csv | 2 +- erpnext/translations/si.csv | 2 +- erpnext/translations/sk.csv | 2 +- erpnext/translations/sl.csv | 2 +- erpnext/translations/sq.csv | 2 +- erpnext/translations/sr.csv | 2 +- erpnext/translations/sv.csv | 2 +- erpnext/translations/sw.csv | 2 +- erpnext/translations/ta.csv | 2 +- erpnext/translations/te.csv | 2 +- erpnext/translations/th.csv | 2 +- erpnext/translations/tr.csv | 2 +- erpnext/translations/uk.csv | 2 +- erpnext/translations/ur.csv | 2 +- erpnext/translations/uz.csv | 2 +- erpnext/translations/vi.csv | 2 +- erpnext/translations/zh.csv | 2 +- erpnext/translations/zh_tw.csv | 2 +- 76 files changed, 131 insertions(+), 100 deletions(-) diff --git a/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.js b/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.js index 126cd03795..12b94347e0 100644 --- a/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.js +++ b/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.js @@ -31,6 +31,18 @@ frappe.query_reports["Asset Depreciation Ledger"] = { "fieldtype": "Link", "options": "Asset" }, + { + "fieldname":"asset_category", + "label": __("Asset Category"), + "fieldtype": "Link", + "options": "Asset Category" + }, + { + "fieldname":"cost_center", + "label": __("Cost Center"), + "fieldtype": "Link", + "options": "Cost Center" + }, { "fieldname":"finance_book", "label": __("Finance Book"), @@ -38,10 +50,10 @@ frappe.query_reports["Asset Depreciation Ledger"] = { "options": "Finance Book" }, { - "fieldname":"asset_category", - "label": __("Asset Category"), - "fieldtype": "Link", - "options": "Asset Category" - } + "fieldname": "include_default_book_assets", + "label": __("Include Default FB Assets"), + "fieldtype": "Check", + "default": 1 + }, ] } diff --git a/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.json b/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.json index 0ef9d858dd..9002e23ed3 100644 --- a/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.json +++ b/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.json @@ -1,15 +1,15 @@ { - "add_total_row": 1, + "add_total_row": 0, "columns": [], "creation": "2016-04-08 14:49:58.133098", "disabled": 0, "docstatus": 0, "doctype": "Report", "filters": [], - "idx": 2, + "idx": 6, "is_standard": "Yes", "letterhead": null, - "modified": "2023-07-26 21:05:33.554778", + "modified": "2023-11-08 20:17:05.774211", "modified_by": "Administrator", "module": "Accounts", "name": "Asset Depreciation Ledger", diff --git a/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py b/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py index f21c94b494..d285f28d8e 100644 --- a/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py +++ b/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py @@ -4,7 +4,7 @@ import frappe from frappe import _ -from frappe.utils import flt +from frappe.utils import cstr, flt def execute(filters=None): @@ -32,7 +32,6 @@ def get_data(filters): filters_data.append(["against_voucher", "=", filters.get("asset")]) if filters.get("asset_category"): - assets = frappe.db.sql_list( """select name from tabAsset where asset_category = %s and docstatus=1""", @@ -41,12 +40,27 @@ def get_data(filters): filters_data.append(["against_voucher", "in", assets]) - if filters.get("finance_book"): - filters_data.append(["finance_book", "in", ["", filters.get("finance_book")]]) + company_fb = frappe.get_cached_value("Company", filters.get("company"), "default_finance_book") + + if filters.get("include_default_book_assets") and company_fb: + if filters.get("finance_book") and cstr(filters.get("finance_book")) != cstr(company_fb): + frappe.throw(_("To use a different finance book, please uncheck 'Include Default FB Assets'")) + else: + finance_book = company_fb + elif filters.get("finance_book"): + finance_book = filters.get("finance_book") + else: + finance_book = None + + if finance_book: + or_filters_data = [["finance_book", "in", ["", finance_book]], ["finance_book", "is", "not set"]] + else: + or_filters_data = [["finance_book", "in", [""]], ["finance_book", "is", "not set"]] gl_entries = frappe.get_all( "GL Entry", filters=filters_data, + or_filters=or_filters_data, fields=["against_voucher", "debit_in_account_currency as debit", "voucher_no", "posting_date"], order_by="against_voucher, posting_date", ) @@ -61,7 +75,9 @@ def get_data(filters): asset_data = assets_details.get(d.against_voucher) if asset_data: if not asset_data.get("accumulated_depreciation_amount"): - asset_data.accumulated_depreciation_amount = d.debit + asset_data.accumulated_depreciation_amount = d.debit + asset_data.get( + "opening_accumulated_depreciation" + ) else: asset_data.accumulated_depreciation_amount += d.debit @@ -70,7 +86,7 @@ def get_data(filters): { "depreciation_amount": d.debit, "depreciation_date": d.posting_date, - "amount_after_depreciation": ( + "value_after_depreciation": ( flt(row.gross_purchase_amount) - flt(row.accumulated_depreciation_amount) ), "depreciation_entry": d.voucher_no, @@ -88,10 +104,12 @@ def get_assets_details(assets): fields = [ "name as asset", "gross_purchase_amount", + "opening_accumulated_depreciation", "asset_category", "status", "depreciation_method", "purchase_date", + "cost_center", ] for d in frappe.get_all("Asset", fields=fields, filters={"name": ("in", assets)}): @@ -121,6 +139,12 @@ def get_columns(): "fieldtype": "Currency", "width": 120, }, + { + "label": _("Opening Accumulated Depreciation"), + "fieldname": "opening_accumulated_depreciation", + "fieldtype": "Currency", + "width": 140, + }, { "label": _("Depreciation Amount"), "fieldname": "depreciation_amount", @@ -134,8 +158,8 @@ def get_columns(): "width": 210, }, { - "label": _("Amount After Depreciation"), - "fieldname": "amount_after_depreciation", + "label": _("Value After Depreciation"), + "fieldname": "value_after_depreciation", "fieldtype": "Currency", "width": 180, }, @@ -153,12 +177,13 @@ def get_columns(): "options": "Asset Category", "width": 120, }, - {"label": _("Current Status"), "fieldname": "status", "fieldtype": "Data", "width": 120}, { - "label": _("Depreciation Method"), - "fieldname": "depreciation_method", - "fieldtype": "Data", - "width": 130, + "label": _("Cost Center"), + "fieldtype": "Link", + "fieldname": "cost_center", + "options": "Cost Center", + "width": 100, }, + {"label": _("Current Status"), "fieldname": "status", "fieldtype": "Data", "width": 120}, {"label": _("Purchase Date"), "fieldname": "purchase_date", "fieldtype": "Date", "width": 120}, ] diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.js b/erpnext/accounts/report/balance_sheet/balance_sheet.js index c2b57f768f..b05e744ae0 100644 --- a/erpnext/accounts/report/balance_sheet/balance_sheet.js +++ b/erpnext/accounts/report/balance_sheet/balance_sheet.js @@ -17,7 +17,7 @@ frappe.query_reports["Balance Sheet"]["filters"].push({ frappe.query_reports["Balance Sheet"]["filters"].push({ fieldname: "include_default_book_entries", - label: __("Include Default Book Entries"), + label: __("Include Default FB Entries"), fieldtype: "Check", default: 1, }); diff --git a/erpnext/accounts/report/cash_flow/cash_flow.js b/erpnext/accounts/report/cash_flow/cash_flow.js index 6b8ed27e64..ef17eb1503 100644 --- a/erpnext/accounts/report/cash_flow/cash_flow.js +++ b/erpnext/accounts/report/cash_flow/cash_flow.js @@ -17,7 +17,7 @@ frappe.query_reports["Cash Flow"]["filters"].splice(8, 1); frappe.query_reports["Cash Flow"]["filters"].push( { "fieldname": "include_default_book_entries", - "label": __("Include Default Book Entries"), + "label": __("Include Default FB Entries"), "fieldtype": "Check", "default": 1 } diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js index 590408c6f8..0e0c42dad9 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js @@ -104,7 +104,7 @@ frappe.query_reports["Consolidated Financial Statement"] = { }, { "fieldname": "include_default_book_entries", - "label": __("Include Default Book Entries"), + "label": __("Include Default FB Entries"), "fieldtype": "Check", "default": 1 }, diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 693725d8f5..096bb10706 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -561,9 +561,7 @@ def apply_additional_conditions(doctype, query, from_date, ignore_closing_entrie company_fb = frappe.get_cached_value("Company", filters.company, "default_finance_book") if filters.finance_book and company_fb and cstr(filters.finance_book) != cstr(company_fb): - frappe.throw( - _("To use a different finance book, please uncheck 'Include Default Book Entries'") - ) + frappe.throw(_("To use a different finance book, please uncheck 'Include Default FB Entries'")) query = query.where( (gl_entry.finance_book.isin([cstr(filters.finance_book), cstr(company_fb), ""])) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js index c0b4f59579..4cb443cf92 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.js +++ b/erpnext/accounts/report/general_ledger/general_ledger.js @@ -175,7 +175,7 @@ frappe.query_reports["General Ledger"] = { }, { "fieldname": "include_default_book_entries", - "label": __("Include Default Book Entries"), + "label": __("Include Default FB Entries"), "fieldtype": "Check", "default": 1 }, diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index 5e484cf558..94cd293615 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -259,9 +259,7 @@ def get_conditions(filters): if filters.get("company_fb") and cstr(filters.get("finance_book")) != cstr( filters.get("company_fb") ): - frappe.throw( - _("To use a different finance book, please uncheck 'Include Default Book Entries'") - ) + frappe.throw(_("To use a different finance book, please uncheck 'Include Default FB Entries'")) else: conditions.append("(finance_book in (%(finance_book)s, '') OR finance_book IS NULL)") else: diff --git a/erpnext/accounts/report/trial_balance/trial_balance.js b/erpnext/accounts/report/trial_balance/trial_balance.js index edd40b68ef..2c4c762073 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.js +++ b/erpnext/accounts/report/trial_balance/trial_balance.js @@ -95,7 +95,7 @@ frappe.query_reports["Trial Balance"] = { }, { "fieldname": "include_default_book_entries", - "label": __("Include Default Book Entries"), + "label": __("Include Default FB Entries"), "fieldtype": "Check", "default": 1 }, diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py index 2a8aa0c202..8b7f0bbc00 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.py +++ b/erpnext/accounts/report/trial_balance/trial_balance.py @@ -275,9 +275,7 @@ def get_opening_balance( company_fb = frappe.get_cached_value("Company", filters.company, "default_finance_book") if filters.finance_book and company_fb and cstr(filters.finance_book) != cstr(company_fb): - frappe.throw( - _("To use a different finance book, please uncheck 'Include Default Book Entries'") - ) + frappe.throw(_("To use a different finance book, please uncheck 'Include Default FB Entries'")) opening_balance = opening_balance.where( (closing_balance.finance_book.isin([cstr(filters.finance_book), cstr(company_fb), ""])) diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js index 48d33314ec..812b7f78e1 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js @@ -52,7 +52,7 @@ frappe.query_reports["Fixed Asset Register"] = { }, { "fieldname": "include_default_book_assets", - "label": __("Include Default Book Assets"), + "label": __("Include Default FB Assets"), "fieldtype": "Check", "default": 1 }, diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py index 383be97347..45811a9344 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py @@ -223,7 +223,7 @@ def get_assets_linked_to_fb(filters): company_fb = frappe.get_cached_value("Company", filters.company, "default_finance_book") if filters.finance_book and company_fb and cstr(filters.finance_book) != cstr(company_fb): - frappe.throw(_("To use a different finance book, please uncheck 'Include Default Book Assets'")) + frappe.throw(_("To use a different finance book, please uncheck 'Include Default FB Assets'")) query = query.where( (afb.finance_book.isin([cstr(filters.finance_book), cstr(company_fb), ""])) diff --git a/erpnext/translations/af.csv b/erpnext/translations/af.csv index f45731468d..ee77d98948 100644 --- a/erpnext/translations/af.csv +++ b/erpnext/translations/af.csv @@ -1153,7 +1153,7 @@ In Value,In Waarde, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",In die geval van 'n multi-vlak program sal kliënte outomaties toegewys word aan die betrokke vlak volgens hul besteding, Inactive,onaktiewe, Incentives,aansporings, -Include Default Book Entries,Sluit standaardboekinskrywings in, +Include Default FB Entries,Sluit standaardboekinskrywings in, Include Exploded Items,Sluit ontplofte items in, Include POS Transactions,Sluit POS-transaksies in, Include UOM,Sluit UOM in, diff --git a/erpnext/translations/am.csv b/erpnext/translations/am.csv index 0453d5d685..a5f09a7488 100644 --- a/erpnext/translations/am.csv +++ b/erpnext/translations/am.csv @@ -1153,7 +1153,7 @@ In Value,እሴት ውስጥ, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","በባለብዙ ደረጃ መርሃግብር ሁኔታ, ደንበኞች በተጠቀሱት ወጪ መሰረት ለተሰጣቸው ደረጃ ደረጃ በራስ መተላለፍ ይኖራቸዋል", Inactive,ገባሪ አይደለም, Incentives,ማበረታቻዎች, -Include Default Book Entries,ነባሪ የመጽሐፍ ግቤቶችን አካትት።, +Include Default FB Entries,ነባሪ የመጽሐፍ ግቤቶችን አካትት።, Include Exploded Items,የተበተኑ ንጥሎችን አካት, Include POS Transactions,የ POS ሽግግሮችን አክል, Include UOM,UOM አካት, diff --git a/erpnext/translations/ar.csv b/erpnext/translations/ar.csv index 67b409e7ef..195b9c7963 100644 --- a/erpnext/translations/ar.csv +++ b/erpnext/translations/ar.csv @@ -1153,7 +1153,7 @@ In Value,القيمة القادمة, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",في حالة البرنامج متعدد المستويات ، سيتم تعيين العملاء تلقائيًا إلى الطبقة المعنية وفقًا للإنفاق, Inactive,غير نشط, Incentives,الحوافز, -Include Default Book Entries,تضمين إدخالات دفتر افتراضي, +Include Default FB Entries,تضمين إدخالات دفتر افتراضي, Include Exploded Items,تشمل البنود المستبعدة, Include POS Transactions,تشمل معاملات نقطه البيع, Include UOM,تضمين UOM, diff --git a/erpnext/translations/bg.csv b/erpnext/translations/bg.csv index 787f81ee7f..c2bacf4f93 100644 --- a/erpnext/translations/bg.csv +++ b/erpnext/translations/bg.csv @@ -1153,7 +1153,7 @@ In Value,В стойност, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","В случай на многостепенна програма, клиентите ще бъдат автоматично зададени на съответния подреждан по тяхна сметка", Inactive,неактивен, Incentives,Стимули, -Include Default Book Entries,Включете записи по подразбиране на книги, +Include Default FB Entries,Включете записи по подразбиране на книги, Include Exploded Items,Включете експлодираните елементи, Include POS Transactions,Включете POS транзакции, Include UOM,Включете UOM, diff --git a/erpnext/translations/bn.csv b/erpnext/translations/bn.csv index 69fd08cb51..d7366e14ab 100644 --- a/erpnext/translations/bn.csv +++ b/erpnext/translations/bn.csv @@ -1153,7 +1153,7 @@ In Value,মান, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","মাল্টি-টিয়ার প্রোগ্রামের ক্ষেত্রে, গ্রাহকরা তাদের ব্যয় অনুযায়ী সংশ্লিষ্ট টায়ারে স্বয়ংক্রিয়ভাবে নিয়োগ পাবেন", Inactive,নিষ্ক্রিয়, Incentives,ইনসেনটিভ, -Include Default Book Entries,ডিফল্ট বুক এন্ট্রি অন্তর্ভুক্ত করুন, +Include Default FB Entries,ডিফল্ট বুক এন্ট্রি অন্তর্ভুক্ত করুন, Include Exploded Items,বিস্ফোরিত আইটেম অন্তর্ভুক্ত করুন, Include POS Transactions,পিওএস লেনদেন অন্তর্ভুক্ত করুন, Include UOM,UOM অন্তর্ভুক্ত করুন, diff --git a/erpnext/translations/bs.csv b/erpnext/translations/bs.csv index ef680a36ad..df4083eed6 100644 --- a/erpnext/translations/bs.csv +++ b/erpnext/translations/bs.csv @@ -1153,7 +1153,7 @@ In Value,u vrijednost, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","U slučaju višeslojnog programa, Korisnici će automatski biti dodeljeni za dotičnu grupu po njihovom trošenju", Inactive,Neaktivan, Incentives,Poticaji, -Include Default Book Entries,Uključite zadane unose knjiga, +Include Default FB Entries,Uključite zadane unose knjiga, Include Exploded Items,Uključite eksplodirane predmete, Include POS Transactions,Uključite POS transakcije, Include UOM,Uključite UOM, diff --git a/erpnext/translations/ca.csv b/erpnext/translations/ca.csv index fa545a4c19..b3cf2c5fe1 100644 --- a/erpnext/translations/ca.csv +++ b/erpnext/translations/ca.csv @@ -1153,7 +1153,7 @@ In Value,En valor, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","En el cas del programa de diversos nivells, els clients seran assignats automàticament al nivell corresponent segons el seu gastat", Inactive,Inactiu, Incentives,Incentius, -Include Default Book Entries,Inclou les entrades de llibres predeterminats, +Include Default FB Entries,Inclou les entrades de llibres predeterminats, Include Exploded Items,Inclou articles explotats, Include POS Transactions,Inclou transaccions de POS, Include UOM,Inclou UOM, diff --git a/erpnext/translations/cs.csv b/erpnext/translations/cs.csv index 7fb1679f9c..b6deaa46d8 100644 --- a/erpnext/translations/cs.csv +++ b/erpnext/translations/cs.csv @@ -1153,7 +1153,7 @@ In Value,v Hodnota, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",V případě víceúrovňového programu budou zákazníci automaticky přiděleni danému vrstvě podle svých vynaložených nákladů, Inactive,Neaktivní, Incentives,Pobídky, -Include Default Book Entries,Zahrnout výchozí položky knihy, +Include Default FB Entries,Zahrnout výchozí položky knihy, Include Exploded Items,Zahrnout výbušné položky, Include POS Transactions,Zahrnout POS transakce, Include UOM,Zahrnout UOM, diff --git a/erpnext/translations/da.csv b/erpnext/translations/da.csv index 4eb39609f2..4bcc307583 100644 --- a/erpnext/translations/da.csv +++ b/erpnext/translations/da.csv @@ -1153,7 +1153,7 @@ In Value,I Value, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","I tilfælde af multi-tier program, vil kunder automatisk blive tildelt den pågældende tier som per deres brugt", Inactive,inaktive, Incentives,Incitamenter, -Include Default Book Entries,Inkluder standardbogsindlæg, +Include Default FB Entries,Inkluder standardbogsindlæg, Include Exploded Items,Inkluder eksploderede elementer, Include POS Transactions,Inkluder POS-transaktioner, Include UOM,Inkluder UOM, diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index c856fefcc0..61e301eed4 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -1161,7 +1161,7 @@ In Value,Wert bei, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",Im Falle eines mehrstufigen Programms werden Kunden automatisch der betroffenen Ebene entsprechend ihrer Ausgaben zugewiesen, Inactive,Inaktiv, Incentives,Anreize, -Include Default Book Entries,Standardbucheinträge einschließen, +Include Default FB Entries,Standardbucheinträge einschließen, Include Exploded Items,Unterartikel einbeziehen, Include POS Transactions,POS-Transaktionen einschließen, Include UOM,Fügen Sie UOM hinzu, diff --git a/erpnext/translations/el.csv b/erpnext/translations/el.csv index 21fb435901..e67eaffc69 100644 --- a/erpnext/translations/el.csv +++ b/erpnext/translations/el.csv @@ -1153,7 +1153,7 @@ In Value,στην Αξία, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Στην περίπτωση προγράμματος πολλαπλών βαθμίδων, οι Πελάτες θα αντιστοιχούν αυτόματα στη σχετική βαθμίδα σύμφωνα με το ποσό που δαπανώνται", Inactive,Αδρανής, Incentives,Κίνητρα, -Include Default Book Entries,Συμπεριλάβετε τις προεπιλεγμένες καταχωρίσεις βιβλίων, +Include Default FB Entries,Συμπεριλάβετε τις προεπιλεγμένες καταχωρίσεις βιβλίων, Include Exploded Items,Συμπεριλάβετε εκραγμένα στοιχεία, Include POS Transactions,Συμπεριλάβετε τις συναλλαγές POS, Include UOM,Συμπεριλάβετε UOM, diff --git a/erpnext/translations/es.csv b/erpnext/translations/es.csv index 2abe418707..0c90694f8f 100644 --- a/erpnext/translations/es.csv +++ b/erpnext/translations/es.csv @@ -1153,7 +1153,7 @@ In Value,En valor, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","En el caso del programa de varios niveles, los Clientes se asignarán automáticamente al nivel correspondiente según su gasto", Inactive,Inactivo, Incentives,Incentivos, -Include Default Book Entries,Incluir entradas de libro predeterminadas, +Include Default FB Entries,Incluir entradas de libro predeterminadas, Include Exploded Items,Incluir Elementos Estallados, Include POS Transactions,Incluir transacciones POS, Include UOM,Incluir UOM, diff --git a/erpnext/translations/et.csv b/erpnext/translations/et.csv index a4a8736006..69a89d9d22 100644 --- a/erpnext/translations/et.csv +++ b/erpnext/translations/et.csv @@ -1153,7 +1153,7 @@ In Value,väärtuse, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",Mitmekordsete programmide korral määratakse Kliendid automaatselt asjaomasele tasemele vastavalt nende kasutatud kuludele, Inactive,Mitteaktiivne, Incentives,Soodustused, -Include Default Book Entries,Lisage vaikeraamatu kanded, +Include Default FB Entries,Lisage vaikeraamatu kanded, Include Exploded Items,Kaasa lõhutud esemed, Include POS Transactions,Kaasa POS-tehingud, Include UOM,Lisa UOM, diff --git a/erpnext/translations/fa.csv b/erpnext/translations/fa.csv index bd40c8b396..cddabfb448 100644 --- a/erpnext/translations/fa.csv +++ b/erpnext/translations/fa.csv @@ -1153,7 +1153,7 @@ In Value,با ارزش, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",در مورد برنامه چند لایه، مشتریان به صورت خودکار به سطر مربوطه اختصاص داده می شوند، همانطور که در هزینه های خود هستند, Inactive,غیر فعال, Incentives,انگیزه, -Include Default Book Entries,شامل ورودی های پیش فرض کتاب, +Include Default FB Entries,شامل ورودی های پیش فرض کتاب, Include Exploded Items,شامل موارد انفجار, Include POS Transactions,شامل معاملات POS, Include UOM,شامل UOM, diff --git a/erpnext/translations/fi.csv b/erpnext/translations/fi.csv index 33cf1574ae..eae6053915 100644 --- a/erpnext/translations/fi.csv +++ b/erpnext/translations/fi.csv @@ -1153,7 +1153,7 @@ In Value,in Arvo, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",Monitasoisen ohjelman tapauksessa asiakkaat määräytyvät automaattisesti kyseiselle tasolle niiden kulutuksen mukaan, Inactive,Epäaktiivinen, Incentives,kannustimet/bonukset, -Include Default Book Entries,Sisällytä oletustiedot, +Include Default FB Entries,Sisällytä oletustiedot, Include Exploded Items,Sisällytä räjähtämättömiä kohteita, Include POS Transactions,Sisällytä POS-tapahtumia, Include UOM,Sisällytä UOM, diff --git a/erpnext/translations/fr.csv b/erpnext/translations/fr.csv index d15af74d47..5c34759a70 100644 --- a/erpnext/translations/fr.csv +++ b/erpnext/translations/fr.csv @@ -1058,7 +1058,7 @@ In Stock: ,En Stock :, In Value,En valeur, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Dans le cas d'un programme à plusieurs échelons, les clients seront automatiquement affectés au niveau approprié en fonction de leurs dépenses", Incentives,Incitations, -Include Default Book Entries,Inclure les entrées de livre par défaut, +Include Default FB Entries,Inclure les entrées de livre par défaut, Include Exploded Items,Inclure les articles éclatés, Include POS Transactions,Inclure les transactions du point de vente, Include UOM,Inclure UdM, diff --git a/erpnext/translations/gu.csv b/erpnext/translations/gu.csv index 06a3cc64af..604ec411c4 100644 --- a/erpnext/translations/gu.csv +++ b/erpnext/translations/gu.csv @@ -1153,7 +1153,7 @@ In Value,ભાવ, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","મલ્ટિ-ટાયર પ્રોગ્રામના કિસ્સામાં, ગ્રાહક તેમના ખર્ચ મુજબ સંબંધિત ટાયરમાં ઓટો હશે", Inactive,નિષ્ક્રિય, Incentives,ઇનસેન્ટીવ્સ, -Include Default Book Entries,ડિફaultલ્ટ બુક એન્ટ્રીઓ શામેલ કરો, +Include Default FB Entries,ડિફaultલ્ટ બુક એન્ટ્રીઓ શામેલ કરો, Include Exploded Items,વિસ્ફોટ થયેલ આઇટમ્સ શામેલ કરો, Include POS Transactions,POS વ્યવહારો શામેલ કરો, Include UOM,યુએમએમ શામેલ કરો, diff --git a/erpnext/translations/he.csv b/erpnext/translations/he.csv index d5fcab6454..5407578f2b 100644 --- a/erpnext/translations/he.csv +++ b/erpnext/translations/he.csv @@ -1153,7 +1153,7 @@ In Value,ערך, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","במקרה של תוכנית רב-שכבתית, הלקוחות יוקצו אוטומטית לשכבה הנוגעת בדבר שהוצאו", Inactive,לֹא פָּעִיל, Incentives,תמריצים, -Include Default Book Entries,כלול רשומות ברירת מחדל לספרים, +Include Default FB Entries,כלול רשומות ברירת מחדל לספרים, Include Exploded Items,כלול פריטים מפוצצים, Include POS Transactions,כלול עסקאות קופה, Include UOM,כלול UOM, diff --git a/erpnext/translations/hi.csv b/erpnext/translations/hi.csv index a5caa666c2..00532df0f1 100644 --- a/erpnext/translations/hi.csv +++ b/erpnext/translations/hi.csv @@ -1153,7 +1153,7 @@ In Value,मूल्य में, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","मल्टी-स्तरीय कार्यक्रम के मामले में, ग्राहक अपने खर्च के अनुसार संबंधित स्तर को स्वचालित रूप से सौंपा जाएगा", Inactive,निष्क्रिय, Incentives,प्रोत्साहन, -Include Default Book Entries,डिफ़ॉल्ट बुक प्रविष्टियाँ शामिल करें, +Include Default FB Entries,डिफ़ॉल्ट बुक प्रविष्टियाँ शामिल करें, Include Exploded Items,विस्फोट किए गए आइटम शामिल करें, Include POS Transactions,पीओएस लेनदेन शामिल करें, Include UOM,यूओएम शामिल करें, diff --git a/erpnext/translations/hr.csv b/erpnext/translations/hr.csv index 2834602248..3cc9ef3be2 100644 --- a/erpnext/translations/hr.csv +++ b/erpnext/translations/hr.csv @@ -1153,7 +1153,7 @@ In Value,u vrijednost, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","U slučaju višerazinskog programa, Kupci će biti automatski dodijeljeni odgovarajućem stupcu po njihovu potrošenom", Inactive,neaktivan, Incentives,poticaji, -Include Default Book Entries,Uključite zadane unose u knjige, +Include Default FB Entries,Uključite zadane unose u knjige, Include Exploded Items,Uključi eksplodirane predmete, Include POS Transactions,Uključi POS transakcije, Include UOM,Uključi UOM, diff --git a/erpnext/translations/hu.csv b/erpnext/translations/hu.csv index a262c8a994..42175bbe34 100644 --- a/erpnext/translations/hu.csv +++ b/erpnext/translations/hu.csv @@ -1153,7 +1153,7 @@ In Value,Az Értékben, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Többszintű program esetében az ügyfeleket automatikusan az adott kategóriába sorolják, az általuk elköltöttek szerint", Inactive,Inaktív, Incentives,Ösztönzők, -Include Default Book Entries,Tartalmazza az alapértelmezett könyvbejegyzéseket, +Include Default FB Entries,Tartalmazza az alapértelmezett könyvbejegyzéseket, Include Exploded Items,Tartalmazza a robbantott elemeket, Include POS Transactions,Tartalmazza a POS kassza tranzakciókat, Include UOM,Ide tartozik az ANYJ, diff --git a/erpnext/translations/id.csv b/erpnext/translations/id.csv index c4e50fdfe5..d69eef389d 100644 --- a/erpnext/translations/id.csv +++ b/erpnext/translations/id.csv @@ -1153,7 +1153,7 @@ In Value,Nilai, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Dalam kasus program multi-tier, Pelanggan akan ditugaskan secara otomatis ke tingkat yang bersangkutan sesuai yang mereka habiskan", Inactive,Tidak aktif, Incentives,Insentif, -Include Default Book Entries,Sertakan Entri Buku Default, +Include Default FB Entries,Sertakan Entri Buku Default, Include Exploded Items,Sertakan barang yang meledak, Include POS Transactions,Sertakan Transaksi POS, Include UOM,Termasuk UOM, diff --git a/erpnext/translations/is.csv b/erpnext/translations/is.csv index 50c06ecfbb..1acefbb3ee 100644 --- a/erpnext/translations/is.csv +++ b/erpnext/translations/is.csv @@ -1153,7 +1153,7 @@ In Value,Virði, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Þegar um er að ræða fjölþættaráætlun, verða viðskiptavinir sjálfkrafa tengdir viðkomandi flokka eftir því sem þeir eru í", Inactive,Óvirkt, Incentives,Incentives, -Include Default Book Entries,Hafa sjálfgefnar bókarfærslur með, +Include Default FB Entries,Hafa sjálfgefnar bókarfærslur með, Include Exploded Items,Inniheldur sprauta hluti, Include POS Transactions,Innifalið POS-viðskipti, Include UOM,Innifalið UOM, diff --git a/erpnext/translations/it.csv b/erpnext/translations/it.csv index 37608958c3..e6e64254bb 100644 --- a/erpnext/translations/it.csv +++ b/erpnext/translations/it.csv @@ -1153,7 +1153,7 @@ In Value,In valore, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Nel caso di un programma multilivello, i clienti verranno assegnati automaticamente al livello interessato come da loro speso", Inactive,Inattivo, Incentives,Incentivi, -Include Default Book Entries,Includi voci di libro predefinite, +Include Default FB Entries,Includi voci di libro predefinite, Include Exploded Items,Includi elementi esplosi, Include POS Transactions,Includi transazioni POS, Include UOM,Includi UOM, diff --git a/erpnext/translations/ja.csv b/erpnext/translations/ja.csv index 888ec800c9..dd5820ae77 100644 --- a/erpnext/translations/ja.csv +++ b/erpnext/translations/ja.csv @@ -1153,7 +1153,7 @@ In Value,値内, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",マルチティアプログラムの場合、顧客は、消費されるごとに自動的に関係する層に割り当てられます, Inactive,非アクティブ, Incentives,インセンティブ, -Include Default Book Entries,デフォルトのブックエントリを含める, +Include Default FB Entries,デフォルトのブックエントリを含める, Include Exploded Items,分解された項目を含める, Include POS Transactions,POSトランザクションを含める, Include UOM,UOMを含める, diff --git a/erpnext/translations/km.csv b/erpnext/translations/km.csv index d2003c004e..2740d7fc8a 100644 --- a/erpnext/translations/km.csv +++ b/erpnext/translations/km.csv @@ -1153,7 +1153,7 @@ In Value,នៅក្នុងតម្លៃ, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",ក្នុងករណីមានកម្មវិធីពហុលំដាប់អតិថិជននឹងត្រូវបានចាត់តាំងដោយខ្លួនឯងទៅថ្នាក់ដែលពាក់ព័ន្ធដោយចំណាយរបស់ពួកគេ, Inactive,អសកម្ម, Incentives,ការលើកទឹកចិត្ត, -Include Default Book Entries,រួមបញ្ចូលធាតុសៀវភៅលំនាំដើម។, +Include Default FB Entries,រួមបញ្ចូលធាតុសៀវភៅលំនាំដើម។, Include Exploded Items,រួមបញ្ចូលធាតុផ្ទុះ, Include POS Transactions,បញ្ចូលប្រតិបត្តិការ POS, Include UOM,រួមបញ្ចូល UOM, diff --git a/erpnext/translations/kn.csv b/erpnext/translations/kn.csv index 72066711ca..8b271682eb 100644 --- a/erpnext/translations/kn.csv +++ b/erpnext/translations/kn.csv @@ -1153,7 +1153,7 @@ In Value,ಮೌಲ್ಯ, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","ಮಲ್ಟಿ-ಟೈರ್ ಪ್ರೋಗ್ರಾಂನ ಸಂದರ್ಭದಲ್ಲಿ, ಗ್ರಾಹಕರು ತಮ್ಮ ಖರ್ಚುಗೆ ಅನುಗುಣವಾಗಿ ಆಯಾ ಶ್ರೇಣಿಗೆ ಸ್ವಯಂ ನಿಯೋಜಿಸಲಾಗುವುದು", Inactive,ನಿಷ್ಕ್ರಿಯವಾಗಿದೆ, Incentives,ಪ್ರೋತ್ಸಾಹ, -Include Default Book Entries,ಡೀಫಾಲ್ಟ್ ಪುಸ್ತಕ ನಮೂದುಗಳನ್ನು ಸೇರಿಸಿ, +Include Default FB Entries,ಡೀಫಾಲ್ಟ್ ಪುಸ್ತಕ ನಮೂದುಗಳನ್ನು ಸೇರಿಸಿ, Include Exploded Items,ಸ್ಫೋಟಗೊಂಡ ವಸ್ತುಗಳನ್ನು ಸೇರಿಸಿ, Include POS Transactions,ಪಿಒಎಸ್ ಟ್ರಾನ್ಸಾಕ್ಷನ್ಸ್ ಸೇರಿಸಿ, Include UOM,UOM ಸೇರಿಸಿ, diff --git a/erpnext/translations/ko.csv b/erpnext/translations/ko.csv index 99119256c1..edd3f2731f 100644 --- a/erpnext/translations/ko.csv +++ b/erpnext/translations/ko.csv @@ -1153,7 +1153,7 @@ In Value,값에서, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",다중 계층 프로그램의 경우 고객은 지출 한대로 해당 계층에 자동으로 할당됩니다., Inactive,비활성, Incentives,장려책, -Include Default Book Entries,기본 도서 항목 포함, +Include Default FB Entries,기본 도서 항목 포함, Include Exploded Items,분해 된 항목 포함, Include POS Transactions,POS 트랜잭션 포함, Include UOM,UOM 포함, diff --git a/erpnext/translations/ku.csv b/erpnext/translations/ku.csv index 8fec05993d..e18ce45132 100644 --- a/erpnext/translations/ku.csv +++ b/erpnext/translations/ku.csv @@ -1153,7 +1153,7 @@ In Value,di Nirx, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Di rewşê de bernameya pir-tier, Ewrûpa dê ji hêla xercê xwe ve girêdayî xerîb be", Inactive,Bêkar, Incentives,aborîve, -Include Default Book Entries,Navnîşanên Pirtûka Pêvek Bawer bikin, +Include Default FB Entries,Navnîşanên Pirtûka Pêvek Bawer bikin, Include Exploded Items,Included Dead Items, Include POS Transactions,Têkiliyên POSê de, Include UOM,UOM, diff --git a/erpnext/translations/lo.csv b/erpnext/translations/lo.csv index 0831788651..46acd22939 100644 --- a/erpnext/translations/lo.csv +++ b/erpnext/translations/lo.csv @@ -1153,7 +1153,7 @@ In Value,ໃນມູນຄ່າ, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","ໃນກໍລະນີຂອງໂຄງການຫຼາຍຂັ້ນ, ລູກຄ້າຈະໄດ້ຮັບການມອບຫມາຍໂດຍອັດຕະໂນມັດໃຫ້ກັບຂັ້ນຕອນທີ່ກ່ຽວຂ້ອງຕາມການໃຊ້ຈ່າຍຂອງເຂົາເຈົ້າ", Inactive,Inactive, Incentives,ສິ່ງຈູງໃຈ, -Include Default Book Entries,ລວມທັງການອອກສຽງປື້ມແບບເລີ່ມຕົ້ນ, +Include Default FB Entries,ລວມທັງການອອກສຽງປື້ມແບບເລີ່ມຕົ້ນ, Include Exploded Items,ລວມເອົາສິ່ງທີ່ເກີດຂື້ນ, Include POS Transactions,ລວມທຸລະກໍາ POS, Include UOM,ລວມ UOM, diff --git a/erpnext/translations/lt.csv b/erpnext/translations/lt.csv index 82152754e8..292c9d88e2 100644 --- a/erpnext/translations/lt.csv +++ b/erpnext/translations/lt.csv @@ -1153,7 +1153,7 @@ In Value,vertės, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Kalbant apie daugiapakopę programą, klientai bus automatiškai priskirti atitinkamam lygmeniui, atsižvelgiant į jų išleidimą", Inactive,Neaktyvus, Incentives,Paskatos, -Include Default Book Entries,Įtraukite numatytuosius knygų įrašus, +Include Default FB Entries,Įtraukite numatytuosius knygų įrašus, Include Exploded Items,Įtraukti sprogus elementus, Include POS Transactions,Įtraukti POS operacijas, Include UOM,Įtraukti UOM, diff --git a/erpnext/translations/lv.csv b/erpnext/translations/lv.csv index 8c4526ca52..52641b25af 100644 --- a/erpnext/translations/lv.csv +++ b/erpnext/translations/lv.csv @@ -1153,7 +1153,7 @@ In Value,Vērtībā, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Daudzpakāpju programmas gadījumā Klienti tiks automātiski piešķirti attiecīgajam līmenim, salīdzinot ar viņu iztērēto", Inactive,Neaktīvs, Incentives,Stimuli, -Include Default Book Entries,Iekļaujiet noklusējuma grāmatas ierakstus, +Include Default FB Entries,Iekļaujiet noklusējuma grāmatas ierakstus, Include Exploded Items,Iekļaut izpūstas preces, Include POS Transactions,Iekļaut POS darījumus, Include UOM,Iekļaut UOM, diff --git a/erpnext/translations/mk.csv b/erpnext/translations/mk.csv index a622524bf1..0a290198cc 100644 --- a/erpnext/translations/mk.csv +++ b/erpnext/translations/mk.csv @@ -1153,7 +1153,7 @@ In Value,Во вредност, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Во случај на повеќеслојна програма, корисниците ќе бидат автоматски доделени на засегнатите нивоа, како на нивните потрошени", Inactive,Неактивен, Incentives,Стимулации, -Include Default Book Entries,Вклучете стандардни записи за книги, +Include Default FB Entries,Вклучете стандардни записи за книги, Include Exploded Items,Вклучи експлодирани елементи, Include POS Transactions,Вклучете POS-трансакции, Include UOM,Вклучете UOM, diff --git a/erpnext/translations/ml.csv b/erpnext/translations/ml.csv index 777d5c64ff..04af8ab1ad 100644 --- a/erpnext/translations/ml.csv +++ b/erpnext/translations/ml.csv @@ -1153,7 +1153,7 @@ In Value,മൂല്യത്തിൽ, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","മൾട്ടി-ടയർ പരിപാടിയുടെ കാര്യത്തിൽ, കസ്റ്റമർമാർ ചെലവഴിച്ച തുക പ്രകാരം ബന്ധപ്പെട്ട ടീമിൽ ഓട്ടോ നിർണ്ണയിക്കും", Inactive,നിഷ്ക്രിയം, Incentives,ഇൻസെന്റീവ്സ്, -Include Default Book Entries,സ്ഥിരസ്ഥിതി പുസ്തക എൻ‌ട്രികൾ‌ ഉൾ‌പ്പെടുത്തുക, +Include Default FB Entries,സ്ഥിരസ്ഥിതി പുസ്തക എൻ‌ട്രികൾ‌ ഉൾ‌പ്പെടുത്തുക, Include Exploded Items,എക്സ്പ്ലോഡഡ് ഇനങ്ങൾ ഉൾപ്പെടുത്തുക, Include POS Transactions,POS ഇടപാടുകൾ ഉൾപ്പെടുത്തുക, Include UOM,UOM ഉൾപ്പെടുത്തുക, diff --git a/erpnext/translations/mr.csv b/erpnext/translations/mr.csv index 624f1ab481..785ab65c54 100644 --- a/erpnext/translations/mr.csv +++ b/erpnext/translations/mr.csv @@ -1153,7 +1153,7 @@ In Value,मूल्य, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",बहु-स्तरीय कार्यक्रमाच्या बाबतीत ग्राहक त्यांच्या खर्चानुसार संबंधित टायरला स्वयंचलितरित्या नियुक्त केले जातील, Inactive,निष्क्रिय, Incentives,प्रोत्साहन, -Include Default Book Entries,डीफॉल्ट पुस्तक नोंदी समाविष्ट करा, +Include Default FB Entries,डीफॉल्ट पुस्तक नोंदी समाविष्ट करा, Include Exploded Items,विस्फोट केलेल्या वस्तू समाविष्ट करा, Include POS Transactions,पीओएस व्यवहार समाविष्ट करा, Include UOM,यूओएम समाविष्ट करा, diff --git a/erpnext/translations/ms.csv b/erpnext/translations/ms.csv index 75e150a88d..db20d3c054 100644 --- a/erpnext/translations/ms.csv +++ b/erpnext/translations/ms.csv @@ -1153,7 +1153,7 @@ In Value,Dalam Nilai, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Dalam hal program multi-tier, Pelanggan akan ditugaskan secara automatik ke peringkat yang bersangkutan seperti yang dibelanjakannya", Inactive,Tidak aktif, Incentives,Insentif, -Include Default Book Entries,Sertakan Penyertaan Buku Lalai, +Include Default FB Entries,Sertakan Penyertaan Buku Lalai, Include Exploded Items,Termasuk Item Meletup, Include POS Transactions,Termasuk Transaksi POS, Include UOM,Termasuk UOM, diff --git a/erpnext/translations/my.csv b/erpnext/translations/my.csv index 36cd8740d7..f4b8676fee 100644 --- a/erpnext/translations/my.csv +++ b/erpnext/translations/my.csv @@ -1153,7 +1153,7 @@ In Value,Value တစ်ခုအတွက်, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Multi-tier အစီအစဉ်၏အမှု၌, Customer များအလိုအလျောက်သူတို့ရဲ့သုံးစွဲနှုန်းအတိုင်းစိုးရိမ်ပူပန်ဆင့်မှတာဝန်ပေးအပ်ပါလိမ့်မည်", Inactive,မလှုပ်ရှားတတ်သော, Incentives,မက်လုံးတွေပေးပြီး, -Include Default Book Entries,ပုံမှန်စာအုပ် Entries Include, +Include Default FB Entries,ပုံမှန်စာအုပ် Entries Include, Include Exploded Items,ပေါက်ကွဲပစ္စည်းများ Include, Include POS Transactions,POS အရောင်းအဝယ် Include, Include UOM,UOM Include, diff --git a/erpnext/translations/nl.csv b/erpnext/translations/nl.csv index 5859833861..1778c8044a 100644 --- a/erpnext/translations/nl.csv +++ b/erpnext/translations/nl.csv @@ -1153,7 +1153,7 @@ In Value,in Value, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",In het geval van een meerlagig programma worden klanten automatisch toegewezen aan de betreffende laag op basis van hun bestede tijd, Inactive,Inactief, Incentives,Incentives, -Include Default Book Entries,Standaard boekvermeldingen opnemen, +Include Default FB Entries,Standaard boekvermeldingen opnemen, Include Exploded Items,Exploded Items opnemen, Include POS Transactions,POS-transacties opnemen, Include UOM,Inclusief UOM, diff --git a/erpnext/translations/no.csv b/erpnext/translations/no.csv index a3236ac084..542217afe7 100644 --- a/erpnext/translations/no.csv +++ b/erpnext/translations/no.csv @@ -1153,7 +1153,7 @@ In Value,I verdi, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Når det gjelder flerlagsprogram, vil kundene automatisk bli tilordnet den aktuelle delen som brukt", Inactive,inaktiv, Incentives,Motivasjon, -Include Default Book Entries,Inkluder standardbokoppføringer, +Include Default FB Entries,Inkluder standardbokoppføringer, Include Exploded Items,Inkluder eksploderte elementer, Include POS Transactions,Inkluder POS-transaksjoner, Include UOM,Inkluder UOM, diff --git a/erpnext/translations/pl.csv b/erpnext/translations/pl.csv index df41e39862..247d0bae11 100644 --- a/erpnext/translations/pl.csv +++ b/erpnext/translations/pl.csv @@ -1151,7 +1151,7 @@ In Stock: ,W magazynie:, In Value,w polu Wartość, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","W przypadku programu wielowarstwowego Klienci zostaną automatycznie przypisani do danego poziomu, zgodnie z wydatkami", Inactive,Nieaktywny, -Include Default Book Entries,Dołącz domyślne wpisy książki, +Include Default FB Entries,Dołącz domyślne wpisy książki, Include Exploded Items,Dołącz rozstrzelone przedmioty, Include POS Transactions,Uwzględnij transakcje POS, Include UOM,Dołącz UOM, diff --git a/erpnext/translations/ps.csv b/erpnext/translations/ps.csv index 5a0b2a50cb..09d4df31ff 100644 --- a/erpnext/translations/ps.csv +++ b/erpnext/translations/ps.csv @@ -1153,7 +1153,7 @@ In Value,په ارزښت, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",د څو اړخیز پروګرام په صورت کې، پیرودونکي به د خپل مصرف په اساس اړوند ټیټ ته ګمارل کیږي, Inactive,غیر فعال, Incentives,هڅوونکي, -Include Default Book Entries,د ډیفالټ کتاب ننوتل شامل کړئ, +Include Default FB Entries,د ډیفالټ کتاب ننوتل شامل کړئ, Include Exploded Items,چاودیدونکي توکي شامل کړئ, Include POS Transactions,د POS تعاملات شامل کړئ, Include UOM,UOM شامل کړئ, diff --git a/erpnext/translations/pt-BR.csv b/erpnext/translations/pt-BR.csv index bc5b616080..92845b0a40 100644 --- a/erpnext/translations/pt-BR.csv +++ b/erpnext/translations/pt-BR.csv @@ -1153,7 +1153,7 @@ In Value,Valor Entrada, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","No caso do programa multicamadas, os Clientes serão atribuídos automaticamente ao nível em questão de acordo com o gasto", Inactive,Inativo, Incentives,Incentivos, -Include Default Book Entries,Incluir Entradas de Livro Padrão, +Include Default FB Entries,Incluir Entradas de Livro Padrão, Include Exploded Items,Incluir Itens Explodidos, Include POS Transactions,Incluir Transações PDV, Include UOM,Incluir UDM, diff --git a/erpnext/translations/pt.csv b/erpnext/translations/pt.csv index e6846c6a37..58cf6c8316 100644 --- a/erpnext/translations/pt.csv +++ b/erpnext/translations/pt.csv @@ -1153,7 +1153,7 @@ In Value,No Valor, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","No caso do programa multicamadas, os Clientes serão atribuídos automaticamente ao nível em questão de acordo com o gasto", Inactive,Inativo, Incentives,Incentivos, -Include Default Book Entries,Incluir entradas de livro padrão, +Include Default FB Entries,Incluir entradas de livro padrão, Include Exploded Items,Incluir itens explodidos, Include POS Transactions,Incluir transações POS, Include UOM,Incluir UOM, diff --git a/erpnext/translations/ro.csv b/erpnext/translations/ro.csv index ac7e598e2d..935b1e66fd 100644 --- a/erpnext/translations/ro.csv +++ b/erpnext/translations/ro.csv @@ -1153,7 +1153,7 @@ In Value,În valoare, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","În cazul unui program cu mai multe niveluri, Clienții vor fi automat alocați nivelului respectiv în funcție de cheltuielile efectuate", Inactive,Inactiv, Incentives,stimulente, -Include Default Book Entries,Includeți intrări implicite în cărți, +Include Default FB Entries,Includeți intrări implicite în cărți, Include Exploded Items,Includeți articole explodate, Include POS Transactions,Includeți tranzacțiile POS, Include UOM,Includeți UOM, diff --git a/erpnext/translations/ru.csv b/erpnext/translations/ru.csv index 52c29982f3..2f6f361b10 100644 --- a/erpnext/translations/ru.csv +++ b/erpnext/translations/ru.csv @@ -1151,7 +1151,7 @@ In Value,В цене, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",В случае многоуровневой программы Клиенты будут автоматически назначены соответствующему уровню в соответствии с затраченными, Inactive,Неактивный, Incentives,Стимулирование, -Include Default Book Entries,Включить записи в книгу по умолчанию, +Include Default FB Entries,Включить записи в книгу по умолчанию, Include Exploded Items,Включить раздробленные элементы, Include POS Transactions,Включить POS-транзакции, Include UOM,Включить UOM, diff --git a/erpnext/translations/rw.csv b/erpnext/translations/rw.csv index f035d57985..59362a1e29 100644 --- a/erpnext/translations/rw.csv +++ b/erpnext/translations/rw.csv @@ -1153,7 +1153,7 @@ In Value,Agaciro, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Kubijyanye na gahunda yo mu byiciro byinshi, Abakiriya bazahabwa imodoka bashinzwe urwego bireba nkuko bakoresheje", Inactive,Kudakora, Incentives,Inkunga, -Include Default Book Entries,Shyiramo Ibisanzwe Byanditswe, +Include Default FB Entries,Shyiramo Ibisanzwe Byanditswe, Include Exploded Items,Shyiramo Ibintu Biturika, Include POS Transactions,Shyiramo ibikorwa bya POS, Include UOM,Shyiramo UOM, diff --git a/erpnext/translations/si.csv b/erpnext/translations/si.csv index 4047263492..dd2acc45a2 100644 --- a/erpnext/translations/si.csv +++ b/erpnext/translations/si.csv @@ -1153,7 +1153,7 @@ In Value,අගය දී, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","බහු ස්ථරයේ වැඩසටහනක දී, පාරිභෝගිකයින් විසින් වැය කරනු ලබන පරිදි පාරිභෝගිකයින්ට අදාල ස්ථානයට ස්වයංක්රීයව පවරනු ලැබේ", Inactive,අක්රියයි, Incentives,සහන, -Include Default Book Entries,පෙරනිමි පොත් ඇතුළත් කිරීම් ඇතුළත් කරන්න, +Include Default FB Entries,පෙරනිමි පොත් ඇතුළත් කිරීම් ඇතුළත් කරන්න, Include Exploded Items,පුපුරණ ද්රව්ය අඩංගු කරන්න, Include POS Transactions,POS ගනුදෙනු, Include UOM,UOM ඇතුළත් කරන්න, diff --git a/erpnext/translations/sk.csv b/erpnext/translations/sk.csv index 98e1663f1c..025c8b788f 100644 --- a/erpnext/translations/sk.csv +++ b/erpnext/translations/sk.csv @@ -1153,7 +1153,7 @@ In Value,v Hodnota, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",V prípade viacvrstvového programu budú zákazníci automaticky priradení príslušnému vrstvu podľa ich vynaložených prostriedkov, Inactive,neaktívne, Incentives,Pobídky, -Include Default Book Entries,Zahrnúť predvolené položky knihy, +Include Default FB Entries,Zahrnúť predvolené položky knihy, Include Exploded Items,Zahrňte explodované položky, Include POS Transactions,Zahrňte POS transakcie, Include UOM,Zahrňte UOM, diff --git a/erpnext/translations/sl.csv b/erpnext/translations/sl.csv index 5380714bdc..86b5e58129 100644 --- a/erpnext/translations/sl.csv +++ b/erpnext/translations/sl.csv @@ -1153,7 +1153,7 @@ In Value,V vrednosti, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",V primeru večstopenjskega programa bodo stranke samodejno dodeljene zadevni stopnji glede na porabljene, Inactive,Neaktivno, Incentives,Spodbude, -Include Default Book Entries,Vključi privzete vnose v knjige, +Include Default FB Entries,Vključi privzete vnose v knjige, Include Exploded Items,Vključi eksplodirane elemente, Include POS Transactions,Vključite POS transakcije, Include UOM,Vključi UOM, diff --git a/erpnext/translations/sq.csv b/erpnext/translations/sq.csv index 2a893d272b..3cfa4297df 100644 --- a/erpnext/translations/sq.csv +++ b/erpnext/translations/sq.csv @@ -1153,7 +1153,7 @@ In Value,Në Vlera, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Në rastin e programit multi-shtresor, Konsumatorët do të caktohen automatikisht në nivelin përkatës sipas shpenzimeve të tyre", Inactive,joaktiv, Incentives,Nxitjet, -Include Default Book Entries,Përfshini hyrje të librave të paracaktuar, +Include Default FB Entries,Përfshini hyrje të librave të paracaktuar, Include Exploded Items,Përfshirja e artikujve të eksploduar, Include POS Transactions,Përfshi transaksione POS, Include UOM,Përfshi UOM, diff --git a/erpnext/translations/sr.csv b/erpnext/translations/sr.csv index c1e5eb0eea..621772f39f 100644 --- a/erpnext/translations/sr.csv +++ b/erpnext/translations/sr.csv @@ -1153,7 +1153,7 @@ In Value,У вредности, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","У случају мулти-тиер програма, Корисници ће аутоматски бити додијељени за одређени ниво према њиховом потрошеном", Inactive,Неактиван, Incentives,Подстицаји, -Include Default Book Entries,Укључивање заданих уноса у књиге, +Include Default FB Entries,Укључивање заданих уноса у књиге, Include Exploded Items,Укључите експлодиране ставке, Include POS Transactions,Укључите ПОС трансакције, Include UOM,Укључите УОМ, diff --git a/erpnext/translations/sv.csv b/erpnext/translations/sv.csv index 8b4ab068eb..4fef88b7f4 100644 --- a/erpnext/translations/sv.csv +++ b/erpnext/translations/sv.csv @@ -1153,7 +1153,7 @@ In Value,Värde, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",När det gäller program med flera nivåer kommer kunderna automatiskt att tilldelas den aktuella tiern enligt deras tillbringade, Inactive,Inaktiv, Incentives,Sporen, -Include Default Book Entries,Inkludera standardbokposter, +Include Default FB Entries,Inkludera standardbokposter, Include Exploded Items,Inkludera explosiva artiklar, Include POS Transactions,Inkludera POS-transaktioner, Include UOM,Inkludera UOM, diff --git a/erpnext/translations/sw.csv b/erpnext/translations/sw.csv index fa2287c3bb..3b4d8aee64 100644 --- a/erpnext/translations/sw.csv +++ b/erpnext/translations/sw.csv @@ -1153,7 +1153,7 @@ In Value,Kwa Thamani, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Katika kesi ya mpango wa mipango mbalimbali, Wateja watapatiwa auto kwa tier husika kama kwa matumizi yao", Inactive,Haikufanya kazi, Incentives,Vidokezo, -Include Default Book Entries,Jumuisha Ingizo Mbadala za Kitabu, +Include Default FB Entries,Jumuisha Ingizo Mbadala za Kitabu, Include Exploded Items,Jumuisha Vipengee Vipengee, Include POS Transactions,Jumuisha Shughuli za POS, Include UOM,Jumuisha UOM, diff --git a/erpnext/translations/ta.csv b/erpnext/translations/ta.csv index 6eaae34a6e..f40e512427 100644 --- a/erpnext/translations/ta.csv +++ b/erpnext/translations/ta.csv @@ -1153,7 +1153,7 @@ In Value,மதிப்பு, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","பல அடுக்கு திட்டத்தின் விஷயத்தில், வாடிக்கையாளர்கள் தங்கள் செலவினங்களின்படி சம்பந்தப்பட்ட அடுக்குக்கு கார் ஒதுக்கப்படுவார்கள்", Inactive,செயல்படா, Incentives,செயல் தூண்டுதல், -Include Default Book Entries,இயல்புநிலை புத்தக உள்ளீடுகளைச் சேர்க்கவும், +Include Default FB Entries,இயல்புநிலை புத்தக உள்ளீடுகளைச் சேர்க்கவும், Include Exploded Items,வெடித்துள்ள பொருட்கள் அடங்கும், Include POS Transactions,POS பரிமாற்றங்களைச் சேர்க்கவும், Include UOM,UOM ஐ சேர்க்கவும், diff --git a/erpnext/translations/te.csv b/erpnext/translations/te.csv index d3f739af8b..22fd7d7d1d 100644 --- a/erpnext/translations/te.csv +++ b/erpnext/translations/te.csv @@ -1153,7 +1153,7 @@ In Value,విలువ, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","మల్టీ-టైర్ ప్రోగ్రామ్ విషయంలో, కస్టమర్లు వారి గడువు ప్రకారం, సంబంధిత స్థాయికి కేటాయించబడతారు", Inactive,క్రియారహిత, Incentives,ఇన్సెంటివ్స్, -Include Default Book Entries,డిఫాల్ట్ బుక్ ఎంట్రీలను చేర్చండి, +Include Default FB Entries,డిఫాల్ట్ బుక్ ఎంట్రీలను చేర్చండి, Include Exploded Items,ఎక్స్ప్లోడ్ ఐటెమ్లను చేర్చండి, Include POS Transactions,POS లావాదేవీలను చేర్చండి, Include UOM,UOM ని చేర్చండి, diff --git a/erpnext/translations/th.csv b/erpnext/translations/th.csv index a065595898..5dfb93c585 100644 --- a/erpnext/translations/th.csv +++ b/erpnext/translations/th.csv @@ -1153,7 +1153,7 @@ In Value,ในราคา, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",ในกรณีของโปรแกรมแบบหลายชั้นลูกค้าจะได้รับการกำหนดให้โดยอัตโนมัติตามระดับที่เกี่ยวข้องตามการใช้จ่าย, Inactive,เฉื่อยชา, Incentives,แรงจูงใจ, -Include Default Book Entries,รวมรายการหนังสือเริ่มต้น, +Include Default FB Entries,รวมรายการหนังสือเริ่มต้น, Include Exploded Items,รวมรายการที่ระเบิดแล้ว, Include POS Transactions,รวมธุรกรรม POS, Include UOM,รวม UOM, diff --git a/erpnext/translations/tr.csv b/erpnext/translations/tr.csv index 9e916f0315..82d28240c1 100644 --- a/erpnext/translations/tr.csv +++ b/erpnext/translations/tr.csv @@ -1153,7 +1153,7 @@ In Value,Giriş Maliyeti, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Çok katmanlı program söz konusu olduğunda, Müşteriler harcanan esasa göre ilgili kademeye otomatik olarak atanacaktır.", Inactive,etkisiz, Incentives,Teşvikler, -Include Default Book Entries,Varsayılan Defter Girişlerini Dahil et, +Include Default FB Entries,Varsayılan Defter Girişlerini Dahil et, Include Exploded Items,Patlatılmış Öğeleri Dahil et, Include POS Transactions,POS İşlemlerini Dahil et, Include UOM,Birimi Dahil et, diff --git a/erpnext/translations/uk.csv b/erpnext/translations/uk.csv index 8d1fb04fdb..f77c6da35e 100644 --- a/erpnext/translations/uk.csv +++ b/erpnext/translations/uk.csv @@ -1153,7 +1153,7 @@ In Value,У Сумі, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","У випадку багаторівневої програми, Клієнти будуть автоматично призначені для відповідного рівня відповідно до витрачених ними витрат", Inactive,Неактивний, Incentives,Стимули, -Include Default Book Entries,Включити записи за замовчуванням, +Include Default FB Entries,Включити записи за замовчуванням, Include Exploded Items,Включити вибухнуті елементи, Include POS Transactions,Включити POS-транзакції, Include UOM,Включити UOM, diff --git a/erpnext/translations/ur.csv b/erpnext/translations/ur.csv index 649c1c7759..4dc872be5d 100644 --- a/erpnext/translations/ur.csv +++ b/erpnext/translations/ur.csv @@ -1153,7 +1153,7 @@ In Value,قدر میں, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",کثیر درجے کے پروگرام کے معاملے میں، صارفین اپنے اخراجات کے مطابق متعلقہ درجے کو خود کار طریقے سے تفویض کریں گے, Inactive,غیر فعال, Incentives,ترغیبات, -Include Default Book Entries,ڈیفالٹ کتاب اندراجات شامل کریں۔, +Include Default FB Entries,ڈیفالٹ کتاب اندراجات شامل کریں۔, Include Exploded Items,دھماکہ خیز اشیاء شامل کریں, Include POS Transactions,پی او ایس کے لین دین میں شامل کریں, Include UOM,UOM شامل کریں, diff --git a/erpnext/translations/uz.csv b/erpnext/translations/uz.csv index 5ca51ccb0f..c09aa895e9 100644 --- a/erpnext/translations/uz.csv +++ b/erpnext/translations/uz.csv @@ -1153,7 +1153,7 @@ In Value,Qiymatida, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",Ko'p qatlamli dasturda mijozlar o'zlari sarflagan xarajatlariga muvofiq tegishli darajaga avtomatik tarzda topshiriladi, Inactive,Faol emas, Incentives,Rag'batlantirish, -Include Default Book Entries,Odatiy kitob yozuvlarini qo'shing, +Include Default FB Entries,Odatiy kitob yozuvlarini qo'shing, Include Exploded Items,Portlatilgan narsalarni qo'shish, Include POS Transactions,Qalin operatsiyalarni qo'shish, Include UOM,UOM ni qo'shing, diff --git a/erpnext/translations/vi.csv b/erpnext/translations/vi.csv index 7a005fa414..eb251a5978 100644 --- a/erpnext/translations/vi.csv +++ b/erpnext/translations/vi.csv @@ -1153,7 +1153,7 @@ In Value,Trong giá trị, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Trong trường hợp chương trình nhiều tầng, Khách hàng sẽ được tự động chỉ định cho cấp có liên quan theo mức chi tiêu của họ", Inactive,Không hoạt động, Incentives,Ưu đãi, -Include Default Book Entries,Bao gồm các mục sách mặc định, +Include Default FB Entries,Bao gồm các mục sách mặc định, Include Exploded Items,Bao gồm các mục đã Phát hiện, Include POS Transactions,Bao gồm giao dịch POS, Include UOM,Bao gồm UOM, diff --git a/erpnext/translations/zh.csv b/erpnext/translations/zh.csv index cf89dc6852..08f8d33578 100644 --- a/erpnext/translations/zh.csv +++ b/erpnext/translations/zh.csv @@ -1153,7 +1153,7 @@ In Value,金额, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",在多层程序的情况下,客户将根据其花费自动分配到相关层, Inactive,非活动的, Incentives,激励政策, -Include Default Book Entries,包括默认工作簿条目, +Include Default FB Entries,包括默认工作簿条目, Include Exploded Items,包含爆炸物料, Include POS Transactions,包括POS交易, Include UOM,包括基本计量单位, diff --git a/erpnext/translations/zh_tw.csv b/erpnext/translations/zh_tw.csv index 8ecbaaa9c5..dd683c5a27 100644 --- a/erpnext/translations/zh_tw.csv +++ b/erpnext/translations/zh_tw.csv @@ -1166,7 +1166,7 @@ In Value,在數值, "In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",在多層程序的情況下,客戶將根據其花費自動分配到相關層, Inactive,待用, Incentives,獎勵, -Include Default Book Entries,包括默認工作簿條目, +Include Default FB Entries,包括默認工作簿條目, Include Exploded Items,包含爆炸物品, Include UOM,包括UOM, Included in Gross Profit,包含在毛利潤中, From a89afb65d72b7beb985449151b25fc678b4c6891 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernd=20Oliver=20S=C3=BCnderhauf?= <46800703+bosue@users.noreply.github.com> Date: Wed, 8 Nov 2023 21:52:32 +0100 Subject: [PATCH 104/130] fix: Mark Status field in Payment Entry 'no_copy'. --- erpnext/accounts/doctype/payment_entry/payment_entry.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json index d7b6a198df..4d50a35ed4 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.json +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json @@ -595,6 +595,7 @@ "fieldname": "status", "fieldtype": "Select", "label": "Status", + "no_copy": 1, "options": "\nDraft\nSubmitted\nCancelled", "read_only": 1 }, @@ -750,7 +751,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2023-06-23 18:07:38.023010", + "modified": "2023-11-08 21:51:03.482709", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Entry", From 6e3e094c957d8c2b71ed522e9a8034364345e5a7 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Thu, 2 Nov 2023 17:19:06 +0530 Subject: [PATCH 105/130] refactor: ignore disabled account while selecting Income Accounts --- .../doctype/sales_invoice/sales_invoice.js | 20 +++++++++---------- erpnext/controllers/queries.py | 2 ++ 2 files changed, 12 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js index d4d923902f..b1a7b10eea 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js @@ -187,7 +187,6 @@ erpnext.accounts.SalesInvoiceController = class SalesInvoiceController extends e erpnext.accounts.unreconcile_payments.add_unreconcile_btn(me.frm); } - make_maintenance_schedule() { frappe.model.open_mapped_doc({ method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.make_maintenance_schedule", @@ -563,15 +562,6 @@ cur_frm.fields_dict.write_off_cost_center.get_query = function(doc) { } } -// Income Account in Details Table -// -------------------------------- -cur_frm.set_query("income_account", "items", function(doc) { - return{ - query: "erpnext.controllers.queries.get_income_account", - filters: {'company': doc.company} - } -}); - // Cost Center in Details Table // ----------------------------- cur_frm.fields_dict["items"].grid.get_field("cost_center").get_query = function(doc) { @@ -666,6 +656,16 @@ frappe.ui.form.on('Sales Invoice', { }; }); + frm.set_query("income_account", "items", function() { + return{ + query: "erpnext.controllers.queries.get_income_account", + filters: { + 'company': frm.doc.company, + "disabled": 0 + } + } + }); + frm.custom_make_buttons = { 'Delivery Note': 'Delivery', 'Sales Invoice': 'Return / Credit Note', diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 5ec24743d9..199732b152 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -611,6 +611,8 @@ def get_income_account(doctype, txt, searchfield, start, page_len, filters): if filters.get("company"): condition += "and tabAccount.company = %(company)s" + condition += f"and tabAccount.disabled = {filters.get('disabled', 0)}" + return frappe.db.sql( """select tabAccount.name from `tabAccount` where (tabAccount.report_type = "Profit and Loss" From 779260fb497d6ac8000a89b7b762145c58e84471 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Thu, 9 Nov 2023 12:19:45 +0530 Subject: [PATCH 106/130] fix: make item field read-only in batch --- erpnext/stock/doctype/batch/batch.json | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/stock/doctype/batch/batch.json b/erpnext/stock/doctype/batch/batch.json index e6cb3516a3..e20030a568 100644 --- a/erpnext/stock/doctype/batch/batch.json +++ b/erpnext/stock/doctype/batch/batch.json @@ -61,6 +61,7 @@ "oldfieldname": "item", "oldfieldtype": "Link", "options": "Item", + "read_only_depends_on": "eval:!doc.__islocal", "reqd": 1 }, { @@ -207,7 +208,7 @@ "image_field": "image", "links": [], "max_attachments": 5, - "modified": "2023-03-12 15:56:09.516586", + "modified": "2023-11-09 12:17:28.339975", "modified_by": "Administrator", "module": "Stock", "name": "Batch", @@ -224,7 +225,6 @@ "read": 1, "report": 1, "role": "Item Manager", - "set_user_permissions": 1, "share": 1, "write": 1 } From a8216b9727401bfb6900f15447ab935756d7f353 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Thu, 9 Nov 2023 12:22:26 +0530 Subject: [PATCH 107/130] fix: make adjustment entry using stock reconciliation (#37995) fix: do adjustment entry using stock reconciliation --- .../stock_ledger_entry.json | 9 +++++- .../stock_reconciliation.py | 20 +++++++++++++ erpnext/stock/stock_ledger.py | 30 +++++++++++++++++-- 3 files changed, 56 insertions(+), 3 deletions(-) diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json index 569f58a69f..dcbd9b2d06 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json +++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json @@ -11,6 +11,7 @@ "warehouse", "posting_date", "posting_time", + "is_adjustment_entry", "column_break_6", "voucher_type", "voucher_no", @@ -333,6 +334,12 @@ "fieldname": "has_serial_no", "fieldtype": "Check", "label": "Has Serial No" + }, + { + "default": "0", + "fieldname": "is_adjustment_entry", + "fieldtype": "Check", + "label": "Is Adjustment Entry" } ], "hide_toolbar": 1, @@ -341,7 +348,7 @@ "in_create": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2023-04-03 16:33:16.270722", + "modified": "2023-10-23 18:07:42.063615", "modified_by": "Administrator", "module": "Stock", "name": "Stock Ledger Entry", diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index 323ad4f57d..1bf143b49c 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -442,6 +442,11 @@ class StockReconciliation(StockController): sl_entries = [] for row in self.items: + + if not row.qty and not row.valuation_rate and not row.current_qty: + self.make_adjustment_entry(row, sl_entries) + continue + item = frappe.get_cached_value( "Item", row.item_code, ["has_serial_no", "has_batch_no"], as_dict=1 ) @@ -492,6 +497,21 @@ class StockReconciliation(StockController): ) self.make_sl_entries(sl_entries, allow_negative_stock=allow_negative_stock) + def make_adjustment_entry(self, row, sl_entries): + from erpnext.stock.stock_ledger import get_stock_value_difference + + difference_amount = get_stock_value_difference( + row.item_code, row.warehouse, self.posting_date, self.posting_time + ) + + if not difference_amount: + return + + args = self.get_sle_for_items(row) + args.update({"stock_value_difference": -1 * difference_amount, "is_adjustment_entry": 1}) + + sl_entries.append(args) + def get_sle_for_serialized_items(self, row, sl_entries): if row.current_serial_and_batch_bundle: args = self.get_sle_for_items(row) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index e9381d42b9..63908945c3 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -759,9 +759,11 @@ class update_entries_after(object): sle.valuation_rate = self.wh_data.valuation_rate sle.stock_value = self.wh_data.stock_value sle.stock_queue = json.dumps(self.wh_data.stock_queue) - sle.stock_value_difference = stock_value_difference - sle.doctype = "Stock Ledger Entry" + if not sle.is_adjustment_entry or not self.args.get("sle_id"): + sle.stock_value_difference = stock_value_difference + + sle.doctype = "Stock Ledger Entry" frappe.get_doc(sle).db_update() if not self.args.get("sle_id"): @@ -1939,3 +1941,27 @@ def is_internal_transfer(sle): if data.is_internal_supplier and data.represents_company == data.company: return True + + +def get_stock_value_difference(item_code, warehouse, posting_date, posting_time, voucher_no=None): + table = frappe.qb.DocType("Stock Ledger Entry") + + query = ( + frappe.qb.from_(table) + .select(Sum(table.stock_value_difference).as_("value")) + .where( + (table.is_cancelled == 0) + & (table.item_code == item_code) + & (table.warehouse == warehouse) + & ( + (table.posting_date < posting_date) + | ((table.posting_date == posting_date) & (table.posting_time <= posting_time)) + ) + ) + ) + + if voucher_no: + query = query.where(table.voucher_no != voucher_no) + + difference_amount = query.run() + return flt(difference_amount[0][0]) if difference_amount else 0 From 56ac3424d22b68bbc551913133803686431d1694 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 9 Nov 2023 12:50:44 +0100 Subject: [PATCH 108/130] fix: Alert message and make sure invoice due dates are different for effective test - Make invoice due dates are different so that the invoice with the earliest due date is allocated first in the test - Translate voucher type, simplify alert message. The invoice could be "split" into 1 row, no. of rows in the message seems unnecessary. --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 4 ++-- .../accounts/doctype/payment_entry/test_payment_entry.py | 9 ++++++--- 2 files changed, 8 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index fc6e1f462e..de48b1189b 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1713,8 +1713,8 @@ def split_invoices_based_on_payment_terms(outstanding_invoices, company) -> list continue frappe.msgprint( - _("Spliting {} {} into {} row(s) as per Payment Terms").format( - split_rows[0]["voucher_type"], split_rows[0]["voucher_no"], len(split_rows) + _("Splitting {0} {1} as per Payment Terms").format( + _(entry.voucher_type), frappe.bold(entry.voucher_no) ), alert=True, ) diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index b3511f0f52..797a363aba 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -1270,7 +1270,10 @@ class TestPaymentEntry(FrappeTestCase): customer = create_customer("Max Mustermann", "INR") create_payment_terms_template() - si = create_sales_invoice(qty=1, rate=100, customer=customer) + # SI has an earlier due date and SI2 has a later due date + si = create_sales_invoice( + qty=1, rate=100, customer=customer, posting_date=add_days(nowdate(), -4) + ) si2 = create_sales_invoice(do_not_save=1, qty=1, rate=100, customer=customer) si2.payment_terms_template = "Test Receivable Template" si2.submit() @@ -1286,8 +1289,8 @@ class TestPaymentEntry(FrappeTestCase): args.update( { "get_outstanding_invoices": True, - "from_posting_date": nowdate(), - "to_posting_date": add_days(nowdate(), 7), + "from_posting_date": add_days(nowdate(), -4), + "to_posting_date": add_days(nowdate(), 2), } ) references = get_outstanding_reference_documents(args) From 4b4b176fcf99241bf17067989ebc850b2de24bcb Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 9 Nov 2023 13:03:52 +0100 Subject: [PATCH 109/130] style: Remove spaces introduced via merge conflict --- erpnext/accounts/doctype/payment_entry/test_payment_entry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py index 0d5387ef40..603f24a09c 100644 --- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py @@ -1288,7 +1288,7 @@ class TestPaymentEntry(FrappeTestCase): self.assertEqual(references[2].voucher_no, si2.name) self.assertEqual(references[1].payment_term, "Basic Amount Receivable") self.assertEqual(references[2].payment_term, "Tax Receivable") - + def test_receive_payment_from_payable_party_type(self): pe = create_payment_entry( party_type="Supplier", From c2bda2c70535ad9e8a61e752bbb551f387460125 Mon Sep 17 00:00:00 2001 From: Vishakh Desai <78500008+vishakhdesai@users.noreply.github.com> Date: Thu, 9 Nov 2023 18:26:34 +0530 Subject: [PATCH 110/130] fix: Supplier Quotation fields (#37963) --- erpnext/accounts/party.py | 7 ++- .../request_for_quotation.json | 16 ++++- .../supplier_quotation.json | 60 ++++++++++++++++++- erpnext/public/js/utils/party.js | 2 +- 4 files changed, 81 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index 16e73ea52f..371474e0a6 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -31,7 +31,12 @@ from erpnext.accounts.utils import get_fiscal_year from erpnext.exceptions import InvalidAccountCurrency, PartyDisabled, PartyFrozen from erpnext.utilities.regional import temporary_flag -PURCHASE_TRANSACTION_TYPES = {"Purchase Order", "Purchase Receipt", "Purchase Invoice"} +PURCHASE_TRANSACTION_TYPES = { + "Supplier Quotation", + "Purchase Order", + "Purchase Receipt", + "Purchase Invoice", +} SALES_TRANSACTION_TYPES = { "Quotation", "Sales Order", diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json index 06dbd86ba1..fd73f77ff8 100644 --- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json +++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json @@ -9,6 +9,8 @@ "field_order": [ "naming_series", "company", + "billing_address", + "billing_address_display", "vendor", "column_break1", "transaction_date", @@ -292,13 +294,25 @@ "fieldtype": "Check", "label": "Send Document Print", "print_hide": 1 + }, + { + "fieldname": "billing_address", + "fieldtype": "Link", + "label": "Company Billing Address", + "options": "Address" + }, + { + "fieldname": "billing_address_display", + "fieldtype": "Small Text", + "label": "Billing Address Details", + "read_only": 1 } ], "icon": "fa fa-shopping-cart", "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2023-08-09 12:20:26.850623", + "modified": "2023-11-06 12:45:28.898706", "modified_by": "Administrator", "module": "Buying", "name": "Request for Quotation", diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json index 7b635b36ba..ad1aa2b108 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json @@ -79,6 +79,7 @@ "pricing_rule_details", "pricing_rules", "address_and_contact_tab", + "supplier_address_section", "supplier_address", "address_display", "column_break_72", @@ -86,6 +87,14 @@ "contact_display", "contact_mobile", "contact_email", + "shipping_address_section", + "shipping_address", + "column_break_zjaq", + "shipping_address_display", + "company_billing_address_section", + "billing_address", + "column_break_gcth", + "billing_address_display", "terms_tab", "tc_name", "terms", @@ -838,6 +847,55 @@ "fieldname": "named_place", "fieldtype": "Data", "label": "Named Place" + }, + { + "fieldname": "shipping_address", + "fieldtype": "Link", + "label": "Shipping Address", + "options": "Address", + "print_hide": 1 + }, + { + "fieldname": "column_break_zjaq", + "fieldtype": "Column Break" + }, + { + "fieldname": "shipping_address_display", + "fieldtype": "Small Text", + "label": "Shipping Address Details", + "print_hide": 1, + "read_only": 1 + }, + { + "fieldname": "shipping_address_section", + "fieldtype": "Section Break", + "label": "Shipping Address" + }, + { + "fieldname": "supplier_address_section", + "fieldtype": "Section Break", + "label": "Supplier Address" + }, + { + "fieldname": "company_billing_address_section", + "fieldtype": "Section Break", + "label": "Company Billing Address" + }, + { + "fieldname": "billing_address", + "fieldtype": "Link", + "label": "Company Billing Address", + "options": "Address" + }, + { + "fieldname": "column_break_gcth", + "fieldtype": "Column Break" + }, + { + "fieldname": "billing_address_display", + "fieldtype": "Small Text", + "label": "Billing Address Details", + "read_only": 1 } ], "icon": "fa fa-shopping-cart", @@ -845,7 +903,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2023-06-03 16:20:15.880114", + "modified": "2023-11-03 13:21:40.172508", "modified_by": "Administrator", "module": "Buying", "name": "Supplier Quotation", diff --git a/erpnext/public/js/utils/party.js b/erpnext/public/js/utils/party.js index 5c41aa0680..cba615c0d2 100644 --- a/erpnext/public/js/utils/party.js +++ b/erpnext/public/js/utils/party.js @@ -4,7 +4,7 @@ frappe.provide("erpnext.utils"); const SALES_DOCTYPES = ['Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice']; -const PURCHASE_DOCTYPES = ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice']; +const PURCHASE_DOCTYPES = ['Supplier Quotation','Purchase Order', 'Purchase Receipt', 'Purchase Invoice']; erpnext.utils.get_party_details = function(frm, method, args, callback) { if (!method) { From 92f6d2c87cb54214d7afe41adcc3084dccce903c Mon Sep 17 00:00:00 2001 From: Sympatron GmbH <35803463+Sympatron@users.noreply.github.com> Date: Thu, 9 Nov 2023 13:57:11 +0100 Subject: [PATCH 111/130] chore: Update de.csv (#37810) chore: chore: Update de.csv --- erpnext/translations/de.csv | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index 61e301eed4..73755be653 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -5074,7 +5074,7 @@ Percentage you are allowed to transfer more against the quantity ordered. For ex PUR-ORD-.YYYY.-,PUR-ORD-.YYYY.-, Get Items from Open Material Requests,Hole Artikel von offenen Material Anfragen, Fetch items based on Default Supplier.,Abrufen von Elementen basierend auf dem Standardlieferanten., -Required By,Benötigt von, +Required By,Benötigt bis, Order Confirmation No,Auftragsbestätigung Nr, Order Confirmation Date,Auftragsbestätigungsdatum, Customer Mobile No,Mobilnummer des Kunden, From 45b4bfc94783bb0180c0a57a2f3a5bb7086008a3 Mon Sep 17 00:00:00 2001 From: jabir-elat <44110258+jabir-elat@users.noreply.github.com> Date: Thu, 9 Nov 2023 18:30:00 +0530 Subject: [PATCH 112/130] fix: sales order not assigned to territory orders (#37905) filtered sales order are not assigned to 'territory_orders' which results in 0 order amount and 0 billing amount in the output --- .../selling/report/territory_wise_sales/territory_wise_sales.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/selling/report/territory_wise_sales/territory_wise_sales.py b/erpnext/selling/report/territory_wise_sales/territory_wise_sales.py index 5dfc1db097..ecb63d890a 100644 --- a/erpnext/selling/report/territory_wise_sales/territory_wise_sales.py +++ b/erpnext/selling/report/territory_wise_sales/territory_wise_sales.py @@ -80,7 +80,7 @@ def get_data(filters=None): territory_orders = [] if t_quotation_names and sales_orders: - list(filter(lambda x: x.quotation in t_quotation_names, sales_orders)) + territory_orders = list(filter(lambda x: x.quotation in t_quotation_names, sales_orders)) t_order_names = [] if territory_orders: t_order_names = [t.name for t in territory_orders] From 1fc5844025d97de150195b94070b0c4061378992 Mon Sep 17 00:00:00 2001 From: marination Date: Thu, 9 Nov 2023 15:01:58 +0100 Subject: [PATCH 113/130] fix: Re-add no.of rows split in alert message --- erpnext/accounts/doctype/payment_entry/payment_entry.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 45bb434d65..fc22f53f4d 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -1699,8 +1699,8 @@ def split_invoices_based_on_payment_terms(outstanding_invoices, company) -> list continue frappe.msgprint( - _("Splitting {0} {1} as per Payment Terms").format( - _(entry.voucher_type), frappe.bold(entry.voucher_no) + _("Splitting {0} {1} into {2} rows as per Payment Terms").format( + _(entry.voucher_type), frappe.bold(entry.voucher_no), len(split_rows) ), alert=True, ) From 922fffda1f4160aeeffdb576c76845f5bbd587dc Mon Sep 17 00:00:00 2001 From: David Arnold Date: Thu, 9 Nov 2023 17:34:27 +0100 Subject: [PATCH 114/130] fix(payments): incoming payment requests aren't supposed to be in 'initiated' state (only outgoing are) (#37447) * fix(payments): incoming payment requests aren't supposed to be in 'initiated' state (only outgoing are) * fixup! fix(payments): incoming payment requests aren't supposed to be in 'initiated' state (only outgoing are) --- .../doctype/payment_request/payment_request.py | 7 ------- erpnext/patches.txt | 1 + .../v15_0/migrate_payment_request_status.py | 15 +++++++++++++++ 3 files changed, 16 insertions(+), 7 deletions(-) create mode 100644 erpnext/patches/v15_0/migrate_payment_request_status.py diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py index 5f0b434c70..c2e01c4ba3 100644 --- a/erpnext/accounts/doctype/payment_request/payment_request.py +++ b/erpnext/accounts/doctype/payment_request/payment_request.py @@ -175,13 +175,6 @@ class PaymentRequest(Document): if self.payment_url: self.db_set("payment_url", self.payment_url) - if ( - self.payment_url - or not self.payment_gateway_account - or (self.payment_gateway_account and self.payment_channel == "Phone") - ): - self.db_set("status", "Initiated") - def get_payment_url(self): if self.reference_doctype != "Fees": data = frappe.db.get_value( diff --git a/erpnext/patches.txt b/erpnext/patches.txt index d394db6d96..0aeadce6c8 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -338,6 +338,7 @@ erpnext.patches.v15_0.delete_woocommerce_settings_doctype erpnext.patches.v14_0.migrate_deferred_accounts_to_item_defaults erpnext.patches.v14_0.update_invoicing_period_in_subscription execute:frappe.delete_doc("Page", "welcome-to-erpnext") +erpnext.patches.v15_0.migrate_payment_request_status erpnext.patches.v15_0.delete_payment_gateway_doctypes erpnext.patches.v14_0.create_accounting_dimensions_in_sales_order_item erpnext.patches.v15_0.update_sre_from_voucher_details diff --git a/erpnext/patches/v15_0/migrate_payment_request_status.py b/erpnext/patches/v15_0/migrate_payment_request_status.py new file mode 100644 index 0000000000..746a67bced --- /dev/null +++ b/erpnext/patches/v15_0/migrate_payment_request_status.py @@ -0,0 +1,15 @@ +import frappe + + +def execute(): + """ + Description: + Change Inward Payment Requests from statut 'Initiated' to correct status 'Requested'. + Status 'Initiated' is reserved for Outward Payment Requests and was a semantic error in previour versions. + """ + + if frappe.reload_doc("accounts", "doctype", "Payment Request"): + so = frappe.qb.DocType("Payment Request") + frappe.qb.update(so).set(so.status, "Requested").where( + so.payment_request_type == "Inward" + ).where(so.status == "Initiated").run() From 1d8fcd66e6ca6d17a274a8b18699b40a7ac93e1a Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 10 Nov 2023 09:49:55 +0530 Subject: [PATCH 115/130] fix: new logic for handling revaluation journals --- .../accounts_receivable/accounts_receivable.js | 10 ++++++++-- .../accounts_receivable/accounts_receivable.py | 17 +++++++++++++---- 2 files changed, 21 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js index 43ea532291..b4bc8870d3 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js @@ -180,13 +180,19 @@ frappe.query_reports["Accounts Receivable"] = { "fieldname": "in_party_currency", "label": __("In Party Currency"), "fieldtype": "Check", - }, - { + }, + { + "fieldname": "for_revaluation_journals", + "label": __("Revaluation Journals"), + "fieldtype": "Check", + }, + { "fieldname": "ignore_accounts", "label": __("Group by Voucher"), "fieldtype": "Check", } + ], "formatter": function(value, row, column, data, default_formatter) { diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py index 05403743fa..706d743849 100644 --- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py +++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py @@ -283,11 +283,20 @@ class ReceivablePayableReport(object): row.invoice_grand_total = row.invoiced - if (abs(row.outstanding) > 1.0 / 10**self.currency_precision) and ( - (abs(row.outstanding_in_account_currency) > 1.0 / 10**self.currency_precision) - or (row.voucher_no in self.err_journals) - ): + must_consider = False + if self.filters.get("for_revaluation_journals"): + if (abs(row.outstanding) > 1.0 / 10**self.currency_precision) or ( + (abs(row.outstanding_in_account_currency) > 1.0 / 10**self.currency_precision) + ): + must_consider = True + else: + if (abs(row.outstanding) > 1.0 / 10**self.currency_precision) and ( + (abs(row.outstanding_in_account_currency) > 1.0 / 10**self.currency_precision) + or (row.voucher_no in self.err_journals) + ): + must_consider = True + if must_consider: # non-zero oustanding, we must consider this row if self.is_invoice(row) and self.filters.based_on_payment_terms: From ae508144cdce348af11c406f6fd30e73f4dc3879 Mon Sep 17 00:00:00 2001 From: David Arnold Date: Sat, 11 Nov 2023 15:05:07 +0100 Subject: [PATCH 116/130] fix(customer): quick form and integration fixes (#37386) --- erpnext/selling/doctype/customer/customer.py | 21 +++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index a7a1aa2659..9e6095fac3 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -638,7 +638,7 @@ def make_contact(args, is_primary_contact=1): contact = frappe.get_doc( { "doctype": "Contact", - "first_name": args.get("name"), + "first_name": args.get("customer_name"), "is_primary_contact": is_primary_contact, "links": [{"link_doctype": args.get("doctype"), "link_name": args.get("name")}], } @@ -647,12 +647,16 @@ def make_contact(args, is_primary_contact=1): contact.add_email(args.get("email_id"), is_primary=True) if args.get("mobile_no"): contact.add_phone(args.get("mobile_no"), is_primary_mobile_no=True) - contact.insert() + + if flags := args.get("flags"): + contact.insert(ignore_permissions=flags.get("ignore_permissions")) + else: + contact.insert() return contact -def make_address(args, is_primary_address=1): +def make_address(args, is_primary_address=1, is_shipping_address=1): reqd_fields = [] for field in ["city", "country"]: if not args.get(field): @@ -668,16 +672,23 @@ def make_address(args, is_primary_address=1): address = frappe.get_doc( { "doctype": "Address", - "address_title": args.get("name"), + "address_title": args.get("customer_name"), "address_line1": args.get("address_line1"), "address_line2": args.get("address_line2"), "city": args.get("city"), "state": args.get("state"), "pincode": args.get("pincode"), "country": args.get("country"), + "is_primary_address": is_primary_address, + "is_shipping_address": is_shipping_address, "links": [{"link_doctype": args.get("doctype"), "link_name": args.get("name")}], } - ).insert() + ) + + if flags := args.get("flags"): + address.insert(ignore_permissions=flags.get("ignore_permissions")) + else: + address.insert() return address From 4ed9927a304fa88d89c8eaaea2810eb61ee3956e Mon Sep 17 00:00:00 2001 From: David Arnold Date: Sat, 11 Nov 2023 15:07:23 +0100 Subject: [PATCH 117/130] fix(patch): to migrate properly in post section (#38045) --- .../patches/v15_0/migrate_payment_request_status.py | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/erpnext/patches/v15_0/migrate_payment_request_status.py b/erpnext/patches/v15_0/migrate_payment_request_status.py index 746a67bced..9f0de56621 100644 --- a/erpnext/patches/v15_0/migrate_payment_request_status.py +++ b/erpnext/patches/v15_0/migrate_payment_request_status.py @@ -7,9 +7,7 @@ def execute(): Change Inward Payment Requests from statut 'Initiated' to correct status 'Requested'. Status 'Initiated' is reserved for Outward Payment Requests and was a semantic error in previour versions. """ - - if frappe.reload_doc("accounts", "doctype", "Payment Request"): - so = frappe.qb.DocType("Payment Request") - frappe.qb.update(so).set(so.status, "Requested").where( - so.payment_request_type == "Inward" - ).where(so.status == "Initiated").run() + so = frappe.qb.DocType("Payment Request") + frappe.qb.update(so).set(so.status, "Requested").where(so.payment_request_type == "Inward").where( + so.status == "Initiated" + ).run() From 59438ee8d48c0feee03d0d97852c9a9bb359c019 Mon Sep 17 00:00:00 2001 From: hyaray Date: Sat, 11 Nov 2023 22:08:24 +0800 Subject: [PATCH 118/130] chore: Add missing translations (#38043) chore: Add missing translations --- erpnext/public/js/controllers/transaction.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index b0a9e405cd..2c40f4964b 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -1772,7 +1772,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe if(frappe.meta.has_field(me.frm.doc.doctype, fieldname) && !["Purchase Order","Purchase Invoice"].includes(me.frm.doc.doctype)) { if (!me.frm.doc[fieldname]) { frappe.msgprint(__("Please specify") + ": " + - frappe.meta.get_label(me.frm.doc.doctype, fieldname, me.frm.doc.name) + + __(frappe.meta.get_label(me.frm.doc.doctype, fieldname, me.frm.doc.name)) + ". " + __("It is needed to fetch Item Details.")); valid = false; } From a74e1f1600c1c6e3a7ea18b9bf6a0e70db86311a Mon Sep 17 00:00:00 2001 From: David Arnold Date: Sat, 11 Nov 2023 15:50:21 +0100 Subject: [PATCH 119/130] fix(crm): ensure primary address and contact follows customer setting (#37710) Co-authored-by: Deepesh Garg --- erpnext/crm/doctype/lead/lead.py | 9 +++++++++ erpnext/selling/doctype/customer/customer.py | 5 +++++ 2 files changed, 14 insertions(+) diff --git a/erpnext/crm/doctype/lead/lead.py b/erpnext/crm/doctype/lead/lead.py index e897ba41eb..fdec88d70d 100644 --- a/erpnext/crm/doctype/lead/lead.py +++ b/erpnext/crm/doctype/lead/lead.py @@ -7,6 +7,8 @@ from frappe.contacts.address_and_contact import ( delete_contact_and_address, load_address_and_contact, ) +from frappe.contacts.doctype.address.address import get_default_address +from frappe.contacts.doctype.contact.contact import get_default_contact from frappe.email.inbox import link_communication_to_document from frappe.model.mapper import get_mapped_doc from frappe.utils import comma_and, get_link_to_form, has_gravatar, validate_email_address @@ -251,6 +253,13 @@ def _make_customer(source_name, target_doc=None, ignore_permissions=False): target.customer_group = frappe.db.get_default("Customer Group") + address = get_default_address("Lead", source.name) + contact = get_default_contact("Lead", source.name) + if address: + target.customer_primary_address = address + if contact: + target.customer_primary_contact = contact + doclist = get_mapped_doc( "Lead", source_name, diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 9e6095fac3..a4bb9d5381 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -186,6 +186,8 @@ class Customer(TransactionBase): self.db_set("customer_primary_contact", contact.name) self.db_set("mobile_no", self.mobile_no) self.db_set("email_id", self.email_id) + elif self.customer_primary_contact: + frappe.set_value("Contact", self.customer_primary_contact, "is_primary_contact", 1) # ensure def create_primary_address(self): from frappe.contacts.doctype.address.address import get_address_display @@ -196,6 +198,8 @@ class Customer(TransactionBase): self.db_set("customer_primary_address", address.name) self.db_set("primary_address", address_display) + elif self.customer_primary_address: + frappe.set_value("Address", self.customer_primary_address, "is_primary_address", 1) # ensure def update_lead_status(self): """If Customer created from Lead, update lead status to "Converted" @@ -312,6 +316,7 @@ def create_contact(contact, party_type, party, email): "doctype": "Contact", "first_name": contact[0], "last_name": len(contact) > 1 and contact[1] or "", + "is_primary_contact": 1, } ) contact.append("email_ids", dict(email_id=email, is_primary=1)) From 8634abc0210fdb259c8b0e7ffc93976ea0099057 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Sun, 12 Nov 2023 11:42:26 +0530 Subject: [PATCH 120/130] fix: COA Importer app related issues (#37238) * fix: COA Importer app related issues * fix: Clear all account links fields befor import * fix: Attribute error --- .../chart_of_accounts_importer.js | 15 ++++-- .../chart_of_accounts_importer.py | 52 ++++++------------- .../coa_sample_template.csv | 17 ++++++ 3 files changed, 44 insertions(+), 40 deletions(-) create mode 100644 erpnext/accounts/doctype/chart_of_accounts_importer/coa_sample_template.csv diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js index d61f8a6c01..56fa6ce2f3 100644 --- a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js +++ b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js @@ -53,10 +53,18 @@ frappe.ui.form.on('Chart of Accounts Importer', { of Accounts. Please enter the account names and add more rows as per your requirement.`); } } - } + }, + { + label : "Company", + fieldname: "company", + fieldtype: "Link", + reqd: 1, + hidden: 1, + default: frm.doc.company, + }, ], primary_action: function() { - var data = d.get_values(); + let data = d.get_values(); if (!data.template_type) { frappe.throw(__('Please select Template Type to download template')); @@ -66,7 +74,8 @@ frappe.ui.form.on('Chart of Accounts Importer', { '/api/method/erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.download_template', { file_type: data.file_type, - template_type: data.template_type + template_type: data.template_type, + company: data.company } ); diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py index d6e1be4123..5a1c139bde 100644 --- a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py +++ b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py @@ -8,6 +8,7 @@ from functools import reduce import frappe from frappe import _ +from frappe.desk.form.linked_with import get_linked_fields from frappe.model.document import Document from frappe.utils import cint, cstr from frappe.utils.csvutils import UnicodeWriter @@ -294,10 +295,8 @@ def build_response_as_excel(writer): @frappe.whitelist() -def download_template(file_type, template_type): - data = frappe._dict(frappe.local.form_dict) - - writer = get_template(template_type) +def download_template(file_type, template_type, company): + writer = get_template(template_type, company) if file_type == "CSV": # download csv file @@ -308,8 +307,7 @@ def download_template(file_type, template_type): build_response_as_excel(writer) -def get_template(template_type): - +def get_template(template_type, company): fields = [ "Account Name", "Parent Account", @@ -335,34 +333,17 @@ def get_template(template_type): ["", "", "", "", 0, account_type.get("account_type"), account_type.get("root_type")] ) else: - writer = get_sample_template(writer) + writer = get_sample_template(writer, company) return writer -def get_sample_template(writer): - template = [ - ["Application Of Funds(Assets)", "", "", "", 1, "", "Asset"], - ["Sources Of Funds(Liabilities)", "", "", "", 1, "", "Liability"], - ["Equity", "", "", "", 1, "", "Equity"], - ["Expenses", "", "", "", 1, "", "Expense"], - ["Income", "", "", "", 1, "", "Income"], - ["Bank Accounts", "Application Of Funds(Assets)", "", "", 1, "Bank", "Asset"], - ["Cash In Hand", "Application Of Funds(Assets)", "", "", 1, "Cash", "Asset"], - ["Stock Assets", "Application Of Funds(Assets)", "", "", 1, "Stock", "Asset"], - ["Cost Of Goods Sold", "Expenses", "", "", 0, "Cost of Goods Sold", "Expense"], - ["Asset Depreciation", "Expenses", "", "", 0, "Depreciation", "Expense"], - ["Fixed Assets", "Application Of Funds(Assets)", "", "", 0, "Fixed Asset", "Asset"], - ["Accounts Payable", "Sources Of Funds(Liabilities)", "", "", 0, "Payable", "Liability"], - ["Accounts Receivable", "Application Of Funds(Assets)", "", "", 1, "Receivable", "Asset"], - ["Stock Expenses", "Expenses", "", "", 0, "Stock Adjustment", "Expense"], - ["Sample Bank", "Bank Accounts", "", "", 0, "Bank", "Asset"], - ["Cash", "Cash In Hand", "", "", 0, "Cash", "Asset"], - ["Stores", "Stock Assets", "", "", 0, "Stock", "Asset"], - ] - - for row in template: - writer.writerow(row) +def get_sample_template(writer, company): + currency = frappe.db.get_value("Company", company, "default_currency") + with open(os.path.join(os.path.dirname(__file__), "coa_sample_template.csv"), "r") as f: + for row in f: + row = row.strip().split(",") + [currency] + writer.writerow(row) return writer @@ -453,14 +434,11 @@ def get_mandatory_account_types(): def unset_existing_data(company): - linked = frappe.db.sql( - '''select fieldname from tabDocField - where fieldtype="Link" and options="Account" and parent="Company"''', - as_dict=True, - ) - # remove accounts data from company - update_values = {d.fieldname: "" for d in linked} + + fieldnames = get_linked_fields("Account").get("Company", {}).get("fieldname", []) + linked = [{"fieldname": name} for name in fieldnames] + update_values = {d.get("fieldname"): "" for d in linked} frappe.db.set_value("Company", company, update_values, update_values) # remove accounts data from various doctypes diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/coa_sample_template.csv b/erpnext/accounts/doctype/chart_of_accounts_importer/coa_sample_template.csv new file mode 100644 index 0000000000..85a2f2112f --- /dev/null +++ b/erpnext/accounts/doctype/chart_of_accounts_importer/coa_sample_template.csv @@ -0,0 +1,17 @@ +Application Of Funds(Assets),,,,1,,Asset +Sources Of Funds(Liabilities),,,,1,,Liability +Equity,,,,1,,Equity +Expenses,,,,1,Expense Account,Expense +Income,,,,1,Income Account,Income +Bank Accounts,Application Of Funds(Assets),,,1,Bank,Asset +Cash In Hand,Application Of Funds(Assets),,,1,Cash,Asset +Stock Assets,Application Of Funds(Assets),,,1,Stock,Asset +Cost Of Goods Sold,Expenses,,,0,Cost of Goods Sold,Expense +Asset Depreciation,Expenses,,,0,Depreciation,Expense +Fixed Assets,Application Of Funds(Assets),,,0,Fixed Asset,Asset +Accounts Payable,Sources Of Funds(Liabilities),,,0,Payable,Liability +Accounts Receivable,Application Of Funds(Assets),,,1,Receivable,Asset +Stock Expenses,Expenses,,,0,Stock Adjustment,Expense +Sample Bank,Bank Accounts,,,0,Bank,Asset +Cash,Cash In Hand,,,0,Cash,Asset +Stores,Stock Assets,,,0,Stock,Asset \ No newline at end of file From 6f6d5cb4cffebefa8754b62ec0264c4aadc97cda Mon Sep 17 00:00:00 2001 From: Niraj Gautam Date: Sun, 12 Nov 2023 11:52:35 +0530 Subject: [PATCH 121/130] fix(POS): 100 % Discount on Point of Sales (#37411) fix: Allow 100% discount in POS --- erpnext/selling/page/point_of_sale/pos_controller.js | 8 ++++++++ erpnext/selling/page/point_of_sale/pos_payment.js | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js index db6255a4be..9bba4ebf50 100644 --- a/erpnext/selling/page/point_of_sale/pos_controller.js +++ b/erpnext/selling/page/point_of_sale/pos_controller.js @@ -548,6 +548,14 @@ erpnext.PointOfSale.Controller = class { if (!item_code) return; + if (rate == undefined || rate == 0) { + frappe.show_alert({ + message: __('Price is not set for the item.'), + indicator: 'orange' + }); + frappe.utils.play_sound("error"); + return; + } const new_item = { item_code, batch_no, rate, uom, [field]: value }; if (serial_no) { diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js index 89ce61ab16..63711c5ed2 100644 --- a/erpnext/selling/page/point_of_sale/pos_payment.js +++ b/erpnext/selling/page/point_of_sale/pos_payment.js @@ -203,7 +203,7 @@ erpnext.PointOfSale.Payment = class { const paid_amount = doc.paid_amount; const items = doc.items; - if (paid_amount == 0 || !items.length) { + if (!items.length || (paid_amount == 0 && doc.additional_discount_percentage != 100)) { const message = items.length ? __("You cannot submit the order without payment.") : __("You cannot submit empty order."); frappe.show_alert({ message, indicator: "orange" }); frappe.utils.play_sound("error"); From ecc305dd5929fbe1624e499cc355dd6058e21102 Mon Sep 17 00:00:00 2001 From: ViralKansodiya <141210323+viralkansodiya@users.noreply.github.com> Date: Sun, 12 Nov 2023 17:48:37 +0530 Subject: [PATCH 122/130] fix: add customer name and supplier name columns (#37557) fix: add customer name and supplier name columns --- .../address_and_contacts.py | 33 ++++++++++++++----- 1 file changed, 25 insertions(+), 8 deletions(-) diff --git a/erpnext/selling/report/address_and_contacts/address_and_contacts.py b/erpnext/selling/report/address_and_contacts/address_and_contacts.py index 4542bdff43..0a29d435a4 100644 --- a/erpnext/selling/report/address_and_contacts/address_and_contacts.py +++ b/erpnext/selling/report/address_and_contacts/address_and_contacts.py @@ -26,7 +26,7 @@ def execute(filters=None): def get_columns(filters): party_type = filters.get("party_type") party_type_value = get_party_group(party_type) - return [ + columns = [ "{party_type}:Link/{party_type}".format(party_type=party_type), "{party_value_type}::150".format(party_value_type=frappe.unscrub(str(party_type_value))), "Address Line 1", @@ -43,6 +43,15 @@ def get_columns(filters): "Email Id", "Is Primary Contact:Check", ] + if filters.get("party_type") == "Supplier" and frappe.db.get_single_value( + "Buying Settings", "supp_master_name" + ) == ["Naming Series", "Auto Name"]: + columns.insert(1, "Supplier Name:Data:150") + if filters.get("party_type") == "Customer" and frappe.db.get_single_value( + "Selling Settings", "cust_master_name" + ) == ["Naming Series", "Auto Name"]: + columns.insert(1, "Customer Name:Data:150") + return columns def get_data(filters): @@ -50,27 +59,33 @@ def get_data(filters): party = filters.get("party_name") party_group = get_party_group(party_type) - return get_party_addresses_and_contact(party_type, party, party_group) + return get_party_addresses_and_contact(party_type, party, party_group, filters) -def get_party_addresses_and_contact(party_type, party, party_group): +def get_party_addresses_and_contact(party_type, party, party_group, filters): data = [] - filters = None + query_filters = None party_details = frappe._dict() if not party_type: return [] if party: - filters = {"name": party} + query_filters = {"name": party} + if filters.get("party_type") in ["Customer", "Supplier"]: + field = filters.get("party_type").lower() + "_name" + else: + field = "partner_name" fetch_party_list = frappe.get_list( - party_type, filters=filters, fields=["name", party_group], as_list=True + party_type, filters=query_filters, fields=["name", party_group, field], as_list=True ) party_list = [d[0] for d in fetch_party_list] party_groups = {} + party_name_map = {} for d in fetch_party_list: party_groups[d[0]] = d[1] + party_name_map[d[0]] = d[2] for d in party_list: party_details.setdefault(d, frappe._dict()) @@ -84,6 +99,8 @@ def get_party_addresses_and_contact(party_type, party, party_group): if not any([addresses, contacts]): result = [party] result.append(party_groups[party]) + if filters.get("party_type") in ["Customer", "Supplier"]: + result.append(party_name_map[party]) result.extend(add_blank_columns_for("Contact")) result.extend(add_blank_columns_for("Address")) data.append(result) @@ -95,11 +112,12 @@ def get_party_addresses_and_contact(party_type, party, party_group): for idx in range(0, max_length): result = [party] result.append(party_groups[party]) + if filters.get("party_type") in ["Customer", "Supplier"]: + result.append(party_name_map[party]) address = addresses[idx] if idx < len(addresses) else add_blank_columns_for("Address") contact = contacts[idx] if idx < len(contacts) else add_blank_columns_for("Contact") result.extend(address) result.extend(contact) - data.append(result) return data @@ -115,7 +133,6 @@ def get_party_details(party_type, party_list, doctype, party_details): for d in records: details = party_details.get(d[0]) details.setdefault(frappe.scrub(doctype), []).append(d[1:]) - return party_details From 9fde7824035580a3d8bd0b48a2e1a3d2ffc46082 Mon Sep 17 00:00:00 2001 From: David Arnold Date: Sun, 12 Nov 2023 13:21:18 +0100 Subject: [PATCH 123/130] fix(customer): contact creation for companies (#38055) --- erpnext/selling/doctype/customer/customer.py | 37 ++++++++++++++------ 1 file changed, 26 insertions(+), 11 deletions(-) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index a4bb9d5381..f298becd06 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -309,13 +309,14 @@ class Customer(TransactionBase): def create_contact(contact, party_type, party, email): """Create contact based on given contact name""" - contact = contact.split(" ") + names = contact.split(" ") contact = frappe.get_doc( { "doctype": "Contact", - "first_name": contact[0], - "last_name": len(contact) > 1 and contact[1] or "", + "first_name": names[0], + "middle_name": len(names) > 2 and " ".join(names[1:-1]) or "", + "last_name": len(names) > 1 and names[-1] or "", "is_primary_contact": 1, } ) @@ -640,14 +641,28 @@ def get_credit_limit(customer, company): def make_contact(args, is_primary_contact=1): - contact = frappe.get_doc( - { - "doctype": "Contact", - "first_name": args.get("customer_name"), - "is_primary_contact": is_primary_contact, - "links": [{"link_doctype": args.get("doctype"), "link_name": args.get("name")}], - } - ) + values = { + "doctype": "Contact", + "is_primary_contact": is_primary_contact, + "links": [{"link_doctype": args.get("doctype"), "link_name": args.get("name")}], + } + if args.customer_type == "Individual": + names = args.get("customer_name").split(" ") + values.update( + { + "first_name": names[0], + "middle_name": len(names) > 2 and " ".join(names[1:-1]) or "", + "last_name": len(names) > 1 and names[-1] or "", + } + ) + else: + values.update( + { + "company_name": args.get("customer_name"), + } + ) + contact = frappe.get_doc(values) + if args.get("email_id"): contact.add_email(args.get("email_id"), is_primary=True) if args.get("mobile_no"): From 94faa44697144157940a874090fd1bd02c1eb981 Mon Sep 17 00:00:00 2001 From: Arjun Date: Sun, 12 Nov 2023 18:02:13 +0530 Subject: [PATCH 124/130] fix: close `Credit Limit Crossed` dialog (#38052) --- erpnext/selling/doctype/customer/customer.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index f298becd06..fee55b440f 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -501,6 +501,7 @@ def check_credit_limit(customer, company, ignore_outstanding_sales_order=False, primary_action={ "label": "Send Email", "server_action": "erpnext.selling.doctype.customer.customer.send_emails", + "hide_on_success": True, "args": { "customer": customer, "customer_outstanding": customer_outstanding, From 6f432b8e454b56c00ef444924b5253c0ca1af988 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Sun, 12 Nov 2023 14:48:18 +0100 Subject: [PATCH 125/130] refactor: parse full name --- erpnext/selling/doctype/customer/customer.py | 35 ++++++++++++-------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index fee55b440f..526b0dc70a 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -309,20 +309,19 @@ class Customer(TransactionBase): def create_contact(contact, party_type, party, email): """Create contact based on given contact name""" - names = contact.split(" ") - - contact = frappe.get_doc( + first, middle, last = parse_full_name(contact) + doc = frappe.get_doc( { "doctype": "Contact", - "first_name": names[0], - "middle_name": len(names) > 2 and " ".join(names[1:-1]) or "", - "last_name": len(names) > 1 and names[-1] or "", + "first_name": first, + "middle_name": middle, + "last_name": last, "is_primary_contact": 1, } ) - contact.append("email_ids", dict(email_id=email, is_primary=1)) - contact.append("links", dict(link_doctype=party_type, link_name=party)) - contact.insert() + doc.append("email_ids", dict(email_id=email, is_primary=1)) + doc.append("links", dict(link_doctype=party_type, link_name=party)) + return doc.insert() @frappe.whitelist() @@ -648,12 +647,12 @@ def make_contact(args, is_primary_contact=1): "links": [{"link_doctype": args.get("doctype"), "link_name": args.get("name")}], } if args.customer_type == "Individual": - names = args.get("customer_name").split(" ") + first, middle, last = parse_full_name(args.get("customer_name")) values.update( { - "first_name": names[0], - "middle_name": len(names) > 2 and " ".join(names[1:-1]) or "", - "last_name": len(names) > 1 and names[-1] or "", + "first_name": first, + "middle_name": middle, + "last_name": last, } ) else: @@ -730,3 +729,13 @@ def get_customer_primary_contact(doctype, txt, searchfield, start, page_len, fil .where((dlink.link_name == customer) & (con.name.like(f"%{txt}%"))) .run() ) + + +def parse_full_name(full_name: str) -> tuple[str, str | None, str | None]: + """Parse full name into first name, middle name and last name""" + names = full_name.split() + first_name = names[0] + middle_name = " ".join(names[1:-1]) if len(names) > 2 else None + last_name = names[-1] if len(names) > 1 else None + + return first_name, middle_name, last_name From d17e37c581732bfc78fb9b5f39bc354fa8667977 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Sun, 12 Nov 2023 14:51:40 +0100 Subject: [PATCH 126/130] test: parse full name --- .../selling/doctype/customer/test_customer.py | 22 ++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/erpnext/selling/doctype/customer/test_customer.py b/erpnext/selling/doctype/customer/test_customer.py index 6e737e4b55..29dbd4f321 100644 --- a/erpnext/selling/doctype/customer/test_customer.py +++ b/erpnext/selling/doctype/customer/test_customer.py @@ -10,7 +10,11 @@ from frappe.utils import flt from erpnext.accounts.party import get_due_date from erpnext.exceptions import PartyDisabled, PartyFrozen -from erpnext.selling.doctype.customer.customer import get_credit_limit, get_customer_outstanding +from erpnext.selling.doctype.customer.customer import ( + get_credit_limit, + get_customer_outstanding, + parse_full_name, +) from erpnext.tests.utils import create_test_contact_and_address test_ignore = ["Price List"] @@ -373,6 +377,22 @@ class TestCustomer(FrappeTestCase): frappe.db.set_single_value("Selling Settings", "cust_master_name", "Customer Name") + def test_parse_full_name(self): + first, middle, last = parse_full_name("John") + self.assertEqual(first, "John") + self.assertEqual(middle, None) + self.assertEqual(last, None) + + first, middle, last = parse_full_name("John Doe") + self.assertEqual(first, "John") + self.assertEqual(middle, None) + self.assertEqual(last, "Doe") + + first, middle, last = parse_full_name("John Michael Doe") + self.assertEqual(first, "John") + self.assertEqual(middle, "Michael") + self.assertEqual(last, "Doe") + def get_customer_dict(customer_name): return { From d380bf817916b930254b64847ba66b6066e65cb3 Mon Sep 17 00:00:00 2001 From: barredterra <14891507+barredterra@users.noreply.github.com> Date: Sun, 12 Nov 2023 20:04:38 +0100 Subject: [PATCH 127/130] feat: remove unused method create_contact --- erpnext/selling/doctype/customer/customer.py | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py index 526b0dc70a..459fc9fcff 100644 --- a/erpnext/selling/doctype/customer/customer.py +++ b/erpnext/selling/doctype/customer/customer.py @@ -307,23 +307,6 @@ class Customer(TransactionBase): ) -def create_contact(contact, party_type, party, email): - """Create contact based on given contact name""" - first, middle, last = parse_full_name(contact) - doc = frappe.get_doc( - { - "doctype": "Contact", - "first_name": first, - "middle_name": middle, - "last_name": last, - "is_primary_contact": 1, - } - ) - doc.append("email_ids", dict(email_id=email, is_primary=1)) - doc.append("links", dict(link_doctype=party_type, link_name=party)) - return doc.insert() - - @frappe.whitelist() def make_quotation(source_name, target_doc=None): def set_missing_values(source, target): From 95edd82638c5cb5aabefd871966d558f51095f94 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 13 Nov 2023 11:56:43 +0530 Subject: [PATCH 128/130] refactor: add revaluation journal checkbox in AR/AP summary reports --- .../accounts_payable_summary/accounts_payable_summary.js | 5 +++++ .../accounts_receivable_summary.js | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js index 9e575e669d..0f206b1cf4 100644 --- a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js +++ b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js @@ -110,6 +110,11 @@ frappe.query_reports["Accounts Payable Summary"] = { "fieldname":"based_on_payment_terms", "label": __("Based On Payment Terms"), "fieldtype": "Check", + }, + { + "fieldname": "for_revaluation_journals", + "label": __("Revaluation Journals"), + "fieldtype": "Check", } ], diff --git a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js index 5ad10c7890..2f6d2582b3 100644 --- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js +++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js @@ -139,6 +139,11 @@ frappe.query_reports["Accounts Receivable Summary"] = { "label": __("Show GL Balance"), "fieldtype": "Check", }, + { + "fieldname": "for_revaluation_journals", + "label": __("Revaluation Journals"), + "fieldtype": "Check", + } ], onload: function(report) { From 4a111f73620136cda022e030c58d7d58b0c22a8e Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Mon, 13 Nov 2023 13:26:55 +0530 Subject: [PATCH 129/130] fix: indentation issue in the Production Plan Summary report (#38019) fix: Production Plan Summary report --- .../production_plan/production_plan.js | 4 -- .../production_plan/production_plan.py | 2 - .../production_plan/test_production_plan.py | 43 ------------------- .../production_plan_summary.js | 4 +- .../production_plan_summary.py | 26 ++++++++--- 5 files changed, 22 insertions(+), 57 deletions(-) diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js index 72438ddcee..dd102b0fae 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.js +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js @@ -89,10 +89,6 @@ frappe.ui.form.on('Production Plan', { frm.trigger("show_progress"); if (frm.doc.status !== "Completed") { - frm.add_custom_button(__("Work Order Tree"), ()=> { - frappe.set_route('Tree', 'Work Order', {production_plan: frm.doc.name}); - }, __('View')); - frm.add_custom_button(__("Production Plan Summary"), ()=> { frappe.set_route('query-report', 'Production Plan Summary', {production_plan: frm.doc.name}); }, __('View')); diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 6b12a29b50..6efb762905 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -828,8 +828,6 @@ class ProductionPlan(Document): # Combine subassembly items sub_assembly_items_store = self.combine_subassembly_items(sub_assembly_items_store) - sub_assembly_items_store.sort(key=lambda d: d.bom_level, reverse=True) # sort by bom level - for idx, row in enumerate(sub_assembly_items_store): row.idx = idx + 1 self.append("sub_assembly_items", row) diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py index e9c6ee3af2..dd32c34358 100644 --- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py @@ -664,49 +664,6 @@ class TestProductionPlan(FrappeTestCase): frappe.db.rollback() - def test_subassmebly_sorting(self): - "Test subassembly sorting in case of multiple items with nested BOMs." - from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom - - prefix = "_TestLevel_" - boms = { - "Assembly": { - "SubAssembly1": { - "ChildPart1": {}, - "ChildPart2": {}, - }, - "ChildPart6": {}, - "SubAssembly4": {"SubSubAssy2": {"ChildPart7": {}}}, - }, - "MegaDeepAssy": { - "SecretSubassy": { - "SecretPart": {"VerySecret": {"SuperSecret": {"Classified": {}}}}, - }, - # ^ assert that this is - # first item in subassy table - }, - } - create_nested_bom(boms, prefix=prefix) - - items = [prefix + item_code for item_code in boms.keys()] - plan = create_production_plan(item_code=items[0], do_not_save=True) - plan.append( - "po_items", - { - "use_multi_level_bom": 1, - "item_code": items[1], - "bom_no": frappe.db.get_value("Item", items[1], "default_bom"), - "planned_qty": 1, - "planned_start_date": now_datetime(), - }, - ) - plan.get_sub_assembly_items() - - bom_level_order = [d.bom_level for d in plan.sub_assembly_items] - self.assertEqual(bom_level_order, sorted(bom_level_order, reverse=True)) - # lowest most level of subassembly should be first - self.assertIn("SuperSecret", plan.sub_assembly_items[0].production_item) - def test_multiple_work_order_for_production_plan_item(self): "Test producing Prod Plan (making WO) in parts." diff --git a/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.js b/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.js index 521543ab1b..afe4a6e0cb 100644 --- a/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.js +++ b/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.js @@ -22,9 +22,9 @@ frappe.query_reports["Production Plan Summary"] = { "formatter": function(value, row, column, data, default_formatter) { value = default_formatter(value, row, column, data); - if (column.fieldname == "document_name") { + if (column.fieldname == "item_code") { var color = data.pending_qty > 0 ? 'red': 'green'; - value = `${data['document_name']}`; + value = `${data['item_code']}`; } return value; diff --git a/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.py b/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.py index 2c8f82f2cc..076690ff09 100644 --- a/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.py +++ b/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.py @@ -44,6 +44,7 @@ def get_production_plan_item_details(filters, data, order_details): { "indent": 0, "item_code": row.item_code, + "sales_order": row.get("sales_order"), "item_name": frappe.get_cached_value("Item", row.item_code, "item_name"), "qty": row.planned_qty, "document_type": "Work Order", @@ -80,7 +81,7 @@ def get_production_plan_sub_assembly_item_details( data.append( { - "indent": 1, + "indent": 1 + item.indent, "item_code": item.production_item, "item_name": item.item_name, "qty": item.qty, @@ -98,7 +99,7 @@ def get_work_order_details(filters, order_details): for row in frappe.get_all( "Work Order", filters={"production_plan": filters.get("production_plan")}, - fields=["name", "produced_qty", "production_plan", "production_item"], + fields=["name", "produced_qty", "production_plan", "production_item", "sales_order"], ): order_details.setdefault((row.name, row.production_item), row) @@ -118,10 +119,17 @@ def get_column(filters): "label": _("Finished Good"), "fieldtype": "Link", "fieldname": "item_code", - "width": 300, + "width": 240, "options": "Item", }, - {"label": _("Item Name"), "fieldtype": "data", "fieldname": "item_name", "width": 100}, + {"label": _("Item Name"), "fieldtype": "data", "fieldname": "item_name", "width": 150}, + { + "label": _("Sales Order"), + "options": "Sales Order", + "fieldtype": "Link", + "fieldname": "sales_order", + "width": 100, + }, { "label": _("Document Type"), "fieldtype": "Link", @@ -133,10 +141,16 @@ def get_column(filters): "label": _("Document Name"), "fieldtype": "Dynamic Link", "fieldname": "document_name", - "width": 150, + "options": "document_type", + "width": 180, }, {"label": _("BOM Level"), "fieldtype": "Int", "fieldname": "bom_level", "width": 100}, {"label": _("Order Qty"), "fieldtype": "Float", "fieldname": "qty", "width": 120}, - {"label": _("Received Qty"), "fieldtype": "Float", "fieldname": "produced_qty", "width": 160}, + { + "label": _("Produced / Received Qty"), + "fieldtype": "Float", + "fieldname": "produced_qty", + "width": 200, + }, {"label": _("Pending Qty"), "fieldtype": "Float", "fieldname": "pending_qty", "width": 110}, ] From ea7565889fb9a833b3bcd9f36abca020285e17cc Mon Sep 17 00:00:00 2001 From: David Arnold Date: Mon, 13 Nov 2023 09:49:22 +0100 Subject: [PATCH 130/130] feat(customer): add button to quick-create sales order and quotation (#37320) * feat(customer): add button to quick-create sales order and quotation * fix: create buttons in customer * refactor: don't repeat yourself --------- Co-authored-by: barredterra <14891507+barredterra@users.noreply.github.com> --- erpnext/selling/doctype/customer/customer.js | 41 +++++++++++++------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/erpnext/selling/doctype/customer/customer.js b/erpnext/selling/doctype/customer/customer.js index 42932ad8bd..ddc7e2af8f 100644 --- a/erpnext/selling/doctype/customer/customer.js +++ b/erpnext/selling/doctype/customer/customer.js @@ -3,17 +3,32 @@ frappe.ui.form.on("Customer", { setup: function(frm) { - + frm.custom_make_buttons = { + "Opportunity": "Opportunity", + "Quotation": "Quotation", + "Sales Order": "Sales Order", + "Pricing Rule": "Pricing Rule", + }; frm.make_methods = { - 'Quotation': () => frappe.model.open_mapped_doc({ - method: "erpnext.selling.doctype.customer.customer.make_quotation", - frm: cur_frm - }), - 'Opportunity': () => frappe.model.open_mapped_doc({ - method: "erpnext.selling.doctype.customer.customer.make_opportunity", - frm: cur_frm - }) - } + "Quotation": () => + frappe.model.open_mapped_doc({ + method: "erpnext.selling.doctype.customer.customer.make_quotation", + frm: frm, + }), + "Sales Order": () => + frappe.model.with_doctype("Sales Order", function () { + var so = frappe.model.get_new_doc("Sales Order"); + so.customer = frm.doc.name; // Set the current customer as the SO customer + frappe.set_route("Form", "Sales Order", so.name); + }), + "Opportunity": () => + frappe.model.open_mapped_doc({ + method: "erpnext.selling.doctype.customer.customer.make_opportunity", + frm: frm, + }), + "Pricing Rule": () => + erpnext.utils.make_pricing_rule(frm.doc.doctype, frm.doc.name), + }; frm.add_fetch('lead_name', 'company_name', 'customer_name'); frm.add_fetch('default_sales_partner','commission_rate','default_commission_rate'); @@ -146,9 +161,9 @@ frappe.ui.form.on("Customer", { {party_type: 'Customer', party: frm.doc.name, party_name: frm.doc.customer_name}); }, __('View')); - frm.add_custom_button(__('Pricing Rule'), function () { - erpnext.utils.make_pricing_rule(frm.doc.doctype, frm.doc.name); - }, __('Create')); + for (const doctype in frm.make_methods) { + frm.add_custom_button(__(doctype), frm.make_methods[doctype], __("Create")); + } frm.add_custom_button(__('Get Customer Group Details'), function () { frm.trigger("get_customer_group_details");