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 01/87] 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 02/87] 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 03/87] 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 04/87] 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 05/87] 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 06/87] 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 07/87] 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 08/87] 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 09/87] 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 10/87] 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 11/87] 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 12/87] 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 13/87] 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 8d9b90f3f53083e631444a0d39d86bda0b08f479 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Fri, 27 Oct 2023 12:37:37 +0530 Subject: [PATCH 14/87] refactor: ignore cancelled GLE's while looking for currency --- erpnext/accounts/party.py | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py index 310e41208f..16e73ea52f 100644 --- a/erpnext/accounts/party.py +++ b/erpnext/accounts/party.py @@ -5,7 +5,7 @@ from typing import Optional import frappe -from frappe import _, msgprint, scrub +from frappe import _, msgprint, qb, scrub from frappe.contacts.doctype.address.address import get_company_address, get_default_address from frappe.core.doctype.user_permission.user_permission import get_permitted_documents from frappe.model.utils import get_fetch_values @@ -480,11 +480,19 @@ def get_party_account_currency(party_type, party, company): def get_party_gle_currency(party_type, party, company): def generator(): - existing_gle_currency = frappe.db.sql( - """select account_currency from `tabGL Entry` - where docstatus=1 and company=%(company)s and party_type=%(party_type)s and party=%(party)s - limit 1""", - {"company": company, "party_type": party_type, "party": party}, + gl = qb.DocType("GL Entry") + existing_gle_currency = ( + qb.from_(gl) + .select(gl.account_currency) + .where( + (gl.docstatus == 1) + & (gl.company == company) + & (gl.party_type == party_type) + & (gl.party == party) + & (gl.is_cancelled == 0) + ) + .limit(1) + .run() ) return existing_gle_currency[0][0] if existing_gle_currency else None From f276fbba4f84979e12b8091492be7eddbf0caa56 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bernd=20Oliver=20S=C3=BCnderhauf?= <46800703+bosue@users.noreply.github.com> Date: Sat, 28 Oct 2023 02:10:28 +0200 Subject: [PATCH 15/87] refactor: remove extraneous disabled filters --- .../profitability_analysis/profitability_analysis.js | 7 ------- erpnext/assets/doctype/asset/asset.js | 1 - .../supplier_quotation_comparison.js | 5 ----- .../report/bom_operations_time/bom_operations_time.js | 2 +- erpnext/public/js/controllers/accounts.js | 1 - erpnext/stock/doctype/item_price/item_price.js | 1 - .../doctype/stock_reconciliation/stock_reconciliation.js | 7 ------- erpnext/support/doctype/issue/issue.js | 7 ------- 8 files changed, 1 insertion(+), 30 deletions(-) diff --git a/erpnext/accounts/report/profitability_analysis/profitability_analysis.js b/erpnext/accounts/report/profitability_analysis/profitability_analysis.js index 4a3d9bb479..b6bbd979ed 100644 --- a/erpnext/accounts/report/profitability_analysis/profitability_analysis.js +++ b/erpnext/accounts/report/profitability_analysis/profitability_analysis.js @@ -32,13 +32,6 @@ frappe.query_reports["Profitability Analysis"] = { "label": __("Accounting Dimension"), "fieldtype": "Link", "options": "Accounting Dimension", - "get_query": () =>{ - return { - filters: { - "disabled": 0 - } - } - } }, { "fieldname": "fiscal_year", diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js index f0e4c82048..d378fbd26a 100644 --- a/erpnext/assets/doctype/asset/asset.js +++ b/erpnext/assets/doctype/asset/asset.js @@ -9,7 +9,6 @@ frappe.ui.form.on('Asset', { frm.set_query("item_code", function() { return { "filters": { - "disabled": 0, "is_fixed_asset": 1, "is_stock_item": 0 } diff --git a/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js index fd73b870c5..579c0a65ad 100644 --- a/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js +++ b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.js @@ -44,11 +44,6 @@ frappe.query_reports["Supplier Quotation Comparison"] = { } } } - else { - return { - filters: { "disabled": 0 } - } - } } }, { diff --git a/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.js b/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.js index 34edb9d538..8729775dc2 100644 --- a/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.js +++ b/erpnext/manufacturing/report/bom_operations_time/bom_operations_time.js @@ -12,7 +12,7 @@ frappe.query_reports["BOM Operations Time"] = { "options": "Item", "get_query": () =>{ return { - filters: { "disabled": 0, "is_stock_item": 1 } + filters: { "is_stock_item": 1 } } } }, diff --git a/erpnext/public/js/controllers/accounts.js b/erpnext/public/js/controllers/accounts.js index 354552137b..7879173cd1 100644 --- a/erpnext/public/js/controllers/accounts.js +++ b/erpnext/public/js/controllers/accounts.js @@ -30,7 +30,6 @@ erpnext.accounts.taxes = { filters: { "account_type": account_type, "company": doc.company, - "disabled": 0 } } }); diff --git a/erpnext/stock/doctype/item_price/item_price.js b/erpnext/stock/doctype/item_price/item_price.js index ce489ff52b..8a4b4eef0a 100644 --- a/erpnext/stock/doctype/item_price/item_price.js +++ b/erpnext/stock/doctype/item_price/item_price.js @@ -6,7 +6,6 @@ frappe.ui.form.on("Item Price", { frm.set_query("item_code", function() { return { filters: { - "disabled": 0, "has_variants": 0 } }; diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js index 5452692a24..b3998b7c7e 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js @@ -123,13 +123,6 @@ frappe.ui.form.on("Stock Reconciliation", { fieldname: "item_code", fieldtype: "Link", options: "Item", - "get_query": function() { - return { - "filters": { - "disabled": 0, - } - }; - } }, { label: __("Ignore Empty Stock"), diff --git a/erpnext/support/doctype/issue/issue.js b/erpnext/support/doctype/issue/issue.js index d4daacd4ea..f96823b290 100644 --- a/erpnext/support/doctype/issue/issue.js +++ b/erpnext/support/doctype/issue/issue.js @@ -1,13 +1,6 @@ frappe.ui.form.on("Issue", { onload: function(frm) { frm.email_field = "raised_by"; - frm.set_query("customer", function () { - return { - filters: { - "disabled": 0 - } - }; - }); frappe.db.get_value("Support Settings", {name: "Support Settings"}, ["allow_resetting_service_level_agreement", "track_service_level_agreement"], (r) => { 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 16/87] 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 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 17/87] 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 18/87] 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 500435b856a028bdab7fdbe12647ec0f11287eab Mon Sep 17 00:00:00 2001 From: Didiman1998 <118364772+Didiman1998@users.noreply.github.com> Date: Mon, 30 Oct 2023 10:48:41 +0100 Subject: [PATCH 19/87] fix: make changes that enable gantt view for job cards (#37661) * fix: make changes that enable gantt view for job cards * fix: add fields on listview and remove from json file * fix: undo modified date --------- Co-authored-by: Dietmar Fischer --- erpnext/manufacturing/doctype/job_card/job_card_calendar.js | 4 ++-- erpnext/manufacturing/doctype/job_card/job_card_list.js | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/manufacturing/doctype/job_card/job_card_calendar.js b/erpnext/manufacturing/doctype/job_card/job_card_calendar.js index f4877fdca0..9e32085351 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card_calendar.js +++ b/erpnext/manufacturing/doctype/job_card/job_card_calendar.js @@ -10,8 +10,8 @@ frappe.views.calendar["Job Card"] = { }, gantt: { field_map: { - "start": "started_time", - "end": "started_time", + "start": "expected_start_date", + "end": "expected_end_date", "id": "name", "title": "subject", "color": "color", diff --git a/erpnext/manufacturing/doctype/job_card/job_card_list.js b/erpnext/manufacturing/doctype/job_card/job_card_list.js index 5d883bf9fa..99fca9570f 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card_list.js +++ b/erpnext/manufacturing/doctype/job_card/job_card_list.js @@ -1,6 +1,6 @@ frappe.listview_settings['Job Card'] = { has_indicator_for_draft: true, - + add_fields: ["expected_start_date", "expected_end_date"], get_indicator: function(doc) { const status_colors = { "Work In Progress": "orange", From afc64ed9eedb1aca2802f5d5e53cc5668359f575 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Mon, 30 Oct 2023 15:39:11 +0530 Subject: [PATCH 20/87] fix: ignore permissions while mapping DN Item --- erpnext/selling/doctype/sales_order/sales_order.py | 1 + 1 file changed, 1 insertion(+) diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 94f9d6e37c..2f6578ea16 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -831,6 +831,7 @@ def make_delivery_note(source_name, target_doc=None, kwargs=None): "postprocess": update_dn_item, } }, + ignore_permissions=True, ) dn_item.qty = flt(sre.reserved_qty) * flt(dn_item.get("conversion_factor", 1)) From ca698452382eba85bd940dfb6344ff19d1eab4e4 Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Mon, 30 Oct 2023 16:15:05 +0530 Subject: [PATCH 21/87] chore: add index to posting_date in PLE --- .../doctype/payment_ledger_entry/payment_ledger_entry.json | 5 +++-- 1 file changed, 3 insertions(+), 2 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 9cf2ac6c2a..4ae813571a 100644 --- a/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json +++ b/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json @@ -30,7 +30,8 @@ { "fieldname": "posting_date", "fieldtype": "Date", - "label": "Posting Date" + "label": "Posting Date", + "search_index": 1 }, { "fieldname": "account_type", @@ -153,7 +154,7 @@ "in_create": 1, "index_web_pages_for_search": 1, "links": [], - "modified": "2023-06-29 12:24:20.500632", + "modified": "2023-10-30 16:15:00.470283", "modified_by": "Administrator", "module": "Accounts", "name": "Payment Ledger Entry", From 056b74b162ed58dd979cd9748129752fb8cab242 Mon Sep 17 00:00:00 2001 From: rohitwaghchaure Date: Tue, 31 Oct 2023 12:32:52 +0530 Subject: [PATCH 22/87] 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 23/87] 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 24/87] 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 25/87] 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 26/87] 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 27/87] 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 28/87] 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 29/87] 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 30/87] 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 31/87] 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 32/87] 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 33/87] 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 34/87] 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 35/87] 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 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 36/87] 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 8fa677b8e8c6e4e65193ad0be7c0a6d5b4f1769e Mon Sep 17 00:00:00 2001 From: ruthra kumar Date: Wed, 1 Nov 2023 20:30:16 +0530 Subject: [PATCH 37/87] 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 38/87] 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 39/87] 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 40/87] 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 41/87] 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 42/87] 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 43/87] 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 44/87] 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 45/87] 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 c37e374fdd322c0f0423469ce95faecac15b18e4 Mon Sep 17 00:00:00 2001 From: Deepesh Garg Date: Thu, 2 Nov 2023 21:00:45 +0530 Subject: [PATCH 46/87] 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 47/87] 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 48/87] 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 469ae2c7f1edb6dbd2d770b65c48bad5a0293ffe Mon Sep 17 00:00:00 2001 From: Ankush Menat Date: Fri, 3 Nov 2023 15:57:52 +0530 Subject: [PATCH 49/87] 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 50/87] 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 51/87] 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 52/87] 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 53/87] 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 54/87] 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 55/87] 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 56/87] 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 57/87] 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 58/87] 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 59/87] 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 60/87] 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 61/87] 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 62/87] 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 63/87] 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 64/87] 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 65/87] 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 66/87] 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 67/87] 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 68/87] 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 69/87] 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 a9d91189b0e23ccb860e9f954e882ee0ebebbc2c Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Mon, 6 Nov 2023 11:05:04 +0530 Subject: [PATCH 70/87] 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 71/87] 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 5cce522ecdb7f13a001d4abbf6c3682088edb1b6 Mon Sep 17 00:00:00 2001 From: s-aga-r Date: Mon, 6 Nov 2023 17:13:29 +0530 Subject: [PATCH 72/87] 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 73/87] 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 74/87] 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 75/87] 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 76/87] 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 77/87] 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 78/87] 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 79/87] 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 80/87] 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 81/87] 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 82/87] 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 83/87] 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 84/87] 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 85/87] 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 86/87] 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 87/87] 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