Merge pull request #32557 from ruthra-kumar/value_error_on_pos_save
fix: Value error on validation of POS invoices with Serial Nos
diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/unverified/at_austria_chart_template.json b/erpnext/accounts/doctype/account/chart_of_accounts/unverified/at_austria_chart_template.json
index 3b07d51..58d67be 100644
--- a/erpnext/accounts/doctype/account/chart_of_accounts/unverified/at_austria_chart_template.json
+++ b/erpnext/accounts/doctype/account/chart_of_accounts/unverified/at_austria_chart_template.json
@@ -2,397 +2,438 @@
"country_code": "at",
"name": "Austria - Chart of Accounts",
"tree": {
- "Summe Abschreibungen und Aufwendungen": {
- "7010 bis 7080 Abschreibungen auf das Anlageverm\u00f6gen (ausgenommen Finanzanlagen)": {},
- "7100 bis 7190 Sonstige Steuern": {
- "account_type": "Tax"
- },
- "7200 bis 7290 Instandhaltung u. Reinigung durh Dritte, Entsorgung, Beleuchtung": {},
- "7300 bis 7310 Transporte durch Dritte": {},
- "7320 bis 7330 Kfz - Aufwand": {},
- "7340 bis 7350 Reise- und Fahraufwand": {},
- "7360 bis 7370 Tag- und N\u00e4chtigungsgelder": {},
- "7380 bis 7390 Nachrichtenaufwand": {},
- "7400 bis 7430 Miet- und Pachtaufwand": {},
- "7440 bis 7470 Leasingaufwand": {},
- "7480 bis 7490 Lizenzaufwand": {},
- "7500 bis 7530 Aufwand f\u00fcr beigestelltes Personal": {},
- "7540 bis 7570 Provisionen an Dritte": {},
- "7580 bis 7590 Aufsichtsratsverg\u00fctungen": {},
- "7610 bis 7620 Druckerzeugnisse und Vervielf\u00e4ltigungen": {},
- "7650 bis 7680 Werbung und Repr\u00e4sentationen": {},
- "7700 bis 7740 Versicherungen": {},
- "7750 bis 7760 Beratungs- und Pr\u00fcfungsaufwand": {},
- "7800 bis 7810 Schadensf\u00e4lle": {},
- "7840 bis 7880 Verschiedene betriebliche Aufwendungen": {},
- "7910 bis 7950 Aufwandsstellenrechung der Hersteller": {},
- "Abschreibungen auf aktivierte Aufwendungen f\u00fcr das Ingangs. u. Erweitern des Betriebes": {},
- "Abschreibungen vom Umlaufverm\u00f6gen, soweit diese die im Unternehmen \u00fcblichen Abschreibungen \u00fcbersteigen": {},
- "Aufwandsstellenrechnung": {},
- "Aus- und Fortbildung": {},
- "Buchwert abgegangener Anlagen, ausgenommen Finanzanlagen": {},
- "B\u00fcromaterial und Drucksorten": {},
- "Fachliteratur und Zeitungen ": {},
- "Herstellungskosten der zur Erzielung der Umsatzerl\u00f6se erbrachten Leistungen": {},
- "Mitgliedsbeitr\u00e4ge": {},
- "Skontoertr\u00e4ge auf sonstige betriebliche Aufwendungen": {},
- "Sonstige betrieblichen Aufwendungen": {},
- "Spenden und Trinkgelder": {},
- "Spesen des Geldverkehrs": {},
- "Verluste aus dem Abgang vom Anlageverm\u00f6gen, ausgenommen Finanzanlagen": {},
- "Vertriebskosten": {},
- "Verwaltungskosten": {},
- "root_type": "Expense"
- },
- "Summe Betriebliche Ertr\u00e4ge": {
- "4400 bis 4490 Erl\u00f6sschm\u00e4lerungen": {},
- "4500 bis 4570 Ver\u00e4nderungen des Bestandes an fertigen und unfertigen Erzeugn. sowie an noch nicht abrechenbaren Leistungen": {},
- "4580 bis 4590 andere aktivierte Eigenleistungen": {},
- "4600 bis 4620 Erl\u00f6se aus dem Abgang vom Anlageverm\u00f6gen, ausgen. Finanzanlagen": {},
- "4630 bis 4650 Ertr\u00e4ge aus dem Abgang vom Anlageverm\u00f6gen, ausgen. Finanzanlagen": {},
- "4660 bis 4670 Ertr\u00e4ge aus der Zuschreibung zum Anlageverm\u00f6gen, ausgen. Finanzanlagen": {},
- "4700 bis 4790 Ertr\u00e4ge aus der Aufl\u00f6sung von R\u00fcckstellungen": {},
- "4800 bis 4990 \u00dcbrige betriebliche Ertr\u00e4ge": {},
- "Erl\u00f6se 0 % Ausfuhrlieferungen/Drittl\u00e4nder": {},
- "Erl\u00f6se 10 %": {},
- "Erl\u00f6se 20 %": {},
- "Erl\u00f6se aus im Inland stpfl. EG Lieferungen 10 % USt": {},
- "Erl\u00f6se aus im Inland stpfl. EG Lieferungen 20 % USt": {},
- "Erl\u00f6se i.g. Lieferungen (stfr)": {},
- "root_type": "Income"
- },
- "Summe Eigenkapital R\u00fccklagen Abschlusskonten": {
- "9000 bis 9180 Gezeichnetes bzw. gewidmetes Kapital": {
- "account_type": "Equity"
- },
- "9200 bis 9290 Kapitalr\u00fccklagen": {
- "account_type": "Equity"
- },
- "9300 bis 9380 Gewinnr\u00fccklagen": {
- "account_type": "Equity"
- },
- "9400 bis 9590 Bewertungsreserven uns sonst. unversteuerte R\u00fccklagen": {
- "account_type": "Equity"
- },
- "9600 bis 9690 Privat und Verrechnungskonten bei Einzelunternehmen und Personengesellschaften": {},
- "9700 bis 9790 Einlagen stiller Gesellschafter ": {},
- "9900 bis 9999 Evidenzkonten": {},
- "Bilanzgewinn (-verlust )": {
- "account_type": "Equity"
- },
- "Er\u00f6ffnungsbilanz": {},
- "Gewinn- und Verlustrechnung": {},
- "Schlussbilanz": {},
- "nicht eingeforderte ausstehende Einlagen": {
- "account_type": "Equity"
- },
- "root_type": "Equity"
- },
- "Summe Finanzertr\u00e4ge und Aufwendungen": {
- "8000 bis 8040 Ertr\u00e4ge aus Beteiligungen": {},
- "8050 bis 8090 Ertr\u00e4ge aus anderen Wertpapieren und Ausleihungen des Finanzanlageverm\u00f6gens": {},
- "8100 bis 8130 Sonstige Zinsen und \u00e4hnliche Ertr\u00e4ge": {},
- "8220 bis 8250 Aufwendungen aus Beteiligungen": {},
- "8260 bis 8270 Aufwendungen aus sonst. Fiananzanlagen und aus Wertpapieren des Umlaufverm\u00f6gens": {},
- "8280 bis 8340 Zinsen und \u00e4hnliche Aufwendungem": {},
- "8400 bis 8440 Au\u00dferordentliche Ertr\u00e4ge": {},
- "8450 bis 8490 Au\u00dferordentliche Aufwendungen": {},
- "8500 bis 8590 Steuern vom Einkommen und vom Ertrag": {
- "account_type": "Tax"
- },
- "8600 bis 8690 Aufl\u00f6sung unversteuerten R\u00fccklagen": {},
- "8700 bis 8740 Aufl\u00f6sung von Kapitalr\u00fccklagen": {},
- "8750 bis 8790 Aufl\u00f6sung von Gewinnr\u00fccklagen": {},
- "8800 bis 8890 Zuweisung von unversteuerten R\u00fccklagen": {},
- "Buchwert abgegangener Beteiligungen": {},
- "Buchwert abgegangener Wertpapiere des Umlaufverm\u00f6gens": {},
- "Buchwert abgegangener sonstiger Finanzanlagen": {},
- "Erl\u00f6se aus dem Abgang von Beteiligungen": {},
- "Erl\u00f6se aus dem Abgang von Wertpapieren des Umlaufverm\u00f6gens": {},
- "Erl\u00f6se aus dem Abgang von sonstigen Finanzanlagen": {},
- "Ertr\u00e4ge aus dem Abgang von und der Zuschreibung zu Finanzanlagen": {},
- "Ertr\u00e4ge aus dem Abgang von und der Zuschreibung zu Wertpapieren des Umlaufverm\u00f6gens": {},
- "Gewinabfuhr bzw. Verlust\u00fcberrechnung aus Ergebnisabf\u00fchrungsvertr\u00e4gen": {},
- "nicht ausgenutzte Lieferantenskonti": {},
- "root_type": "Income"
- },
- "Summe Fremdkapital": {
- "3020 bis 3030 Steuerr\u00fcckstellungen": {},
- "3040 bis 3090 Sonstige R\u00fcckstellungen": {},
- "3110 bis 3170 Verbindlichkeiten gegen\u00fcber Kredidinstituten": {},
- "3180 bis 3190 Verbindlichkeiten gegen\u00fcber Finanzinstituten": {},
- "3380 bis 3390 Verbindlichkeiten aus der Annahme gezogener Wechsel u. d. Ausstellungen eigener Wechsel": {
+ "Klasse 0 Aktiva: Anlageverm\u00f6gen": {
+ "0100 Konzessionen ": {"account_type": "Fixed Asset"},
+ "0110 Patentrechte und Lizenzen ": {"account_type": "Fixed Asset"},
+ "0120 Datenverarbeitungsprogramme ": {"account_type": "Fixed Asset"},
+ "0130 Marken, Warenzeichen und Musterschutzrechte, sonstige Urheberrechte ": {"account_type": "Fixed Asset"},
+ "0140 Pacht- und Mietrechte ": {"account_type": "Fixed Asset"},
+ "0150 Bezugs- und ähnliche Rechte ": {"account_type": "Fixed Asset"},
+ "0160 Geschäfts-/Firmenwert ": {"account_type": "Fixed Asset"},
+ "0170 Umgründungsmehrwert ": {"account_type": "Fixed Asset"},
+ "0180 Geleistete Anzahlungen auf immaterielle Vermögensgegenstände": {"account_type": "Fixed Asset"},
+ "0190 Kumulierte Abschreibungen zu immateriellen Vermögensgegenständen ": {"account_type": "Fixed Asset"},
+ "0200 Unbebaute Grundstücke, soweit nicht landwirtschaftlich genutzt ": {"account_type": "Fixed Asset"},
+ "0210 Bebaute Grundstücke (Grundwert) ": {"account_type": "Fixed Asset"},
+ "0220 Landwirtschaftlich genutzte Grundstücke ": {"account_type": "Fixed Asset"},
+ "0230 Grundstücksgleiche Rechte ": {"account_type": "Fixed Asset"},
+ "0300 Betriebs- und Geschäftsgebäude auf eigenem Grund ": {"account_type": "Fixed Asset"},
+ "0310 Wohn- und Sozialgebäude auf eigenem Grund ": {"account_type": "Fixed Asset"},
+ "0320 Betriebs- und Geschäftsgebäude auf fremdem Grund ": {"account_type": "Fixed Asset"},
+ "0330 Wohn- und Sozialgebäude auf fremdem Grund ": {"account_type": "Fixed Asset"},
+ "0340 Grundstückseinrichtungen auf eigenem Grund ": {"account_type": "Fixed Asset"},
+ "0350 Grundstückseinrichtungen auf fremdem Grund ": {"account_type": "Fixed Asset"},
+ "0360 Bauliche Investitionen in fremden (gepachteten) Betriebs- und Geschäftsgebäuden": {"account_type": "Fixed Asset"},
+ "0370 Bauliche Investitionen in fremden (gepachteten) Wohn- und Sozialgebäuden": {"account_type": "Fixed Asset"},
+ "0390 Kumulierte Abschreibungen zu Grundstücken ": {"account_type": "Fixed Asset"},
+ "0400 Maschinen und Geräte ": {"account_type": "Fixed Asset"},
+ "0500 Maschinenwerkzeuge ": {"account_type": "Fixed Asset"},
+ "0510 Allgemeine Werkzeuge und Handwerkzeuge ": {"account_type": "Fixed Asset"},
+ "0520 Prototypen, Formen, Modelle ": {"account_type": "Fixed Asset"},
+ "0530 Andere Erzeugungshilfsmittel (auch Softwarewerkzeuge)": {"account_type": "Fixed Asset"},
+ "0540 Hebezeuge und Montageanlagen ": {"account_type": "Fixed Asset"},
+ "0550 Geringwertige Vermögensgegenstände, soweit im Erzeugungsprozess ": {"account_type": "Fixed Asset"},
+ "0560 Festwerte technische Anlagen und Maschinen ": {"account_type": "Fixed Asset"},
+ "0590 Kumulierte Abschreibungen zu technischen Anlagen und Maschinen ": {"account_type": "Fixed Asset"},
+ "0600 Betriebs- und Geschäftsausstattung, soweit nicht gesondert angeführt ": {"account_type": "Fixed Asset"},
+ "0610 Andere Anlagen, soweit nicht gesondert angeführt ": {"account_type": "Fixed Asset"},
+ "0620 Büromaschinen, EDV-Anlagen ": {"account_type": "Fixed Asset"},
+ "0630 PKW und Kombis ": {"account_type": "Fixed Asset"},
+ "0640 LKW ": {"account_type": "Fixed Asset"},
+ "0650 Andere Beförderungsmittel ": {"account_type": "Fixed Asset"},
+ "0660 Gebinde ": {"account_type": "Fixed Asset"},
+ "0670 Geringwertige Vermögensgegenstände, soweit nicht im Erzeugungssprozess verwendet": {"account_type": "Fixed Asset"},
+ "0680 Festwerte außer technische Anlagen und Maschinen ": {"account_type": "Fixed Asset"},
+ "0690 Kumulierte Abschreibungen zu anderen Anlagen, Betriebs- und Geschäftsausstattung": {"account_type": "Fixed Asset"},
+ "0700 Geleistete Anzahlungen auf Sachanlagen ": {"account_type": "Fixed Asset"},
+ "0710 Anlagen in Bau ": {"account_type": "Fixed Asset"},
+ "0790 Kumulierte Abschreibungen zu geleisteten Anzahlungen auf Sachanlagen ": {"account_type": "Fixed Asset"},
+ "0800 Anteile an verbundenen Unternehmen ": {"account_type": "Fixed Asset"},
+ "0810 Beteiligungen an Gemeinschaftsunternehmen ": {"account_type": "Fixed Asset"},
+ "0820 Beteiligungen an angeschlossenen (assoziierten) Unternehmen ": {"account_type": "Fixed Asset"},
+ "0830 Eigene Anteile, Anteile an herrschenden oder mit Mehrheit beteiligten ": {"account_type": "Fixed Asset"},
+ "0840 Sonstige Beteiligungen ": {"account_type": "Fixed Asset"},
+ "0850 Ausleihungen an verbundene Unternehmen ": {"account_type": "Fixed Asset"},
+ "0860 Ausleihungen an Unternehmen mit Beteiligungsverhältnis": {"account_type": "Fixed Asset"},
+ "0870 Ausleihungen an Gesellschafter ": {"account_type": "Fixed Asset"},
+ "0880 Sonstige Ausleihungen ": {"account_type": "Fixed Asset"},
+ "0890 Anteile an Kapitalgesellschaften ohne Beteiligungscharakter ": {"account_type": "Fixed Asset"},
+ "0900 Anteile an Personengesellschaften ohne Beteiligungscharakter ": {"account_type": "Fixed Asset"},
+ "0910 Genossenschaftsanteile ohne Beteiligungscharakter ": {"account_type": "Fixed Asset"},
+ "0920 Anteile an Investmentfonds ": {"account_type": "Fixed Asset"},
+ "0930 Festverzinsliche Wertpapiere des Anlagevermögens ": {"account_type": "Fixed Asset"},
+ "0980 Geleistete Anzahlungen auf Finanzanlagen ": {"account_type": "Fixed Asset"},
+ "0990 Kumulierte Abschreibungen zu Finanzanlagen ": {"account_type": "Fixed Asset"},
+ "root_type": "Asset"
+ },
+ "Klasse 1 Aktiva: Vorr\u00e4te": {
+ "1000 Bezugsverrechnung": {"account_type": "Stock"},
+ "1100 Rohstoffe": {"account_type": "Stock"},
+ "1200 Bezogene Teile": {"account_type": "Stock"},
+ "1300 Hilfsstoffe": {"account_type": "Stock"},
+ "1350 Betriebsstoffe": {"account_type": "Stock"},
+ "1360 Vorrat Energietraeger": {"account_type": "Stock"},
+ "1400 Unfertige Erzeugnisse": {"account_type": "Stock"},
+ "1500 Fertige Erzeugnisse": {"account_type": "Stock"},
+ "1600 Handelswarenvorrat": {"account_type": "Stock Received But Not Billed"},
+ "1700 Noch nicht abrechenbare Leistungen": {"account_type": "Stock"},
+ "1900 Wertberichtigungen": {"account_type": "Stock"},
+ "1800 Geleistete Anzahlungen": {"account_type": "Stock"},
+ "1900 Wertberichtigungen": {"account_type": "Stock"},
+ "root_type": "Asset"
+ },
+ "Klasse 3 Passiva: Verbindlichkeiten": {
+ "3000 Allgemeine Verbindlichkeiten (Schuld)": {"account_type": "Payable"},
+ "3010 R\u00fcckstellungen f\u00fcr Pensionen": {"account_type": "Payable"},
+ "3020 Steuerr\u00fcckstellungen": {"account_type": "Tax"},
+ "3041 Sonstige R\u00fcckstellungen": {"account_type": "Payable"},
+ "3110 Verbindlichkeiten gegen\u00fcber Bank": {"account_type": "Payable"},
+ "3150 Verbindlichkeiten Darlehen": {"account_type": "Payable"},
+ "3185 Verbindlichkeiten Kreditkarte": {"account_type": "Payable"},
+ "3380 Verbindlichkeiten aus der Annahme gezogener Wechsel u. d. Ausstellungen eigener Wechsel": {
"account_type": "Payable"
},
- "3400 bis 3470 Verbindlichkeiten gegen\u00fc. verb. Untern., Verbindl. gegen\u00fc. Untern., mit denen eine Beteiligungsverh\u00e4lnis besteht": {},
- "3600 bis 3690 Verbindlichkeiten im Rahmen der sozialen Sicherheit": {},
- "3700 bis 3890 \u00dcbrige sonstige Verbindlichkeiten": {},
- "3900 bis 3990 Passive Rechnungsabgrenzungsposten": {},
- "Anleihen (einschlie\u00dflich konvertibler)": {},
- "Erhaltene Anzahlungenauf Bestellungen": {},
- "R\u00fcckstellungen f\u00fcr Abfertigung": {},
- "R\u00fcckstellungen f\u00fcr Pensionen": {},
- "USt. \u00a719 /art (reverse charge)": {
+ "3400 Verbindlichkeiten gegen\u00fc. verb. Untern., Verbindl. gegen\u00fc. Untern., mit denen eine Beteiligungsverh\u00e4lnis besteht": {},
+ "3460 Verbindlichkeiten gegenueber Gesellschaftern": {"account_type": "Payable"},
+ "3470 Einlagen stiller Gesellschafter": {"account_type": "Payable"},
+ "3585 Verbindlichkeiten Lohnsteuer": {"account_type": "Tax"},
+ "3590 Verbindlichkeiten Kommunalabgaben": {"account_type": "Tax"},
+ "3595 Verbindlichkeiten Dienstgeberbeitrag": {"account_type": "Tax"},
+ "3600 Verbindlichkeiten Sozialversicherung": {"account_type": "Payable"},
+ "3640 Verbindlichkeiten Loehne und Gehaelter": {"account_type": "Payable"},
+ "3700 Sonstige Verbindlichkeiten": {"account_type": "Payable"},
+ "3900 Passive Rechnungsabgrenzungsposten": {"account_type": "Payable"},
+ "3100 Anleihen (einschlie\u00dflich konvertibler)": {"account_type": "Payable"},
+ "3200 Erhaltene Anzahlungen auf Bestellungen": {"account_type": "Payable"},
+ "3040 R\u00fcckstellungen f\u00fcr Abfertigung": {"account_type": "Payable"},
+
+ "3530 USt. \u00a719 (reverse charge)": {
"account_type": "Tax"
},
- "Umsatzsteuer": {},
- "Umsatzsteuer Zahllast": {
+ "3500 Verbindlichkeiten aus Umsatzsteuer": {"account_type": "Tax"},
+ "3580 Umsatzsteuer Zahllast": {
"account_type": "Tax"
},
- "Umsatzsteuer aus i.g. Erwerb 10%": {
+ "3510 Umsatzsteuer Inland 20%": {
"account_type": "Tax"
},
- "Umsatzsteuer aus i.g. Erwerb 20%": {
+ "3515 Umsatzsteuer Inland 10%": {
+ "account_type": "Tax"
+ },
+ "3520 Umsatzsteuer aus i.g. Erwerb 20%": {
"account_type": "Tax"
},
- "Umsatzsteuer aus i.g. Lieferungen 10%": {
+ "3525 Umsatzsteuer aus i.g. Erwerb 10%": {
"account_type": "Tax"
- },
- "Umsatzsteuer aus i.g. Lieferungen 20%": {
- "account_type": "Tax"
- },
- "Umsatzsteuer-Evidenzkonto f\u00fcr erhaltene Anzahlungen auf Bestellungen": {},
- "Verbindlichkeiten aus Lieferungen u. Leistungen EU": {
+ },
+ "3560 Umsatzsteuer-Evidenzkonto f\u00fcr erhaltene Anzahlungen auf Bestellungen": {},
+ "3360 Verbindlichkeiten aus Lieferungen u. Leistungen EU": {
"account_type": "Payable"
},
- "Verbindlichkeiten aus Lieferungen u. Leistungen Inland": {
+ "3000 Verbindlichkeiten aus Lieferungen u. Leistungen Inland": {
"account_type": "Payable"
},
- "Verbindlichkeiten aus Lieferungen u. Leistungen sonst. Ausland": {
+ "3370 Verbindlichkeiten aus Lieferungen u. Leistungen sonst. Ausland": {
"account_type": "Payable"
},
- "Verbindlichkeiten gegen\u00fcber Gesellschaften": {},
- "Verrechnung Finanzamt": {
+ "3400 Verbindlichkeiten gegen\u00fcber verbundenen Unternehmen": {},
+ "3570 Verrechnung Finanzamt": {
"account_type": "Tax"
},
"root_type": "Liability"
- },
- "Summe Kontoklasse 0 Anlageverm\u00f6gen": {
- "44 bis 49 Sonstige Maschinen und maschinelle Anlagen": {},
- "920 bis 930 Festverzinsliche Wertpapiere des Anlageverm\u00f6gens": {},
- "940 bis 970 Sonstige Finanzanlagen, Wertrechte": {},
- "Allgemeine Werkzeuge und Handwerkzeuge": {},
- "Andere Bef\u00f6rderungsmittel": {},
- "Andere Betriebs- und Gesch\u00e4ftsausstattung": {},
- "Andere Erzeugungshilfsmittel": {},
- "Anlagen im Bau": {},
- "Anteile an Investmentfonds": {},
- "Anteile an Kapitalgesellschaften ohne Beteiligungscharakter": {},
- "Anteile an Personengesellschaften ohne Beteiligungscharakter": {},
- "Anteile an verbundenen Unternehmen": {},
- "Antriebsmaschinen": {},
- "Aufwendungen f\u00fcs das Ingangssetzen u. Erweitern eines Betriebes": {},
- "Ausleihungen an verbundene Unternehmen": {},
- "Ausleihungen an verbundene Unternehmen, mit denen ein Beteiligungsverh\u00e4lnis besteht": {},
- "Bauliche Investitionen in fremden (gepachteten) Betriebs- und Gesch\u00e4ftsgeb\u00e4uden": {},
- "Bauliche Investitionen in fremden (gepachteten) Wohn- und Sozialgeb\u00e4uden": {},
- "Bebaute Grundst\u00fccke (Grundwert)": {},
- "Beheizungs- und Beleuchtungsanlagen": {},
- "Beteiligungen an Gemeinschaftunternehmen": {},
- "Beteiligungen an angeschlossenen (assoziierten) Unternehmen": {},
- "Betriebs- und Gesch\u00e4ftsgeb\u00e4ude auf eigenem Grund": {},
- "Betriebs- und Gesch\u00e4ftsgeb\u00e4ude auf fremdem Grund": {},
- "B\u00fcromaschinen, EDV - Anlagen": {},
- "Datenverarbeitungsprogramme": {},
- "Energieversorgungsanlagen": {},
- "Fertigungsmaschinen": {},
- "Gebinde": {},
- "Geleistete Anzahlungen": {},
- "Genossenschaften ohne Beteiligungscharakter": {},
- "Geringwertige Verm\u00f6gensgegenst\u00e4nde, soweit im Erzeugerprozess verwendet": {},
- "Geringwertige Verm\u00f6gensgegenst\u00e4nde, soweit nicht im Erzeugungsprozess verwendet": {},
- "Gesch\u00e4fts(Firmen)wert": {},
- "Grundst\u00fcckseinrichtunten auf eigenem Grund": {},
- "Grundst\u00fcckseinrichtunten auf fremdem Grund": {},
- "Grundst\u00fccksgleiche Rechte": {},
- "Hebezeuge und Montageanlagen": {},
- "Konzessionen": {},
- "Kumulierte Abschreibungen": {},
- "LKW": {},
- "Marken, Warenzeichen und Musterschutzrechte": {},
- "Maschinenwerkzeuge": {},
- "Nachrichten- und Kontrollanlagen": {},
- "PKW": {},
- "Pacht- und Mietrechte": {},
- "Patentrechte und Lizenzen": {},
- "Sonstige Ausleihungen": {},
- "Sonstige Beteiligungen": {},
- "Transportanlagen": {},
- "Unbebaute Grundst\u00fccke": {},
- "Vorrichtungen, Formen und Modelle": {},
- "Wohn- und Sozialgeb\u00e4ude auf eigenem Grund": {},
- "Wohn- und Sozialgeb\u00e4ude auf fremdem Grund": {},
+ },
+ "Klasse 2 Aktiva: Umlaufverm\u00f6gen, Rechnungsabgrenzungen": {
+ "2030 Forderungen aus Lieferungen und Leistungen Inland (0% USt, umsatzsteuerfrei)": {
+ "account_type": "Receivable"
+ },
+ "2010 Forderungen aus Lieferungen und Leistungen Inland (10% USt, umsatzsteuerfrei)": {
+ "account_type": "Receivable"
+ },
+ "2000 Forderungen aus Lieferungen und Leistungen Inland (20% USt, umsatzsteuerfrei)": {
+ "account_type": "Receivable"
+ },
+ "2040 Forderungen aus Lieferungen und Leistungen Inland (sonstiger USt-Satz)": {
+ "account_type": "Receivable"
+ },
+ "2100 Forderungen aus Lieferungen und Leistungen EU": {
+ "account_type": "Receivable"
+ },
+ "2150 Forderungen aus Lieferungen und Leistungen Ausland (Nicht-EU)": {
+ "account_type": "Receivable"
+ },
+ "2200 Forderungen gegen\u00fcber verbundenen Unternehmen": {
+ "account_type": "Receivable"
+ },
+ "2250 Forderungen gegen\u00fcber Unternehmen, mit denen ein Beteiligungsverh\u00e4ltnis besteht": {
+ "account_type": "Receivable"
+ },
+ "2300 Sonstige Forderungen und Verm\u00f6gensgegenst\u00e4nde": {
+ "account_type": "Receivable"
+ },
+ "2630 Sonstige Wertpapiere": {
+ "account_type": "Stock"
+ },
+ "2750 Kassenbest\u00e4nde in Fremdw\u00e4hrung": {
+ "account_type": "Cash"
+ },
+ "2900 Aktive Rechnungsabrenzungsposten": {
+ "account_type": "Receivable"
+ },
+ "2600 Anteile an verbundenen Unternehmen": {
+ "account_type": "Equity"
+ },
+ "2680 Besitzwechsel ohne Forderungen": {
+ "account_type": "Receivable"
+ },
+ "2950 Aktiviertes Disagio": {
+ "account_type": "Receivable"
+ },
+ "2610 Eigene Anteile und Wertpapiere an mit Mehrheit beteiligten Unternehmen": {
+ "account_type": "Receivable"
+ },
+ "2570 Einfuhrumsatzsteuer (bezahlt)": {"account_type": "Tax"},
+
+ "2460 Eingeforderte aber noch nicht eingezahlte Einlagen": {
+ "account_type": "Receivable"
+ },
+ "2180 Einzelwertberichtigungen zu Forderungen aus Lief. und Leist. Ausland": {
+ "account_type": "Receivable"
+ },
+ "2130 Einzelwertberichtigungen zu Forderungen aus Lief. und Leist. EU": {
+ "account_type": "Receivable"
+ },
+ "2080 Einzelwertberichtigungen zu Forderungen aus Lief. und Leist. Inland ": {
+ "account_type": "Receivable"
+ },
+ "2270 Einzelwertberichtigungen zu Forderungen gegen\u00fcber Unternehmen mit denen ein Beteiligungsverh\u00e4ltnis besteht": {
+ "account_type": "Receivable"
+ },
+ "2230 Einzelwertberichtigungen zu Forderungen gegen\u00fcber verbundenen Unternehmen": {
+ "account_type": "Receivable"
+ },
+ "2470 Einzelwertberichtigungen zu sonstigen Forderungen und Verm\u00f6gensgegenst\u00e4nden": {
+ "account_type": "Receivable"
+ },
+ "2700 Kassenbestand": {
+ "account_type": "Cash"
+ },
+ "2190 Pauschalwertberichtigungen zu Forderungen aus Lief. und Leist. sonstiges Ausland": {
+ "account_type": "Receivable"
+ },
+ "2130 Pauschalwertberichtigungen zu Forderungen aus Lief. und Leist. EU": {
+ "account_type": "Receivable"
+ },
+ "2100 Pauschalwertberichtigungen zu Forderungen aus Lief. und Leist. Inland ": {
+ "account_type": "Receivable"
+ },
+ "2280 Pauschalwertberichtigungen zu Forderungen gegen\u00fcber Unternehmen mit denen ein Beteiligungsverh\u00e4ltnis besteht": {
+ "account_type": "Receivable"
+ },
+ "2240 Pauschalwertberichtigungen zu Forderungen gegen\u00fcber verbundenen Unternehmen": {
+ "account_type": "Receivable"
+ },
+ "2480 Pauschalwertberichtigungen zu sonstigen Forderungen und Verm\u00f6gensgegenst\u00e4nden": {
+ "account_type": "Receivable"
+ },
+ "2740 Postwertzeichen": {
+ "account_type": "Cash"
+ },
+ "2780 Schecks in Euro": {
+ "account_type": "Cash"
+ },
+ "2800 Guthaben bei Bank": {
+ "account_type": "Bank"
+ },
+ "2801 Guthaben bei Bank - Sparkonto": {
+ "account_type": "Bank"
+ },
+ "2810 Guthaben bei Paypal": {
+ "account_type": "Bank"
+ },
+ "2930 Mietvorauszahlungen": {
+ "account_type": "Receivable"
+ },
+ "2980 Abgrenzung latenter Steuern": {
+ "account_type": "Receivable"
+ },
+ "2500 Vorsteuer": {
+ "account_type": "Receivable"
+ },
+ "2510 Vorsteuer Inland 10%": {
+ "account_type": "Tax"
+ },
+ "2895 Schwebende Geldbewegugen": {
+ "account_type": "Bank"
+ },
+ "2513 Vorsteuer Inland 5%": {
+ "account_type": "Tax"
+ },
+ "2515 Vorsteuer Inland 20%": {
+ "account_type": "Tax"
+ },
+ "2520 Vorsteuer aus innergemeinschaftlichem Erwerb 10%": {
+ "account_type": "Tax"
+ },
+ "2525 Vorsteuer aus innergemeinschaftlichem Erwerb 20%": {
+ "account_type": "Tax"
+ },
+ "2530 Vorsteuer \u00a719/Art 19 ( reverse charge ) ": {
+ "account_type": "Tax"
+ },
+ "2690 Wertberichtigungen zu Wertpapieren und Anteilen": {
+ "account_type": "Receivable"
+ },
"root_type": "Asset"
},
- "Summe Personalaufwand": {
- "6000 bis 6190 L\u00f6hne": {},
- "6200 bis 6390 Geh\u00e4lter": {},
- "6400 bis 6440 Aufwendungen f\u00fcr Abfertigungen": {},
- "6450 bis 6490 Aufwendungen f\u00fcr Altersversorgung": {},
- "6500 bis 6550 Gesetzlicher Sozialaufwand Arbeiter": {},
- "6560 bis 6590 Gesetzlicher Sozialaufwand Angestellte": {},
- "6600 bis 6650 Lohnabh\u00e4ngige Abgaben und Pflichtbeitr\u00e4gte": {},
- "6660 bis 6690 Gehaltsabh\u00e4ngige Abgaben und Pflichtbeitr\u00e4gte": {},
- "6700 bis 6890 Sonstige Sozialaufwendungen": {},
- "Aufwandsstellenrechnung": {},
+ "Klasse 4: Betriebliche Erträge": {
+ "4000 Erlöse 20 %": {"account_type": "Income Account"},
+ "4020 Erl\u00f6se 0 % steuerbefreit": {"account_type": "Income Account"},
+ "4010 Erl\u00f6se 10 %": {"account_type": "Income Account"},
+ "4030 Erl\u00f6se 13 %": {"account_type": "Income Account"},
+ "4040 Erl\u00f6se 0 % innergemeinschaftliche Lieferungen": {"account_type": "Income Account"},
+ "4400 Erl\u00f6sreduktion 0 % steuerbefreit": {"account_type": "Expense Account"},
+ "4410 Erl\u00f6sreduktion 10 %": {"account_type": "Expense Account"},
+ "4420 Erl\u00f6sreduktion 20 %": {"account_type": "Expense Account"},
+ "4430 Erl\u00f6sreduktion 13 %": {"account_type": "Expense Account"},
+ "4440 Erl\u00f6sreduktion 0 % innergemeinschaftliche Lieferungen": {"account_type": "Expense Account"},
+ "4500 Ver\u00e4nderungen des Bestandes an fertigen und unfertigen Erzeugn. sowie an noch nicht abrechenbaren Leistungen": {"account_type": "Income Account"},
+ "4580 Aktivierte Eigenleistungen": {"account_type": "Income Account"},
+ "4600 Erl\u00f6se aus dem Abgang vom Anlageverm\u00f6gen, ausgen. Finanzanlagen": {"account_type": "Income Account"},
+ "4630 Ertr\u00e4ge aus dem Abgang vom Anlageverm\u00f6gen, ausgen. Finanzanlagen": {"account_type": "Income Account"},
+ "4660 Ertr\u00e4ge aus der Zuschreibung zum Anlageverm\u00f6gen, ausgen. Finanzanlagen": {"account_type": "Income Account"},
+ "4700 Ertr\u00e4ge aus der Aufl\u00f6sung von R\u00fcckstellungen": {"account_type": "Income Account"},
+ "4800 \u00dcbrige betriebliche Ertr\u00e4ge": {"account_type": "Income Account"},
+ "root_type": "Income"
+ },
+ "Klasse 5: Aufwand f\u00fcr Material und Leistungen": {
+ "5000 Einkauf Partnerleistungen": {"account_type": "Cost of Goods Sold"},
+ "5100 Verbrauch an Rohstoffen": {"account_type": "Cost of Goods Sold"},
+ "5200 Verbrauch von bezogenen Fertig- und Einzelteilen": {"account_type": "Cost of Goods Sold"},
+ "5300 Verbrauch von Hilfsstoffen": {"account_type": "Cost of Goods Sold"},
+ "5340 Verbrauch Verpackungsmaterial": {"account_type": "Cost of Goods Sold"},
+ "5470 Verbrauch von Kleinmaterial": {"account_type": "Cost of Goods Sold"},
+ "5450 Verbrauch von Reinigungsmaterial": {"account_type": "Cost of Goods Sold"},
+ "5400 Verbrauch von Betriebsstoffen": {"account_type": "Cost of Goods Sold"},
+ "5500 Verbrauch von Werkzeugen und anderen Erzeugungshilfsmittel": {"account_type": "Cost of Goods Sold"},
+ "5600 Verbrauch von Brenn- und Treibstoffen, Energie und Wasser": {"account_type": "Cost of Goods Sold"},
+ "5700 Bearbeitung durch Dritte": {"account_type": "Cost of Goods Sold"},
+ "5900 Aufwandsstellenrechnung Material": {"account_type": "Cost of Goods Sold"},
+ "5820 Skontoertr\u00e4ge (20% USt.)": {"account_type": "Income Account"},
+ "5810 Skontoertr\u00e4ge (10% USt.)": {"account_type": "Income Account"},
+ "5010 Handelswareneinkauf 10 %": {"account_type": "Cost of Goods Sold"},
+ "5020 Handelswareneinkauf 20 %": {"account_type": "Cost of Goods Sold"},
+ "5040 Handelswareneinkauf innergemeinschaftlicher Erwerb 10 % VSt/10 % USt": {"account_type": "Cost of Goods Sold"},
+ "5050 Handelswareneinkauf innergemeinschaftlicher Erwerb 20 % VSt/20 % USt": {"account_type": "Cost of Goods Sold"},
+ "5070 Handelswareneinkauf innergemeinschaftlicher Erwerb ohne Vorsteuerabzug und 10 % USt": {"account_type": "Cost of Goods Sold"},
+ "5080 Handelswareneinkauf innergemeinschaftlicher Erwerb ohne Vorsteuerabzug und 20 % USt": {"account_type": "Cost of Goods Sold"},
"root_type": "Expense"
},
- "Summe Umlaufverm\u00f6gen": {
- "2000 bis 2007 Forderungen aus Lief. und Leist. Inland": {
- "account_type": "Receivable"
- },
- "2100 bis 2120 Forderungen aus Lief. und Leist. EU": {
- "account_type": "Receivable"
- },
- "2150 bis 2170 Forderungen aus Lief. und Leist. Ausland": {
- "account_type": "Receivable"
- },
- "2200 bis 2220 Forderungen gegen\u00fcber verbundenen Unternehmen": {
- "account_type": "Receivable"
- },
- "2250 bis 2270 Forderungen gegen\u00fcber Unternehmen, mit denen ein Beteiligungsverh\u00e4ltnis besteht": {
- "account_type": "Receivable"
- },
- "2300 bis 2460 Sonstige Forderungen und Verm\u00f6gensgegenst\u00e4nde": {
- "account_type": "Receivable"
- },
- "2630 bis 2670 Sonstige Wertpapiere": {
- "account_type": "Receivable"
- },
- "2750 bis 2770 Kassenbest\u00e4nde in Fremdw\u00e4hrung": {
- "account_type": "Receivable"
- },
- "Aktive Rechnungsabrenzungsposten": {
- "account_type": "Receivable"
- },
- "Anteile an verbundenen Unternehmen": {
- "account_type": "Receivable"
- },
- "Bank / Guthaben bei Kreditinstituten": {
- "account_type": "Receivable"
- },
- "Besitzwechsel ...": {
- "account_type": "Receivable"
- },
- "Disagio": {
- "account_type": "Receivable"
- },
- "Eigene Anteile (Wertpapiere)": {
- "account_type": "Receivable"
- },
- "Einfuhrumsatzsteuer (bezahlt)": {},
- "Eingeforderte aber noch nicht eingezahlte Einlagen": {
- "account_type": "Receivable"
- },
- "Einzelwertberichtigungen zu Forderungen aus Lief. und Leist. Ausland": {
- "account_type": "Receivable"
- },
- "Einzelwertberichtigungen zu Forderungen aus Lief. und Leist. EU": {
- "account_type": "Receivable"
- },
- "Einzelwertberichtigungen zu Forderungen aus Lief. und Leist. Inland ": {
- "account_type": "Receivable"
- },
- "Einzelwertberichtigungen zu Forderungen gegen\u00fcber Unternehmen mit denen ein Beteiligungsverh\u00e4ltnis besteht": {
- "account_type": "Receivable"
- },
- "Einzelwertberichtigungen zu Forderungen gegen\u00fcber verbundenen Unternehmen": {
- "account_type": "Receivable"
- },
- "Einzelwertberichtigungen zu sonstigen Forderungen und Verm\u00f6gensgegenst\u00e4nden": {
- "account_type": "Receivable"
- },
- "Kassenbestand": {
- "account_type": "Receivable"
- },
- "Pauschalwertberichtigungen zu Forderungen aus Lief. und Leist. Ausland": {
- "account_type": "Receivable"
- },
- "Pauschalwertberichtigungen zu Forderungen aus Lief. und Leist. EU": {
- "account_type": "Receivable"
- },
- "Pauschalwertberichtigungen zu Forderungen aus Lief. und Leist. Inland ": {
- "account_type": "Receivable"
- },
- "Pauschalwertberichtigungen zu Forderungen gegen\u00fcber Unternehmen mit denen ein Beteiligungsverh\u00e4ltnis besteht": {
- "account_type": "Receivable"
- },
- "Pauschalwertberichtigungen zu Forderungen gegen\u00fcber verbundenen Unternehmen": {
- "account_type": "Receivable"
- },
- "Pauschalwertberichtigungen zu sonstigen Forderungen und Verm\u00f6gensgegenst\u00e4nden": {
- "account_type": "Receivable"
- },
- "Postwertzeichen": {
- "account_type": "Receivable"
- },
- "Schecks in Inlandsw\u00e4hrung": {
- "account_type": "Receivable"
- },
- "Sonstige Anteile": {
- "account_type": "Receivable"
- },
- "Stempelmarken": {
- "account_type": "Receivable"
- },
- "Steuerabgrenzung": {
- "account_type": "Receivable"
- },
- "Unterschiedsbetrag gem. Abschnitt XII Pensionskassengesetz": {
- "account_type": "Receivable"
- },
- "Unterschiedsbetrag zur gebotenen Pensionsr\u00fcckstellung": {
- "account_type": "Receivable"
- },
- "Vorsteuer": {
- "account_type": "Receivable"
- },
- "Vorsteuer aus ig. Erwerb 10%": {
- "account_type": "Tax"
- },
- "Vorsteuer aus ig. Erwerb 20%": {
- "account_type": "Tax"
- },
- "Vorsteuer \u00a719/Art 19 ( reverse charge ) ": {
- "account_type": "Tax"
- },
- "Wertberichtigungen": {
- "account_type": "Receivable"
- },
- "root_type": "Asset"
- },
- "Summe Vorr\u00e4te": {
- "1000 bis 1090 Bezugsverrechnung": {},
- "1100 bis 1190 Rohstoffe": {},
- "1200 bis 1290 Bezogene Teile": {},
- "1300 bis 1340 Hilfsstoffe": {},
- "1350 bis 1390 Betriebsstoffe": {},
- "1400 bis 1490 Unfertige Erzeugniss": {},
- "1500 bis 1590 Fertige Erzeugniss": {},
- "1600 bis 1690 Waren": {},
- "1700 bis 1790 Noch nicht abgerechenbare Leistungen": {},
- "1900 bis 1990 Wertberichtigungen": {},
- "geleistete Anzahlungen": {},
- "root_type": "Asset"
- },
- "Summe Wareneinsatz": {
- "5100 bis 5190 Verbrauch an Rohstoffen": {},
- "5200 bis 5290 Verbrauch von bezogenen Fertig- und Einzelteilen": {},
- "5300 bis 5390 Verbrauch von Hilfsstoffen": {},
- "5400 bis 5490 Verbrauch von Betriebsstoffen": {},
- "5500 bis 5590 Verbrauch von Werkzeugen und anderen Erzeugungshilfsmittel": {},
- "5600 bis 5690 Verbrauch von Brenn- und Treibstoffen, Energie und Wasser": {},
- "5700 bis 5790 Sonstige bezogene Herstellungsleistungen": {},
- "Aufwandsstellenrechnung": {},
- "Skontoertr\u00e4ge auf Materialaufwand": {},
- "Skontoertr\u00e4ge auf sonstige bezogene Herstellungsleistungen": {},
- "Wareneinkauf 10 %": {},
- "Wareneinkauf 20 %": {},
- "Wareneinkauf igErwerb 10 % VSt/10 % USt": {},
- "Wareneinkauf igErwerb 20 % VSt/20 % USt": {},
- "Wareneinkauf igErwerb ohne Vorsteuerabzug und 10 % USt": {},
- "Wareneinkauf igErwerb ohne Vorsteuerabzug und 20 % USt": {},
+ "Klasse 6: Personalaufwand": {
+ "6000 L\u00f6hne": {"account_type": "Payable"},
+ "6200 Geh\u00e4lter": {"account_type": "Payable"},
+ "6400 Aufwendungen f\u00fcr Abfertigungen": {"account_type": "Payable"},
+ "6450 Aufwendungen f\u00fcr Altersversorgung": {"account_type": "Payable"},
+ "6500 Gesetzlicher Sozialaufwand Arbeiter": {"account_type": "Payable"},
+ "6560 Gesetzlicher Sozialaufwand Angestellte": {"account_type": "Payable"},
+ "6600 Lohnabh\u00e4ngige Abgaben und Pflichtbeitr\u00e4gte": {"account_type": "Payable"},
+ "6660 Gehaltsabh\u00e4ngige Abgaben und Pflichtbeitr\u00e4gte": {"account_type": "Payable"},
+ "6700 Sonstige Sozialaufwendungen": {"account_type": "Payable"},
+ "6900 Aufwandsstellenrechnung Personal": {"account_type": "Payable"},
"root_type": "Expense"
+ },
+ "Klasse 7: Abschreibungen und sonstige betriebliche Aufwendungen": {
+ "7010 Abschreibungen auf das Anlageverm\u00f6gen (ausgenommen Finanzanlagen)": {"account_type": "Depreciation"},
+ "7100 Sonstige Steuern und Geb\u00fchren": {"account_type": "Tax"},
+ "7200 Instandhaltung u. Reinigung durch Dritte, Entsorgung, Energie": {"account_type": "Expense Account"},
+ "7300 Transporte durch Dritte": {"account_type": "Expense Account"},
+ "7310 Fahrrad - Aufwand": {"account_type": "Expense Account"},
+ "7320 Kfz - Aufwand": {"account_type": "Expense Account"},
+ "7330 LKW - Aufwand": {"account_type": "Expense Account"},
+ "7340 Lastenrad - Aufwand": {"account_type": "Expense Account"},
+ "7350 Reise- und Fahraufwand": {"account_type": "Expense Account"},
+ "7360 Tag- und N\u00e4chtigungsgelder": {"account_type": "Expense Account"},
+ "7380 Nachrichtenaufwand": {"account_type": "Expense Account"},
+ "7400 Miet- und Pachtaufwand": {"account_type": "Expense Account"},
+ "7440 Leasingaufwand": {"account_type": "Expense Account"},
+ "7480 Lizenzaufwand": {"account_type": "Expense Account"},
+ "7500 Aufwand f\u00fcr beigestelltes Personal": {"account_type": "Expense Account"},
+ "7540 Provisionen an Dritte": {"account_type": "Expense Account"},
+ "7580 Aufsichtsratsverg\u00fctungen": {"account_type": "Expense Account"},
+ "7610 Druckerzeugnisse und Vervielf\u00e4ltigungen": {"account_type": "Expense Account"},
+ "7650 Werbung und Repr\u00e4sentationen": {"account_type": "Expense Account"},
+ "7700 Versicherungen": {"account_type": "Expense Account"},
+ "7750 Beratungs- und Pr\u00fcfungsaufwand": {"account_type": "Expense Account"},
+ "7800 Forderungsverluste und Schadensf\u00e4lle": {"account_type": "Expense Account"},
+ "7840 Verschiedene betriebliche Aufwendungen": {"account_type": "Expense Account"},
+ "7910 Aufwandsstellenrechung der Hersteller": {"account_type": "Expense Account"},
+ "7060 Sofortabschreibungen geringwertig": {"account_type": "Expense Account"},
+ "7070 Abschreibungen vom Umlaufverm\u00f6gen, soweit diese die im Unternehmen \u00fcblichen Abschreibungen \u00fcbersteigen": {"account_type": "Depreciation"},
+ "7900 Aufwandsstellenrechnung": {"account_type": "Expense Account"},
+ "7770 Aus- und Fortbildung": {"account_type": "Expense Account"},
+ "7820 Buchwert abgegangener Anlagen, ausgenommen Finanzanlagen": {"account_type": "Expense Account"},
+ "7600 B\u00fcromaterial und Drucksorten": {"account_type": "Expense Account"},
+ "7630 Fachliteratur und Zeitungen ": {"account_type": "Expense Account"},
+ "7960 Herstellungskosten der zur Erzielung der Umsatzerl\u00f6se erbrachten Leistungen": {"account_type": "Expense Account"},
+ "7780 Mitgliedsbeitr\u00e4ge": {"account_type": "Expense Account"},
+ "7880 Skontoertr\u00e4ge auf sonstige betriebliche Aufwendungen": {"account_type": "Expense Account"},
+ "7990 Sonstige betrieblichen Aufwendungen": {"account_type": "Expense Account"},
+ "7680 Spenden und Trinkgelder": {"account_type": "Expense Account"},
+ "7790 Spesen des Geldverkehrs": {"account_type": "Expense Account"},
+ "7830 Verluste aus dem Abgang vom Anlageverm\u00f6gen, ausgenommen Finanzanlagen": {"account_type": "Expense Account"},
+ "7970 Vertriebskosten": {"account_type": "Expense Account"},
+ "7980 Verwaltungskosten": {"account_type": "Expense Account"},
+ "root_type": "Expense"
+ },
+ "Klasse 8: Finanz- und ausserordentliche Ertr\u00e4ge und Aufwendungen": {
+ "8000 Ertr\u00e4ge aus Beteiligungen": {"account_type": "Income Account"},
+ "8050 Ertr\u00e4ge aus anderen Wertpapieren und Ausleihungen des Finanzanlageverm\u00f6gens": {"account_type": "Income Account"},
+ "8100 Zinsen aus Bankguthaben": {"account_type": "Income Account"},
+ "8110 Zinsen aus gewaehrten Darlehen": {"account_type": "Income Account"},
+ "8130 Verzugszinsenertraege": {"account_type": "Income Account"},
+ "8220 Aufwendungen aus Beteiligungen": {"account_type": "Expense Account"},
+ "8260 Aufwendungen aus sonst. Fiananzanlagen und aus Wertpapieren des Umlaufverm\u00f6gens": {},
+ "8280 Zinsen und \u00e4hnliche Aufwendungem": {"account_type": "Expense Account"},
+ "8400 Au\u00dferordentliche Ertr\u00e4ge": {"account_type": "Income Account"},
+ "8450 Au\u00dferordentliche Aufwendungen": {"account_type": "Expense Account"},
+ "8500 Steuern vom Einkommen und vom Ertrag": {
+ "account_type": "Tax"
+ },
+ "8600 Aufl\u00f6sung unversteuerten R\u00fccklagen": {"account_type": "Income Account"},
+ "8700 Aufl\u00f6sung von Kapitalr\u00fccklagen": {"account_type": "Income Account"},
+ "8750 Aufl\u00f6sung von Gewinnr\u00fccklagen": {"account_type": "Income Account"},
+ "8800 Zuweisung zu unversteuerten R\u00fccklagen": {"account_type": "Expense Account"},
+ "8900 Zuweisung zu Gewinnr\u00fccklagen": {"account_type": "Expense Account"},
+ "8100 Buchwert abgegangener Beteiligungen": {"account_type": "Expense Account"},
+ "8130 Buchwert abgegangener Wertpapiere des Umlaufverm\u00f6gens": {"account_type": "Expense Account"},
+ "8120 Buchwert abgegangener sonstiger Finanzanlagen": {"account_type": "Expense Account"},
+ "8990 Gewinnabfuhr bzw. Verlust\u00fcberrechnung aus Ergebnisabf\u00fchrungsvertr\u00e4gen": {"account_type": "Expense Account"},
+ "8350 nicht ausgenutzte Lieferantenskonti": {"account_type": "Expense Account"},
+ "root_type": "Income"
+ },
+ "Klasse 9 Passiva: Eigenkapital, R\u00fccklagen, stille Einlagen, Abschlusskonten": {
+ "9000 Gezeichnetes bzw. gewidmetes Kapital": {
+ "account_type": "Equity"
+ },
+ "9200 Kapitalr\u00fccklagen": {
+ "account_type": "Equity"
+ },
+ "9300 Gewinnr\u00fccklagen": {
+ "account_type": "Equity"
+ },
+ "9400 Bewertungsreserven uns sonst. unversteuerte R\u00fccklagen": {
+ "account_type": "Equity"
+ },
+ "9600 Private Entnahmen": {"account_type": "Equity"},
+ "9610 Privatsteuern": {"account_type": "Equity"},
+ "9700 Einlagen stiller Gesellschafter ": {"account_type": "Equity"},
+ "9900 Evidenzkonto": {"account_type": "Equity"},
+ "9800 Er\u00f6ffnungsbilanzkonto (EBK)": {"account_type": "Equity"},
+ "9880 Jahresergebnis laut Gewinn- und Verlustrechnung (G+V)": {"account_type": "Equity"},
+ "9850 Schlussbilanzkonto (SBK)": {"account_type": "Round Off"},
+ "9190 nicht eingeforderte ausstehende Einlagen und berechtigte Entnahmen von Gesellschaftern": {
+ "account_type": "Equity"
+ },
+ "root_type": "Equity"
}
- }
-}
+ }
+ }
diff --git a/erpnext/accounts/doctype/bank_clearance/bank_clearance.py b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py
index 1a572d9..78c3526 100644
--- a/erpnext/accounts/doctype/bank_clearance/bank_clearance.py
+++ b/erpnext/accounts/doctype/bank_clearance/bank_clearance.py
@@ -99,7 +99,7 @@
.where(loan_disbursement.clearance_date.isnull())
.where(loan_disbursement.disbursement_account.isin([self.bank_account, self.account]))
.orderby(loan_disbursement.disbursement_date)
- .orderby(loan_disbursement.name, frappe.qb.desc)
+ .orderby(loan_disbursement.name, order=frappe.qb.desc)
).run(as_dict=1)
loan_repayment = frappe.qb.DocType("Loan Repayment")
@@ -126,7 +126,9 @@
if frappe.db.has_column("Loan Repayment", "repay_from_salary"):
query = query.where((loan_repayment.repay_from_salary == 0))
- query = query.orderby(loan_repayment.posting_date).orderby(loan_repayment.name, frappe.qb.desc)
+ query = query.orderby(loan_repayment.posting_date).orderby(
+ loan_repayment.name, order=frappe.qb.desc
+ )
loan_repayments = query.run(as_dict=True)
diff --git a/erpnext/accounts/doctype/budget/budget.json b/erpnext/accounts/doctype/budget/budget.json
index fc4dd20..f0566f4 100644
--- a/erpnext/accounts/doctype/budget/budget.json
+++ b/erpnext/accounts/doctype/budget/budget.json
@@ -1,6 +1,7 @@
{
"actions": [],
"allow_import": 1,
+ "autoname": "naming_series:",
"creation": "2016-05-16 11:42:29.632528",
"doctype": "DocType",
"editable_grid": 1,
@@ -9,6 +10,7 @@
"budget_against",
"company",
"cost_center",
+ "naming_series",
"project",
"fiscal_year",
"column_break_3",
@@ -190,15 +192,26 @@
"label": "Budget Accounts",
"options": "Budget Account",
"reqd": 1
+ },
+ {
+ "fieldname": "naming_series",
+ "fieldtype": "Data",
+ "hidden": 1,
+ "label": "Series",
+ "no_copy": 1,
+ "print_hide": 1,
+ "read_only": 1,
+ "set_only_once": 1
}
],
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2020-10-06 15:13:54.055854",
+ "modified": "2022-10-10 22:14:36.361509",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Budget",
+ "naming_rule": "By \"Naming Series\" field",
"owner": "Administrator",
"permissions": [
{
@@ -220,5 +233,6 @@
],
"sort_field": "modified",
"sort_order": "DESC",
+ "states": [],
"track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/budget/budget.py b/erpnext/accounts/doctype/budget/budget.py
index 5527f9f..6ac3350 100644
--- a/erpnext/accounts/doctype/budget/budget.py
+++ b/erpnext/accounts/doctype/budget/budget.py
@@ -5,7 +5,6 @@
import frappe
from frappe import _
from frappe.model.document import Document
-from frappe.model.naming import make_autoname
from frappe.utils import add_months, flt, fmt_money, get_last_day, getdate
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
@@ -23,11 +22,6 @@
class Budget(Document):
- def autoname(self):
- self.name = make_autoname(
- self.get(frappe.scrub(self.budget_against)) + "/" + self.fiscal_year + "/.###"
- )
-
def validate(self):
if not self.get(frappe.scrub(self.budget_against)):
frappe.throw(_("{0} is mandatory").format(self.budget_against))
@@ -109,6 +103,9 @@
):
self.applicable_on_booking_actual_expenses = 1
+ def before_naming(self):
+ self.naming_series = f"{{{frappe.scrub(self.budget_against)}}}./.{self.fiscal_year}/.###"
+
def validate_expense_against_budget(args):
args = frappe._dict(args)
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js
index 7af41f3..763e2e6 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.js
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js
@@ -173,8 +173,8 @@
var update_jv_details = function(doc, r) {
$.each(r, function(i, d) {
var row = frappe.model.add_child(doc, "Journal Entry Account", "accounts");
- row.account = d.account;
- row.balance = d.balance;
+ frappe.model.set_value(row.doctype, row.name, "account", d.account)
+ frappe.model.set_value(row.doctype, row.name, "balance", d.balance)
});
refresh_field("accounts");
}
diff --git a/erpnext/accounts/doctype/payment_ledger_entry/test_payment_ledger_entry.py b/erpnext/accounts/doctype/payment_ledger_entry/test_payment_ledger_entry.py
index a71b19e..fc6dbba 100644
--- a/erpnext/accounts/doctype/payment_ledger_entry/test_payment_ledger_entry.py
+++ b/erpnext/accounts/doctype/payment_ledger_entry/test_payment_ledger_entry.py
@@ -3,12 +3,13 @@
import frappe
from frappe import qb
-from frappe.tests.utils import FrappeTestCase
+from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import nowdate
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
+from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
from erpnext.stock.doctype.item.test_item import create_item
@@ -127,6 +128,25 @@
payment.posting_date = posting_date
return payment
+ def create_sales_order(
+ self, qty=1, rate=100, posting_date=nowdate(), do_not_save=False, do_not_submit=False
+ ):
+ so = make_sales_order(
+ company=self.company,
+ transaction_date=posting_date,
+ customer=self.customer,
+ item_code=self.item,
+ cost_center=self.cost_center,
+ warehouse=self.warehouse,
+ debit_to=self.debit_to,
+ currency="INR",
+ qty=qty,
+ rate=100,
+ do_not_save=do_not_save,
+ do_not_submit=do_not_submit,
+ )
+ return so
+
def clear_old_entries(self):
doctype_list = [
"GL Entry",
@@ -406,3 +426,89 @@
]
self.assertEqual(pl_entries_for_crnote[0], expected_values[0])
self.assertEqual(pl_entries_for_crnote[1], expected_values[1])
+
+ @change_settings(
+ "Accounts Settings",
+ {"unlink_payment_on_cancellation_of_invoice": 1, "delete_linked_ledger_entries": 1},
+ )
+ def test_multi_payment_unlink_on_invoice_cancellation(self):
+ transaction_date = nowdate()
+ amount = 100
+ si = self.create_sales_invoice(qty=1, rate=amount, posting_date=transaction_date)
+
+ for amt in [40, 40, 20]:
+ # payment 1
+ pe = get_payment_entry(si.doctype, si.name)
+ pe.paid_amount = amt
+ pe.get("references")[0].allocated_amount = amt
+ pe = pe.save().submit()
+
+ si.reload()
+ si.cancel()
+
+ entries = frappe.db.get_list(
+ "Payment Ledger Entry",
+ filters={"against_voucher_type": si.doctype, "against_voucher_no": si.name, "delinked": 0},
+ )
+ self.assertEqual(entries, [])
+
+ # with references removed, deletion should be possible
+ si.delete()
+ self.assertRaises(frappe.DoesNotExistError, frappe.get_doc, si.doctype, si.name)
+
+ @change_settings(
+ "Accounts Settings",
+ {"unlink_payment_on_cancellation_of_invoice": 1, "delete_linked_ledger_entries": 1},
+ )
+ def test_multi_je_unlink_on_invoice_cancellation(self):
+ transaction_date = nowdate()
+ amount = 100
+ si = self.create_sales_invoice(qty=1, rate=amount, posting_date=transaction_date)
+
+ # multiple JE's against invoice
+ for amt in [40, 40, 20]:
+ je1 = self.create_journal_entry(
+ self.income_account, self.debit_to, amt, posting_date=transaction_date
+ )
+ je1.get("accounts")[1].party_type = "Customer"
+ je1.get("accounts")[1].party = self.customer
+ je1.get("accounts")[1].reference_type = si.doctype
+ je1.get("accounts")[1].reference_name = si.name
+ je1 = je1.save().submit()
+
+ si.reload()
+ si.cancel()
+
+ entries = frappe.db.get_list(
+ "Payment Ledger Entry",
+ filters={"against_voucher_type": si.doctype, "against_voucher_no": si.name, "delinked": 0},
+ )
+ self.assertEqual(entries, [])
+
+ # with references removed, deletion should be possible
+ si.delete()
+ self.assertRaises(frappe.DoesNotExistError, frappe.get_doc, si.doctype, si.name)
+
+ @change_settings(
+ "Accounts Settings",
+ {"unlink_payment_on_cancellation_of_invoice": 1, "delete_linked_ledger_entries": 1},
+ )
+ def test_advance_payment_unlink_on_order_cancellation(self):
+ transaction_date = nowdate()
+ amount = 100
+ so = self.create_sales_order(qty=1, rate=amount, posting_date=transaction_date).save().submit()
+
+ pe = get_payment_entry(so.doctype, so.name).save().submit()
+
+ so.reload()
+ so.cancel()
+
+ entries = frappe.db.get_list(
+ "Payment Ledger Entry",
+ filters={"against_voucher_type": so.doctype, "against_voucher_no": so.name, "delinked": 0},
+ )
+ self.assertEqual(entries, [])
+
+ # with references removed, deletion should be possible
+ so.delete()
+ self.assertRaises(frappe.DoesNotExistError, frappe.get_doc, so.doctype, so.name)
diff --git a/erpnext/accounts/doctype/payment_request/payment_request.json b/erpnext/accounts/doctype/payment_request/payment_request.json
index 2ee356a..2f3516e 100644
--- a/erpnext/accounts/doctype/payment_request/payment_request.json
+++ b/erpnext/accounts/doctype/payment_request/payment_request.json
@@ -186,8 +186,10 @@
{
"fetch_from": "bank_account.bank",
"fieldname": "bank",
- "fieldtype": "Read Only",
- "label": "Bank"
+ "fieldtype": "Link",
+ "label": "Bank",
+ "options": "Bank",
+ "read_only": 1
},
{
"fetch_from": "bank_account.bank_account_no",
@@ -366,10 +368,11 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2020-09-18 12:24:14.178853",
+ "modified": "2022-09-30 16:19:43.680025",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Request",
+ "naming_rule": "By \"Naming Series\" field",
"owner": "Administrator",
"permissions": [
{
@@ -401,5 +404,6 @@
}
],
"sort_field": "modified",
- "sort_order": "DESC"
+ "sort_order": "DESC",
+ "states": []
}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json
index b126d57..eedaaaf 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json
@@ -343,7 +343,8 @@
"no_copy": 1,
"options": "POS Invoice",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "search_index": 1
},
{
"default": "0",
@@ -1553,7 +1554,7 @@
"icon": "fa fa-file-text",
"is_submittable": 1,
"links": [],
- "modified": "2022-03-22 13:00:24.166684",
+ "modified": "2022-09-30 03:49:50.455199",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Invoice",
diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
index 6e3a076..54a3e93 100644
--- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.py
@@ -239,14 +239,14 @@
frappe.bold(d.warehouse),
frappe.bold(d.qty),
)
- if flt(available_stock) <= 0:
+ if is_stock_item and flt(available_stock) <= 0:
frappe.throw(
_("Row #{}: Item Code: {} is not available under warehouse {}.").format(
d.idx, item_code, warehouse
),
title=_("Item Unavailable"),
)
- elif flt(available_stock) < flt(d.qty):
+ elif is_stock_item and flt(available_stock) < flt(d.qty):
frappe.throw(
_(
"Row #{}: Stock quantity not enough for Item Code: {} under warehouse {}. Available quantity {}."
@@ -632,11 +632,12 @@
pos_sales_qty = get_pos_reserved_qty(item_code, warehouse)
return bin_qty - pos_sales_qty, is_stock_item
else:
- is_stock_item = False
+ is_stock_item = True
if frappe.db.exists("Product Bundle", item_code):
return get_bundle_availability(item_code, warehouse), is_stock_item
else:
- # Is a service item
+ is_stock_item = False
+ # Is a service item or non_stock item
return 0, is_stock_item
@@ -650,7 +651,9 @@
available_qty = item_bin_qty - item_pos_reserved_qty
max_available_bundles = available_qty / item.qty
- if bundle_bin_qty > max_available_bundles:
+ if bundle_bin_qty > max_available_bundles and frappe.get_value(
+ "Item", item.item_code, "is_stock_item"
+ ):
bundle_bin_qty = max_available_bundles
pos_sales_qty = get_pos_reserved_qty(bundle_item_code, warehouse)
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
index 986fc03..1e47777 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
@@ -98,7 +98,6 @@
"section_break_44",
"apply_discount_on",
"base_discount_amount",
- "additional_discount_account",
"column_break_46",
"additional_discount_percentage",
"discount_amount",
@@ -1388,12 +1387,6 @@
"read_only": 1
},
{
- "fieldname": "additional_discount_account",
- "fieldtype": "Link",
- "label": "Additional Discount Account",
- "options": "Account"
- },
- {
"default": "0",
"fieldname": "ignore_default_payment_terms_template",
"fieldtype": "Check",
@@ -1437,6 +1430,7 @@
"fieldname": "tax_withheld_vouchers",
"fieldtype": "Table",
"label": "Tax Withheld Vouchers",
+ "no_copy": 1,
"options": "Tax Withheld Vouchers",
"read_only": 1
}
@@ -1445,7 +1439,7 @@
"idx": 204,
"is_submittable": 1,
"links": [],
- "modified": "2022-09-13 23:39:54.525037",
+ "modified": "2022-10-07 14:19:14.214157",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice",
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index d185300..2b633cb 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -669,9 +669,6 @@
exchange_rate_map, net_rate_map = get_purchase_document_details(self)
- enable_discount_accounting = cint(
- frappe.db.get_single_value("Buying Settings", "enable_discount_accounting")
- )
provisional_accounting_for_non_stock_items = cint(
frappe.db.get_value(
"Company", self.company, "enable_provisional_accounting_for_non_stock_items"
@@ -1159,9 +1156,6 @@
def make_tax_gl_entries(self, gl_entries):
# tax table gl entries
valuation_tax = {}
- enable_discount_accounting = cint(
- frappe.db.get_single_value("Buying Settings", "enable_discount_accounting")
- )
for tax in self.get("taxes"):
amount, base_amount = self.get_tax_amounts(tax, None)
@@ -1249,15 +1243,6 @@
)
)
- @property
- def enable_discount_accounting(self):
- if not hasattr(self, "_enable_discount_accounting"):
- self._enable_discount_accounting = cint(
- frappe.db.get_single_value("Buying Settings", "enable_discount_accounting")
- )
-
- return self._enable_discount_accounting
-
def make_internal_transfer_gl_entries(self, gl_entries):
if self.is_internal_transfer() and flt(self.base_total_taxes_and_charges):
account_currency = get_account_currency(self.unrealized_profit_loss_account)
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index 0a4f25b..f901257 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -1543,6 +1543,37 @@
pi.save()
self.assertEqual(pi.items[0].conversion_factor, 1000)
+ def test_batch_expiry_for_purchase_invoice(self):
+ from erpnext.controllers.sales_and_purchase_return import make_return_doc
+
+ item = self.make_item(
+ "_Test Batch Item For Return Check",
+ {
+ "is_purchase_item": 1,
+ "is_stock_item": 1,
+ "has_batch_no": 1,
+ "create_new_batch": 1,
+ "batch_number_series": "TBIRC.#####",
+ },
+ )
+
+ pi = make_purchase_invoice(
+ qty=1,
+ item_code=item.name,
+ update_stock=True,
+ )
+
+ pi.load_from_db()
+ batch_no = pi.items[0].batch_no
+ self.assertTrue(batch_no)
+
+ frappe.db.set_value("Batch", batch_no, "expiry_date", add_days(nowdate(), -1))
+
+ return_pi = make_return_doc(pi.doctype, pi.name)
+ return_pi.save().submit()
+
+ self.assertTrue(return_pi.docstatus == 1)
+
def check_gl_entries(doc, voucher_no, expected_gle, posting_date):
gl_entries = frappe.db.sql(
diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
index 7fa2fe2..fca7e3a 100644
--- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
+++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
@@ -74,7 +74,6 @@
"manufacturer_part_no",
"accounting",
"expense_account",
- "discount_account",
"col_break5",
"is_fixed_asset",
"asset_location",
@@ -861,12 +860,6 @@
"read_only": 1
},
{
- "fieldname": "discount_account",
- "fieldtype": "Link",
- "label": "Discount Account",
- "options": "Account"
- },
- {
"fieldname": "product_bundle",
"fieldtype": "Link",
"label": "Product Bundle",
@@ -877,7 +870,7 @@
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2022-06-17 05:31:10.520171",
+ "modified": "2022-09-27 10:54:23.980713",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice Item",
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index e51938b..afd5a59 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -22,9 +22,12 @@
from erpnext.accounts.party import get_due_date, get_party_account, get_party_details
from erpnext.accounts.utils import get_account_currency
from erpnext.assets.doctype.asset.depreciation import (
+ depreciate_asset,
get_disposal_account_and_cost_center,
get_gl_entries_on_asset_disposal,
get_gl_entries_on_asset_regain,
+ reset_depreciation_schedule,
+ reverse_depreciation_entry_made_after_disposal,
)
from erpnext.controllers.accounts_controller import validate_account_head
from erpnext.controllers.selling_controller import SellingController
@@ -1081,23 +1084,25 @@
if self.is_return:
fixed_asset_gl_entries = get_gl_entries_on_asset_regain(
- asset, item.base_net_amount, item.finance_book
+ asset, item.base_net_amount, item.finance_book, self.get("doctype"), self.get("name")
)
asset.db_set("disposal_date", None)
if asset.calculate_depreciation:
- self.reverse_depreciation_entry_made_after_disposal(asset)
- self.reset_depreciation_schedule(asset)
+ posting_date = frappe.db.get_value("Sales Invoice", self.return_against, "posting_date")
+ reverse_depreciation_entry_made_after_disposal(asset, posting_date)
+ reset_depreciation_schedule(asset, self.posting_date)
else:
+ if asset.calculate_depreciation:
+ depreciate_asset(asset, self.posting_date)
+ asset.reload()
+
fixed_asset_gl_entries = get_gl_entries_on_asset_disposal(
- asset, item.base_net_amount, item.finance_book
+ asset, item.base_net_amount, item.finance_book, self.get("doctype"), self.get("name")
)
asset.db_set("disposal_date", self.posting_date)
- if asset.calculate_depreciation:
- self.depreciate_asset(asset)
-
for gle in fixed_asset_gl_entries:
gle["against"] = self.customer
gl_entries.append(self.get_gl_dict(gle, item=item))
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index 782e08e..ce44ae3 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -8,7 +8,7 @@
from frappe.model.dynamic_links import get_dynamic_link_map
from frappe.model.naming import make_autoname
from frappe.tests.utils import change_settings
-from frappe.utils import add_days, flt, getdate, nowdate
+from frappe.utils import add_days, flt, getdate, nowdate, today
import erpnext
from erpnext.accounts.doctype.account.test_account import create_account, get_inventory_account
@@ -3196,6 +3196,37 @@
"Accounts Settings", "Accounts Settings", "unlink_payment_on_cancel_of_invoice", unlink_enabled
)
+ def test_batch_expiry_for_sales_invoice_return(self):
+ from erpnext.controllers.sales_and_purchase_return import make_return_doc
+ from erpnext.stock.doctype.item.test_item import make_item
+
+ item = make_item(
+ "_Test Batch Item For Return Check",
+ {
+ "is_purchase_item": 1,
+ "is_stock_item": 1,
+ "has_batch_no": 1,
+ "create_new_batch": 1,
+ "batch_number_series": "TBIRC.#####",
+ },
+ )
+
+ pr = make_purchase_receipt(qty=1, item_code=item.name)
+
+ batch_no = pr.items[0].batch_no
+ si = create_sales_invoice(qty=1, item_code=item.name, update_stock=1, batch_no=batch_no)
+
+ si.load_from_db()
+ batch_no = si.items[0].batch_no
+ self.assertTrue(batch_no)
+
+ frappe.db.set_value("Batch", batch_no, "expiry_date", add_days(today(), -1))
+
+ return_si = make_return_doc(si.doctype, si.name)
+ return_si.save().submit()
+
+ self.assertTrue(return_si.docstatus == 1)
+
def get_sales_invoice_for_e_invoice():
si = make_sales_invoice_for_ewaybill()
@@ -3289,6 +3320,7 @@
"serial_no": args.serial_no,
"conversion_factor": 1,
"incoming_rate": args.incoming_rate or 0,
+ "batch_no": args.batch_no or None,
},
)
diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
index 0b5df9e..7eddd81 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
@@ -249,6 +249,9 @@
)
else:
tax_amount = net_total * tax_details.rate / 100 if net_total > 0 else 0
+
+ # once tds is deducted, not need to add vouchers in the invoice
+ voucher_wise_amount = {}
else:
tax_amount = get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers)
@@ -335,6 +338,9 @@
"party": ["in", parties],
}
+ if party_type == "Customer":
+ filters.update({"against_voucher": ["is", "not set"]})
+
if company:
filters["company"] = company
if from_date and to_date:
@@ -422,7 +428,10 @@
):
# Get net total again as TDS is calculated on net total
# Grand is used to just check for threshold breach
- net_total = frappe.db.get_value("Purchase Invoice", invoice_filters, "sum(net_total)") or 0.0
+ net_total = 0
+ if vouchers:
+ net_total = frappe.db.get_value("Purchase Invoice", invoice_filters, "sum(net_total)")
+
net_total += inv.net_total
supp_credit_amt = net_total - cumulative_threshold
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py
index e77e828..82f38da 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.py
+++ b/erpnext/accounts/report/general_ledger/general_ledger.py
@@ -237,9 +237,9 @@
or filters.get("party")
or filters.get("group_by") in ["Group by Account", "Group by Party"]
):
- conditions.append("posting_date >=%(from_date)s")
+ conditions.append("(posting_date >=%(from_date)s or is_opening = 'Yes')")
- conditions.append("(posting_date <=%(to_date)s or is_opening = 'Yes')")
+ conditions.append("(posting_date <=%(to_date)s)")
if filters.get("project"):
conditions.append("project in %(project)s")
diff --git a/erpnext/accounts/report/sales_register/sales_register.py b/erpnext/accounts/report/sales_register/sales_register.py
index 33bd3c7..06e3c61 100644
--- a/erpnext/accounts/report/sales_register/sales_register.py
+++ b/erpnext/accounts/report/sales_register/sales_register.py
@@ -370,7 +370,7 @@
where parent=`tabSales Invoice`.name
and ifnull(`tab{table}`.{field}, '') = %({field})s)"""
- conditions += get_sales_invoice_item_field_condition("mode_of_payments", "Sales Invoice Payment")
+ conditions += get_sales_invoice_item_field_condition("mode_of_payment", "Sales Invoice Payment")
conditions += get_sales_invoice_item_field_condition("cost_center")
conditions += get_sales_invoice_item_field_condition("warehouse")
conditions += get_sales_invoice_item_field_condition("brand")
diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py
index 6bd08ad..6d2cd8e 100644
--- a/erpnext/accounts/report/trial_balance/trial_balance.py
+++ b/erpnext/accounts/report/trial_balance/trial_balance.py
@@ -172,6 +172,7 @@
query_filters = {
"company": filters.company,
"from_date": filters.from_date,
+ "to_date": filters.to_date,
"report_type": report_type,
"year_start_date": filters.year_start_date,
"project": filters.project,
@@ -200,7 +201,7 @@
where
company=%(company)s
{additional_conditions}
- and (posting_date < %(from_date)s or ifnull(is_opening, 'No') = 'Yes')
+ and (posting_date < %(from_date)s or (ifnull(is_opening, 'No') = 'Yes' and posting_date <= %(to_date)s))
and account in (select name from `tabAccount` where report_type=%(report_type)s)
and is_cancelled = 0
group by account""".format(
diff --git a/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.py b/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.py
index 5fcfdff..ee22348 100644
--- a/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.py
+++ b/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.py
@@ -104,12 +104,17 @@
where company=%(company)s
and is_cancelled=0
and ifnull(party_type, '') = %(party_type)s and ifnull(party, '') != ''
- and (posting_date < %(from_date)s or ifnull(is_opening, 'No') = 'Yes')
+ and (posting_date < %(from_date)s or (ifnull(is_opening, 'No') = 'Yes' and posting_date <= %(to_date)s))
{account_filter}
group by party""".format(
account_filter=account_filter
),
- {"company": filters.company, "from_date": filters.from_date, "party_type": filters.party_type},
+ {
+ "company": filters.company,
+ "from_date": filters.from_date,
+ "to_date": filters.to_date,
+ "party_type": filters.party_type,
+ },
as_dict=True,
)
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index c5eb7d8..95ba3d8 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -86,7 +86,7 @@
)
)
- query = query.orderby(FY.year_start_date, Order.desc)
+ query = query.orderby(FY.year_start_date, order=Order.desc)
fiscal_years = query.run(as_dict=True)
frappe.cache().hset("fiscal_years", company, fiscal_years)
@@ -648,6 +648,16 @@
(now(), frappe.session.user, ref_doc.doctype, ref_doc.name),
)
+ ple = qb.DocType("Payment Ledger Entry")
+
+ qb.update(ple).set(ple.against_voucher_type, ple.voucher_type).set(
+ ple.against_voucher_no, ple.voucher_no
+ ).set(ple.modified, now()).set(ple.modified_by, frappe.session.user).where(
+ (ple.against_voucher_type == ref_doc.doctype)
+ & (ple.against_voucher_no == ref_doc.name)
+ & (ple.delinked == 0)
+ ).run()
+
if ref_doc.doctype in ("Sales Invoice", "Purchase Invoice"):
ref_doc.set("advances", [])
diff --git a/erpnext/assets/doctype/asset/asset.js b/erpnext/assets/doctype/asset/asset.js
index a43a16c..5512d41 100644
--- a/erpnext/assets/doctype/asset/asset.js
+++ b/erpnext/assets/doctype/asset/asset.js
@@ -230,7 +230,7 @@
datasets: [{
color: 'green',
values: asset_values,
- formatted: asset_values.map(d => d.toFixed(2))
+ formatted: asset_values.map(d => d?.toFixed(2))
}]
},
type: 'line'
diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json
index 991df4e..f0505ff 100644
--- a/erpnext/assets/doctype/asset/asset.json
+++ b/erpnext/assets/doctype/asset/asset.json
@@ -388,7 +388,7 @@
"in_standard_filter": 1,
"label": "Status",
"no_copy": 1,
- "options": "Draft\nSubmitted\nPartially Depreciated\nFully Depreciated\nSold\nScrapped\nIn Maintenance\nOut of Order\nIssue\nReceipt",
+ "options": "Draft\nSubmitted\nPartially Depreciated\nFully Depreciated\nSold\nScrapped\nIn Maintenance\nOut of Order\nIssue\nReceipt\nCapitalized\nDecapitalized",
"read_only": 1
},
{
diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py
index 8ac7ed6..ca6be9b 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -828,7 +828,9 @@
def update_maintenance_status():
- assets = frappe.get_all("Asset", filters={"docstatus": 1, "maintenance_required": 1})
+ assets = frappe.get_all(
+ "Asset", filters={"docstatus": 1, "maintenance_required": 1, "disposal_date": ("is", "not set")}
+ )
for asset in assets:
asset = frappe.get_doc("Asset", asset.name)
diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py
index 7438638..9794170 100644
--- a/erpnext/assets/doctype/asset/depreciation.py
+++ b/erpnext/assets/doctype/asset/depreciation.py
@@ -4,11 +4,12 @@
import frappe
from frappe import _
-from frappe.utils import cint, flt, getdate, today
+from frappe.utils import add_months, cint, flt, getdate, nowdate, today
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_checks_for_pl_and_bs_accounts,
)
+from erpnext.accounts.doctype.journal_entry.journal_entry import make_reverse_journal_entry
def post_depreciation_entries(date=None, commit=True):
@@ -196,6 +197,11 @@
_("Asset {0} cannot be scrapped, as it is already {1}").format(asset.name, asset.status)
)
+ date = today()
+
+ depreciate_asset(asset, date)
+ asset.reload()
+
depreciation_series = frappe.get_cached_value(
"Company", asset.company, "series_for_depreciation_entry"
)
@@ -203,7 +209,7 @@
je = frappe.new_doc("Journal Entry")
je.voucher_type = "Journal Entry"
je.naming_series = depreciation_series
- je.posting_date = today()
+ je.posting_date = date
je.company = asset.company
je.remark = "Scrap Entry for asset {0}".format(asset_name)
@@ -214,7 +220,7 @@
je.flags.ignore_permissions = True
je.submit()
- frappe.db.set_value("Asset", asset_name, "disposal_date", today())
+ frappe.db.set_value("Asset", asset_name, "disposal_date", date)
frappe.db.set_value("Asset", asset_name, "journal_entry_for_scrap", je.name)
asset.set_status("Scrapped")
@@ -225,6 +231,9 @@
def restore_asset(asset_name):
asset = frappe.get_doc("Asset", asset_name)
+ reverse_depreciation_entry_made_after_disposal(asset, asset.disposal_date)
+ reset_depreciation_schedule(asset, asset.disposal_date)
+
je = asset.journal_entry_for_scrap
asset.db_set("disposal_date", None)
@@ -235,7 +244,94 @@
asset.set_status()
-def get_gl_entries_on_asset_regain(asset, selling_amount=0, finance_book=None):
+def depreciate_asset(asset, date):
+ asset.flags.ignore_validate_update_after_submit = True
+ asset.prepare_depreciation_data(date_of_disposal=date)
+ asset.save()
+
+ make_depreciation_entry(asset.name, date)
+
+
+def reset_depreciation_schedule(asset, date):
+ asset.flags.ignore_validate_update_after_submit = True
+
+ # recreate original depreciation schedule of the asset
+ asset.prepare_depreciation_data(date_of_return=date)
+
+ modify_depreciation_schedule_for_asset_repairs(asset)
+ asset.save()
+
+
+def modify_depreciation_schedule_for_asset_repairs(asset):
+ asset_repairs = frappe.get_all(
+ "Asset Repair", filters={"asset": asset.name}, fields=["name", "increase_in_asset_life"]
+ )
+
+ for repair in asset_repairs:
+ if repair.increase_in_asset_life:
+ asset_repair = frappe.get_doc("Asset Repair", repair.name)
+ asset_repair.modify_depreciation_schedule()
+ asset.prepare_depreciation_data()
+
+
+def reverse_depreciation_entry_made_after_disposal(asset, date):
+ row = -1
+ finance_book = asset.get("schedules")[0].get("finance_book")
+ for schedule in asset.get("schedules"):
+ if schedule.finance_book != finance_book:
+ row = 0
+ finance_book = schedule.finance_book
+ else:
+ row += 1
+
+ if schedule.schedule_date == date:
+ if not disposal_was_made_on_original_schedule_date(
+ asset, schedule, row, date
+ ) or disposal_happens_in_the_future(date):
+
+ reverse_journal_entry = make_reverse_journal_entry(schedule.journal_entry)
+ reverse_journal_entry.posting_date = nowdate()
+ frappe.flags.is_reverse_depr_entry = True
+ reverse_journal_entry.submit()
+
+ frappe.flags.is_reverse_depr_entry = False
+ asset.flags.ignore_validate_update_after_submit = True
+ schedule.journal_entry = None
+ depreciation_amount = get_depreciation_amount_in_je(reverse_journal_entry)
+ asset.finance_books[0].value_after_depreciation += depreciation_amount
+ asset.save()
+
+
+def get_depreciation_amount_in_je(journal_entry):
+ if journal_entry.accounts[0].debit_in_account_currency:
+ return journal_entry.accounts[0].debit_in_account_currency
+ else:
+ return journal_entry.accounts[0].credit_in_account_currency
+
+
+# if the invoice had been posted on the date the depreciation was initially supposed to happen, the depreciation shouldn't be undone
+def disposal_was_made_on_original_schedule_date(asset, schedule, row, posting_date_of_disposal):
+ for finance_book in asset.get("finance_books"):
+ if schedule.finance_book == finance_book.finance_book:
+ orginal_schedule_date = add_months(
+ finance_book.depreciation_start_date, row * cint(finance_book.frequency_of_depreciation)
+ )
+
+ if orginal_schedule_date == posting_date_of_disposal:
+ return True
+ return False
+
+
+def disposal_happens_in_the_future(posting_date_of_disposal):
+ if posting_date_of_disposal > getdate():
+ return True
+
+ return False
+
+
+def get_gl_entries_on_asset_regain(
+ asset, selling_amount=0, finance_book=None, voucher_type=None, voucher_no=None
+):
(
fixed_asset_account,
asset,
@@ -247,28 +343,45 @@
) = get_asset_details(asset, finance_book)
gl_entries = [
- {
- "account": fixed_asset_account,
- "debit_in_account_currency": asset.gross_purchase_amount,
- "debit": asset.gross_purchase_amount,
- "cost_center": depreciation_cost_center,
- },
- {
- "account": accumulated_depr_account,
- "credit_in_account_currency": accumulated_depr_amount,
- "credit": accumulated_depr_amount,
- "cost_center": depreciation_cost_center,
- },
+ asset.get_gl_dict(
+ {
+ "account": fixed_asset_account,
+ "debit_in_account_currency": asset.gross_purchase_amount,
+ "debit": asset.gross_purchase_amount,
+ "cost_center": depreciation_cost_center,
+ "posting_date": getdate(),
+ },
+ item=asset,
+ ),
+ asset.get_gl_dict(
+ {
+ "account": accumulated_depr_account,
+ "credit_in_account_currency": accumulated_depr_amount,
+ "credit": accumulated_depr_amount,
+ "cost_center": depreciation_cost_center,
+ "posting_date": getdate(),
+ },
+ item=asset,
+ ),
]
profit_amount = abs(flt(value_after_depreciation)) - abs(flt(selling_amount))
if profit_amount:
- get_profit_gl_entries(profit_amount, gl_entries, disposal_account, depreciation_cost_center)
+ get_profit_gl_entries(
+ asset, profit_amount, gl_entries, disposal_account, depreciation_cost_center
+ )
+
+ if voucher_type and voucher_no:
+ for entry in gl_entries:
+ entry["voucher_type"] = voucher_type
+ entry["voucher_no"] = voucher_no
return gl_entries
-def get_gl_entries_on_asset_disposal(asset, selling_amount=0, finance_book=None):
+def get_gl_entries_on_asset_disposal(
+ asset, selling_amount=0, finance_book=None, voucher_type=None, voucher_no=None
+):
(
fixed_asset_account,
asset,
@@ -280,23 +393,38 @@
) = get_asset_details(asset, finance_book)
gl_entries = [
- {
- "account": fixed_asset_account,
- "credit_in_account_currency": asset.gross_purchase_amount,
- "credit": asset.gross_purchase_amount,
- "cost_center": depreciation_cost_center,
- },
- {
- "account": accumulated_depr_account,
- "debit_in_account_currency": accumulated_depr_amount,
- "debit": accumulated_depr_amount,
- "cost_center": depreciation_cost_center,
- },
+ asset.get_gl_dict(
+ {
+ "account": fixed_asset_account,
+ "credit_in_account_currency": asset.gross_purchase_amount,
+ "credit": asset.gross_purchase_amount,
+ "cost_center": depreciation_cost_center,
+ "posting_date": getdate(),
+ },
+ item=asset,
+ ),
+ asset.get_gl_dict(
+ {
+ "account": accumulated_depr_account,
+ "debit_in_account_currency": accumulated_depr_amount,
+ "debit": accumulated_depr_amount,
+ "cost_center": depreciation_cost_center,
+ "posting_date": getdate(),
+ },
+ item=asset,
+ ),
]
profit_amount = flt(selling_amount) - flt(value_after_depreciation)
if profit_amount:
- get_profit_gl_entries(profit_amount, gl_entries, disposal_account, depreciation_cost_center)
+ get_profit_gl_entries(
+ asset, profit_amount, gl_entries, disposal_account, depreciation_cost_center
+ )
+
+ if voucher_type and voucher_no:
+ for entry in gl_entries:
+ entry["voucher_type"] = voucher_type
+ entry["voucher_no"] = voucher_no
return gl_entries
@@ -333,15 +461,21 @@
)
-def get_profit_gl_entries(profit_amount, gl_entries, disposal_account, depreciation_cost_center):
+def get_profit_gl_entries(
+ asset, profit_amount, gl_entries, disposal_account, depreciation_cost_center
+):
debit_or_credit = "debit" if profit_amount < 0 else "credit"
gl_entries.append(
- {
- "account": disposal_account,
- "cost_center": depreciation_cost_center,
- debit_or_credit: abs(profit_amount),
- debit_or_credit + "_in_account_currency": abs(profit_amount),
- }
+ asset.get_gl_dict(
+ {
+ "account": disposal_account,
+ "cost_center": depreciation_cost_center,
+ debit_or_credit: abs(profit_amount),
+ debit_or_credit + "_in_account_currency": abs(profit_amount),
+ "posting_date": getdate(),
+ },
+ item=asset,
+ )
)
diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py
index e7af9bd..370b13b 100644
--- a/erpnext/assets/doctype/asset/test_asset.py
+++ b/erpnext/assets/doctype/asset/test_asset.py
@@ -4,10 +4,23 @@
import unittest
import frappe
-from frappe.utils import add_days, add_months, cstr, flt, get_last_day, getdate, nowdate
+from frappe.utils import (
+ add_days,
+ add_months,
+ cstr,
+ flt,
+ get_first_day,
+ get_last_day,
+ getdate,
+ nowdate,
+)
from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import make_purchase_invoice
-from erpnext.assets.doctype.asset.asset import make_sales_invoice, split_asset
+from erpnext.assets.doctype.asset.asset import (
+ make_sales_invoice,
+ split_asset,
+ update_maintenance_status,
+)
from erpnext.assets.doctype.asset.depreciation import (
post_depreciation_entries,
restore_asset,
@@ -178,28 +191,48 @@
self.assertEqual(doc.items[0].is_fixed_asset, 1)
def test_scrap_asset(self):
+ date = nowdate()
+ purchase_date = add_months(get_first_day(date), -2)
+
asset = create_asset(
calculate_depreciation=1,
- available_for_use_date="2020-01-01",
- purchase_date="2020-01-01",
+ available_for_use_date=purchase_date,
+ purchase_date=purchase_date,
expected_value_after_useful_life=10000,
total_number_of_depreciations=10,
frequency_of_depreciation=1,
submit=1,
)
- post_depreciation_entries(date=add_months("2020-01-01", 4))
+ post_depreciation_entries(date=add_months(purchase_date, 2))
+ asset.load_from_db()
+
+ accumulated_depr_amount = flt(
+ asset.gross_purchase_amount - asset.finance_books[0].value_after_depreciation,
+ asset.precision("gross_purchase_amount"),
+ )
+ self.assertEquals(accumulated_depr_amount, 18000.0)
scrap_asset(asset.name)
-
asset.load_from_db()
+
+ accumulated_depr_amount = flt(
+ asset.gross_purchase_amount - asset.finance_books[0].value_after_depreciation,
+ asset.precision("gross_purchase_amount"),
+ )
+ pro_rata_amount, _, _ = asset.get_pro_rata_amt(
+ asset.finance_books[0], 9000, add_months(get_last_day(purchase_date), 1), date
+ )
+ pro_rata_amount = flt(pro_rata_amount, asset.precision("gross_purchase_amount"))
+ self.assertEquals(accumulated_depr_amount, 18000.00 + pro_rata_amount)
+
self.assertEqual(asset.status, "Scrapped")
self.assertTrue(asset.journal_entry_for_scrap)
expected_gle = (
- ("_Test Accumulated Depreciations - _TC", 36000.0, 0.0),
+ ("_Test Accumulated Depreciations - _TC", 18000.0 + pro_rata_amount, 0.0),
("_Test Fixed Asset - _TC", 0.0, 100000.0),
- ("_Test Gain/Loss on Asset Disposal - _TC", 64000.0, 0.0),
+ ("_Test Gain/Loss on Asset Disposal - _TC", 82000.0 - pro_rata_amount, 0.0),
)
gle = frappe.db.sql(
@@ -216,7 +249,64 @@
self.assertFalse(asset.journal_entry_for_scrap)
self.assertEqual(asset.status, "Partially Depreciated")
+ accumulated_depr_amount = flt(
+ asset.gross_purchase_amount - asset.finance_books[0].value_after_depreciation,
+ asset.precision("gross_purchase_amount"),
+ )
+ this_month_depr_amount = 9000.0 if get_last_day(date) == date else 0
+
+ self.assertEquals(accumulated_depr_amount, 18000.0 + this_month_depr_amount)
+
def test_gle_made_by_asset_sale(self):
+ date = nowdate()
+ purchase_date = add_months(get_first_day(date), -2)
+
+ asset = create_asset(
+ calculate_depreciation=1,
+ available_for_use_date=purchase_date,
+ purchase_date=purchase_date,
+ expected_value_after_useful_life=10000,
+ total_number_of_depreciations=10,
+ frequency_of_depreciation=1,
+ submit=1,
+ )
+
+ post_depreciation_entries(date=add_months(purchase_date, 2))
+
+ si = make_sales_invoice(asset=asset.name, item_code="Macbook Pro", company="_Test Company")
+ si.customer = "_Test Customer"
+ si.due_date = nowdate()
+ si.get("items")[0].rate = 25000
+ si.insert()
+ si.submit()
+
+ self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Sold")
+
+ pro_rata_amount, _, _ = asset.get_pro_rata_amt(
+ asset.finance_books[0], 9000, add_months(get_last_day(purchase_date), 1), date
+ )
+ pro_rata_amount = flt(pro_rata_amount, asset.precision("gross_purchase_amount"))
+
+ expected_gle = (
+ ("_Test Accumulated Depreciations - _TC", 18000.0 + pro_rata_amount, 0.0),
+ ("_Test Fixed Asset - _TC", 0.0, 100000.0),
+ ("_Test Gain/Loss on Asset Disposal - _TC", 57000.0 - pro_rata_amount, 0.0),
+ ("Debtors - _TC", 25000.0, 0.0),
+ )
+
+ gle = frappe.db.sql(
+ """select account, debit, credit from `tabGL Entry`
+ where voucher_type='Sales Invoice' and voucher_no = %s
+ order by account""",
+ si.name,
+ )
+
+ self.assertSequenceEqual(gle, expected_gle)
+
+ si.cancel()
+ self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Partially Depreciated")
+
+ def test_asset_with_maintenance_required_status_after_sale(self):
asset = create_asset(
calculate_depreciation=1,
available_for_use_date="2020-06-06",
@@ -224,6 +314,7 @@
expected_value_after_useful_life=10000,
total_number_of_depreciations=3,
frequency_of_depreciation=10,
+ maintenance_required=1,
depreciation_start_date="2020-12-31",
submit=1,
)
@@ -239,24 +330,9 @@
self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Sold")
- expected_gle = (
- ("_Test Accumulated Depreciations - _TC", 20490.2, 0.0),
- ("_Test Fixed Asset - _TC", 0.0, 100000.0),
- ("_Test Gain/Loss on Asset Disposal - _TC", 54509.8, 0.0),
- ("Debtors - _TC", 25000.0, 0.0),
- )
+ update_maintenance_status()
- gle = frappe.db.sql(
- """select account, debit, credit from `tabGL Entry`
- where voucher_type='Sales Invoice' and voucher_no = %s
- order by account""",
- si.name,
- )
-
- self.assertSequenceEqual(gle, expected_gle)
-
- si.cancel()
- self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Partially Depreciated")
+ self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Sold")
def test_asset_splitting(self):
asset = create_asset(
@@ -1376,6 +1452,7 @@
"number_of_depreciations_booked": args.number_of_depreciations_booked or 0,
"gross_purchase_amount": args.gross_purchase_amount or 100000,
"purchase_receipt_amount": args.purchase_receipt_amount or 100000,
+ "maintenance_required": args.maintenance_required or 0,
"warehouse": args.warehouse or "_Test Warehouse - _TC",
"available_for_use_date": args.available_for_use_date or "2020-06-06",
"location": args.location or "Test Location",
diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
index 2e6f0ad..08355f0 100644
--- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
+++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
@@ -12,8 +12,11 @@
import erpnext
from erpnext.assets.doctype.asset.depreciation import (
+ depreciate_asset,
get_gl_entries_on_asset_disposal,
get_value_after_depreciation_on_disposal_date,
+ reset_depreciation_schedule,
+ reverse_depreciation_entry_made_after_disposal,
)
from erpnext.assets.doctype.asset_category.asset_category import get_asset_category_account
from erpnext.assets.doctype.asset_value_adjustment.asset_value_adjustment import (
@@ -424,11 +427,15 @@
asset = self.get_asset(item)
if asset.calculate_depreciation:
- self.depreciate_asset(asset)
+ depreciate_asset(asset, self.posting_date)
asset.reload()
fixed_asset_gl_entries = get_gl_entries_on_asset_disposal(
- asset, item.asset_value, item.get("finance_book") or self.get("finance_book")
+ asset,
+ item.asset_value,
+ item.get("finance_book") or self.get("finance_book"),
+ self.get("doctype"),
+ self.get("name"),
)
asset.db_set("disposal_date", self.posting_date)
@@ -516,8 +523,8 @@
self.set_consumed_asset_status(asset)
if asset.calculate_depreciation:
- self.reverse_depreciation_entry_made_after_disposal(asset)
- self.reset_depreciation_schedule(asset)
+ reverse_depreciation_entry_made_after_disposal(asset, self.posting_date)
+ reset_depreciation_schedule(asset, self.posting_date)
def get_asset(self, item):
asset = frappe.get_doc("Asset", item.asset)
diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.json b/erpnext/buying/doctype/buying_settings/buying_settings.json
index aad2607..28158a3 100644
--- a/erpnext/buying/doctype/buying_settings/buying_settings.json
+++ b/erpnext/buying/doctype/buying_settings/buying_settings.json
@@ -20,7 +20,6 @@
"maintain_same_rate",
"allow_multiple_items",
"bill_for_rejected_quantity_in_purchase_invoice",
- "enable_discount_accounting",
"subcontract",
"backflush_raw_materials_of_subcontract_based_on",
"column_break_11",
@@ -134,13 +133,6 @@
{
"fieldname": "column_break_12",
"fieldtype": "Column Break"
- },
- {
- "default": "0",
- "description": "If enabled, additional ledger entries will be made for discounts in a separate Discount Account",
- "fieldname": "enable_discount_accounting",
- "fieldtype": "Check",
- "label": "Enable Discount Accounting for Buying"
}
],
"icon": "fa fa-cog",
@@ -148,7 +140,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
- "modified": "2022-09-01 18:01:34.994657",
+ "modified": "2022-09-27 10:50:27.050252",
"modified_by": "Administrator",
"module": "Buying",
"name": "Buying Settings",
diff --git a/erpnext/buying/doctype/buying_settings/buying_settings.py b/erpnext/buying/doctype/buying_settings/buying_settings.py
index 7b18cdb..be1ebde 100644
--- a/erpnext/buying/doctype/buying_settings/buying_settings.py
+++ b/erpnext/buying/doctype/buying_settings/buying_settings.py
@@ -5,15 +5,10 @@
import frappe
-from frappe.custom.doctype.property_setter.property_setter import make_property_setter
from frappe.model.document import Document
-from frappe.utils import cint
class BuyingSettings(Document):
- def on_update(self):
- self.toggle_discount_accounting_fields()
-
def validate(self):
for key in ["supplier_group", "supp_master_name", "maintain_same_rate", "buying_price_list"]:
frappe.db.set_default(key, self.get(key, ""))
@@ -26,60 +21,3 @@
self.get("supp_master_name") == "Naming Series",
hide_name_field=False,
)
-
- def toggle_discount_accounting_fields(self):
- enable_discount_accounting = cint(self.enable_discount_accounting)
-
- make_property_setter(
- "Purchase Invoice Item",
- "discount_account",
- "hidden",
- not (enable_discount_accounting),
- "Check",
- validate_fields_for_doctype=False,
- )
- if enable_discount_accounting:
- make_property_setter(
- "Purchase Invoice Item",
- "discount_account",
- "mandatory_depends_on",
- "eval: doc.discount_amount",
- "Code",
- validate_fields_for_doctype=False,
- )
- else:
- make_property_setter(
- "Purchase Invoice Item",
- "discount_account",
- "mandatory_depends_on",
- "",
- "Code",
- validate_fields_for_doctype=False,
- )
-
- make_property_setter(
- "Purchase Invoice",
- "additional_discount_account",
- "hidden",
- not (enable_discount_accounting),
- "Check",
- validate_fields_for_doctype=False,
- )
- if enable_discount_accounting:
- make_property_setter(
- "Purchase Invoice",
- "additional_discount_account",
- "mandatory_depends_on",
- "eval: doc.discount_amount",
- "Code",
- validate_fields_for_doctype=False,
- )
- else:
- make_property_setter(
- "Purchase Invoice",
- "additional_discount_account",
- "mandatory_depends_on",
- "",
- "Code",
- validate_fields_for_doctype=False,
- )
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.js b/erpnext/buying/doctype/purchase_order/purchase_order.js
index fc99d77..ddf81ca 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.js
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.js
@@ -33,6 +33,7 @@
frm.set_query("fg_item", "items", function() {
return {
filters: {
+ 'is_stock_item': 1,
'is_sub_contracted_item': 1,
'default_bom': ['!=', '']
}
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py
index cd58d25..bcedd4d 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.py
@@ -349,7 +349,7 @@
update_linked_doc(self.doctype, self.name, self.inter_company_order_reference)
def on_cancel(self):
- self.ignore_linked_doctypes = "Payment Ledger Entry"
+ self.ignore_linked_doctypes = ("GL Entry", "Payment Ledger Entry")
super(PurchaseOrder, self).on_cancel()
if self.is_against_so():
diff --git a/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py b/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py
index dbdc62e..d089473 100644
--- a/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py
+++ b/erpnext/buying/report/purchase_order_trends/purchase_order_trends.py
@@ -53,4 +53,5 @@
},
"type": "line",
"lineOptions": {"regionFill": 1},
+ "fieldtype": "Currency",
}
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 8686cb5..22291a3 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -38,7 +38,6 @@
validate_party_frozen_disabled,
)
from erpnext.accounts.utils import get_account_currency, get_fiscal_years, validate_fiscal_year
-from erpnext.assets.doctype.asset.depreciation import make_depreciation_entry
from erpnext.buying.utils import update_last_purchase_rate
from erpnext.controllers.print_settings import (
set_print_templates_for_item_table,
@@ -1891,88 +1890,6 @@
_("Select finance book for the item {0} at row {1}").format(item.item_code, item.idx)
)
- def depreciate_asset(self, asset):
- asset.flags.ignore_validate_update_after_submit = True
- asset.prepare_depreciation_data(date_of_disposal=self.posting_date)
- asset.save()
-
- make_depreciation_entry(asset.name, self.posting_date)
-
- def reset_depreciation_schedule(self, asset):
- asset.flags.ignore_validate_update_after_submit = True
-
- # recreate original depreciation schedule of the asset
- asset.prepare_depreciation_data(date_of_return=self.posting_date)
-
- self.modify_depreciation_schedule_for_asset_repairs(asset)
- asset.save()
-
- def modify_depreciation_schedule_for_asset_repairs(self, asset):
- asset_repairs = frappe.get_all(
- "Asset Repair", filters={"asset": asset.name}, fields=["name", "increase_in_asset_life"]
- )
-
- for repair in asset_repairs:
- if repair.increase_in_asset_life:
- asset_repair = frappe.get_doc("Asset Repair", repair.name)
- asset_repair.modify_depreciation_schedule()
- asset.prepare_depreciation_data()
-
- def reverse_depreciation_entry_made_after_disposal(self, asset):
- from erpnext.accounts.doctype.journal_entry.journal_entry import make_reverse_journal_entry
-
- posting_date_of_original_disposal = self.get_posting_date_of_disposal_entry()
-
- row = -1
- finance_book = asset.get("schedules")[0].get("finance_book")
- for schedule in asset.get("schedules"):
- if schedule.finance_book != finance_book:
- row = 0
- finance_book = schedule.finance_book
- else:
- row += 1
-
- if schedule.schedule_date == posting_date_of_original_disposal:
- if not self.disposal_was_made_on_original_schedule_date(
- asset, schedule, row, posting_date_of_original_disposal
- ) or self.disposal_happens_in_the_future(posting_date_of_original_disposal):
-
- reverse_journal_entry = make_reverse_journal_entry(schedule.journal_entry)
- reverse_journal_entry.posting_date = nowdate()
- frappe.flags.is_reverse_depr_entry = True
- reverse_journal_entry.submit()
-
- frappe.flags.is_reverse_depr_entry = False
- asset.flags.ignore_validate_update_after_submit = True
- schedule.journal_entry = None
- asset.save()
-
- def get_posting_date_of_disposal_entry(self):
- if self.doctype == "Sales Invoice" and self.return_against:
- return frappe.db.get_value("Sales Invoice", self.return_against, "posting_date")
- else:
- return self.posting_date
-
- # if the invoice had been posted on the date the depreciation was initially supposed to happen, the depreciation shouldn't be undone
- def disposal_was_made_on_original_schedule_date(
- self, asset, schedule, row, posting_date_of_disposal
- ):
- for finance_book in asset.get("finance_books"):
- if schedule.finance_book == finance_book.finance_book:
- orginal_schedule_date = add_months(
- finance_book.depreciation_start_date, row * cint(finance_book.frequency_of_depreciation)
- )
-
- if orginal_schedule_date == posting_date_of_disposal:
- return True
- return False
-
- def disposal_happens_in_the_future(self, posting_date_of_disposal):
- if posting_date_of_disposal > getdate():
- return True
-
- return False
-
@frappe.whitelist()
def get_tax_rate(account_head):
diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py
index 4f8b5c7..8eae0a0 100644
--- a/erpnext/controllers/queries.py
+++ b/erpnext/controllers/queries.py
@@ -212,21 +212,15 @@
meta = frappe.get_meta(doctype, cached=True)
searchfields = meta.get_search_fields()
- # these are handled separately
- ignored_search_fields = ("item_name", "description")
- for ignored_field in ignored_search_fields:
- if ignored_field in searchfields:
- searchfields.remove(ignored_field)
-
columns = ""
- extra_searchfields = [
- field
- for field in searchfields
- if not field in ["name", "item_group", "description", "item_name"]
- ]
+ extra_searchfields = [field for field in searchfields if not field in ["name", "description"]]
if extra_searchfields:
- columns = ", " + ", ".join(extra_searchfields)
+ columns += ", " + ", ".join(extra_searchfields)
+
+ if "description" in searchfields:
+ columns += """, if(length(tabItem.description) > 40, \
+ concat(substr(tabItem.description, 1, 40), "..."), description) as description"""
searchfields = searchfields + [
field
@@ -266,12 +260,10 @@
if frappe.db.count(doctype, cache=True) < 50000:
# scan description only if items are less than 50000
description_cond = "or tabItem.description LIKE %(txt)s"
+
return frappe.db.sql(
"""select
- tabItem.name, tabItem.item_name, tabItem.item_group,
- if(length(tabItem.description) > 40, \
- concat(substr(tabItem.description, 1, 40), "..."), description) as description
- {columns}
+ tabItem.name {columns}
from tabItem
where tabItem.docstatus < 2
and tabItem.disabled=0
diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py
index 202a880..aa4468c 100644
--- a/erpnext/controllers/subcontracting_controller.py
+++ b/erpnext/controllers/subcontracting_controller.py
@@ -69,9 +69,18 @@
def validate_items(self):
for item in self.items:
- if not frappe.get_value("Item", item.item_code, "is_sub_contracted_item"):
+ is_stock_item, is_sub_contracted_item = frappe.get_value(
+ "Item", item.item_code, ["is_stock_item", "is_sub_contracted_item"]
+ )
+
+ if not is_stock_item:
+ msg = f"Item {item.item_name} must be a stock item."
+ frappe.throw(_(msg))
+
+ if not is_sub_contracted_item:
msg = f"Item {item.item_name} must be a subcontracted item."
frappe.throw(_(msg))
+
if item.bom:
bom = frappe.get_doc("BOM", item.bom)
if not bom.is_active:
@@ -841,7 +850,7 @@
for fg_item_code in fg_item_code_list:
for rm_item in rm_items:
- if rm_item.get("main_item_code") or rm_item.get("item_code") == fg_item_code:
+ if rm_item.get("main_item_code") == fg_item_code or rm_item.get("item_code") == fg_item_code:
rm_item_code = rm_item.get("rm_item_code")
items_dict = {
diff --git a/erpnext/crm/report/opportunity_summary_by_sales_stage/opportunity_summary_by_sales_stage.js b/erpnext/crm/report/opportunity_summary_by_sales_stage/opportunity_summary_by_sales_stage.js
index 116db2f..7cd1710 100644
--- a/erpnext/crm/report/opportunity_summary_by_sales_stage/opportunity_summary_by_sales_stage.js
+++ b/erpnext/crm/report/opportunity_summary_by_sales_stage/opportunity_summary_by_sales_stage.js
@@ -44,7 +44,7 @@
},
{
fieldname: "opportunity_source",
- label: __("Oppoturnity Source"),
+ label: __("Opportunity Source"),
fieldtype: "Link",
options: "Lead Source",
},
@@ -62,4 +62,4 @@
default: frappe.defaults.get_user_default("Company")
}
]
-};
\ No newline at end of file
+};
diff --git a/erpnext/e_commerce/doctype/website_item/website_item.json b/erpnext/e_commerce/doctype/website_item/website_item.json
index c5775ee..6556eab 100644
--- a/erpnext/e_commerce/doctype/website_item/website_item.json
+++ b/erpnext/e_commerce/doctype/website_item/website_item.json
@@ -188,7 +188,8 @@
"in_list_view": 1,
"label": "Item Group",
"options": "Item Group",
- "read_only": 1
+ "read_only": 1,
+ "search_index": 1
},
{
"default": "1",
@@ -234,7 +235,8 @@
"fieldname": "brand",
"fieldtype": "Link",
"label": "Brand",
- "options": "Brand"
+ "options": "Brand",
+ "search_index": 1
},
{
"collapsible": 1,
@@ -346,7 +348,7 @@
"index_web_pages_for_search": 1,
"links": [],
"make_attachments_public": 1,
- "modified": "2022-09-13 04:05:11.614087",
+ "modified": "2022-09-30 04:01:52.090732",
"modified_by": "Administrator",
"module": "E-commerce",
"name": "Website Item",
diff --git a/erpnext/e_commerce/doctype/website_item/website_item.py b/erpnext/e_commerce/doctype/website_item/website_item.py
index c0f8c79..3e5d5f7 100644
--- a/erpnext/e_commerce/doctype/website_item/website_item.py
+++ b/erpnext/e_commerce/doctype/website_item/website_item.py
@@ -403,9 +403,6 @@
# since route is a Text column, it needs a length for indexing
frappe.db.add_index("Website Item", ["route(500)"])
- frappe.db.add_index("Website Item", ["item_group"])
- frappe.db.add_index("Website Item", ["brand"])
-
def check_if_user_is_customer(user=None):
from frappe.contacts.doctype.contact.contact import get_contact_name
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index a08feb4..b8f51f8 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -391,12 +391,12 @@
"erpnext.crm.doctype.social_media_post.social_media_post.process_scheduled_social_media_posts",
],
"hourly": [
- "erpnext.accounts.doctype.subscription.subscription.process_all",
"erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.automatic_synchronization",
"erpnext.projects.doctype.project.project.hourly_reminder",
"erpnext.projects.doctype.project.project.collect_project_status",
],
"hourly_long": [
+ "erpnext.accounts.doctype.subscription.subscription.process_all",
"erpnext.stock.doctype.repost_item_valuation.repost_item_valuation.repost_entries",
"erpnext.bulk_transaction.doctype.bulk_transaction_log.bulk_transaction_log.retry_failing_transaction",
],
@@ -508,6 +508,7 @@
"Landed Cost Item",
"Asset Value Adjustment",
"Asset Repair",
+ "Asset Capitalization",
"Loyalty Program",
"Stock Reconciliation",
"POS Profile",
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index 70637d3..ff84991 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -1019,7 +1019,6 @@
where
bom_item.docstatus < 2
and bom.name = %(bom)s
- and ifnull(item.has_variants, 0) = 0
and item.is_stock_item in (1, {is_stock_item})
{where_conditions}
group by item_code, stock_uom
diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py
index a53c42c..804f03d 100644
--- a/erpnext/manufacturing/doctype/work_order/test_work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py
@@ -17,6 +17,7 @@
close_work_order,
make_job_card,
make_stock_entry,
+ make_stock_return_entry,
stop_unstop,
)
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
@@ -1408,6 +1409,77 @@
)
self.assertEqual(manufacture_ste_doc2.items[1].qty, 1)
+ def test_non_consumed_material_return_against_work_order(self):
+ frappe.db.set_value(
+ "Manufacturing Settings",
+ None,
+ "backflush_raw_materials_based_on",
+ "Material Transferred for Manufacture",
+ )
+
+ item = make_item(
+ "Test FG Item To Test Return Case",
+ {
+ "is_stock_item": 1,
+ },
+ )
+
+ item_code = item.name
+ bom_doc = make_bom(
+ item=item_code,
+ source_warehouse="Stores - _TC",
+ raw_materials=["Test Batch MCC Keyboard", "Test Serial No BTT Headphone"],
+ )
+
+ # Create a work order
+ wo_doc = make_wo_order_test_record(production_item=item_code, qty=5)
+ wo_doc.save()
+
+ self.assertEqual(wo_doc.bom_no, bom_doc.name)
+
+ # Transfer material for manufacture
+ ste_doc = frappe.get_doc(make_stock_entry(wo_doc.name, "Material Transfer for Manufacture", 5))
+ for row in ste_doc.items:
+ row.qty += 2
+ row.transfer_qty += 2
+ nste_doc = test_stock_entry.make_stock_entry(
+ item_code=row.item_code, target="Stores - _TC", qty=row.qty, basic_rate=100
+ )
+
+ row.batch_no = nste_doc.items[0].batch_no
+ row.serial_no = nste_doc.items[0].serial_no
+
+ ste_doc.save()
+ ste_doc.submit()
+ ste_doc.load_from_db()
+
+ # Create a stock entry to manufacture the item
+ ste_doc = frappe.get_doc(make_stock_entry(wo_doc.name, "Manufacture", 5))
+ for row in ste_doc.items:
+ if row.s_warehouse and not row.t_warehouse:
+ row.qty -= 2
+ row.transfer_qty -= 2
+
+ if row.serial_no:
+ serial_nos = get_serial_nos(row.serial_no)
+ row.serial_no = "\n".join(serial_nos[0:5])
+
+ ste_doc.save()
+ ste_doc.submit()
+
+ wo_doc.load_from_db()
+ for row in wo_doc.required_items:
+ self.assertEqual(row.transferred_qty, 7)
+ self.assertEqual(row.consumed_qty, 5)
+
+ self.assertEqual(wo_doc.status, "Completed")
+ return_ste_doc = make_stock_return_entry(wo_doc.name)
+ return_ste_doc.save()
+
+ self.assertTrue(return_ste_doc.is_return)
+ for row in return_ste_doc.items:
+ self.assertEqual(row.qty, 2)
+
def prepare_data_for_backflush_based_on_materials_transferred():
batch_item_doc = make_item(
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js
index 20f1503..4aab3fa 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.js
+++ b/erpnext/manufacturing/doctype/work_order/work_order.js
@@ -180,6 +180,37 @@
frm.trigger("make_bom");
});
}
+
+ frm.trigger("add_custom_button_to_return_components");
+ },
+
+ add_custom_button_to_return_components: function(frm) {
+ if (frm.doc.docstatus === 1 && in_list(["Closed", "Completed"], frm.doc.status)) {
+ let non_consumed_items = frm.doc.required_items.filter(d =>{
+ return flt(d.consumed_qty) < flt(d.transferred_qty - d.returned_qty)
+ });
+
+ if (non_consumed_items && non_consumed_items.length) {
+ frm.add_custom_button(__("Return Components"), function() {
+ frm.trigger("create_stock_return_entry");
+ }).addClass("btn-primary");
+ }
+ }
+ },
+
+ create_stock_return_entry: function(frm) {
+ frappe.call({
+ method: "erpnext.manufacturing.doctype.work_order.work_order.make_stock_return_entry",
+ args: {
+ "work_order": frm.doc.name,
+ },
+ callback: function(r) {
+ if(!r.exc) {
+ let doc = frappe.model.sync(r.message);
+ frappe.set_route("Form", doc[0].doctype, doc[0].name);
+ }
+ }
+ });
},
make_job_card: function(frm) {
@@ -517,7 +548,8 @@
erpnext.work_order = {
set_custom_buttons: function(frm) {
var doc = frm.doc;
- if (doc.docstatus === 1 && doc.status != "Closed") {
+
+ if (doc.status !== "Closed") {
frm.add_custom_button(__('Close'), function() {
frappe.confirm(__("Once the Work Order is Closed. It can't be resumed."),
() => {
@@ -525,7 +557,9 @@
}
);
}, __("Status"));
+ }
+ if (doc.docstatus === 1 && !in_list(["Closed", "Completed"], doc.status)) {
if (doc.status != 'Stopped' && doc.status != 'Completed') {
frm.add_custom_button(__('Stop'), function() {
erpnext.work_order.change_work_order_status(frm, "Stopped");
@@ -557,37 +591,52 @@
if(!frm.doc.skip_transfer){
// If "Material Consumption is check in Manufacturing Settings, allow Material Consumption
- if ((flt(doc.produced_qty) < flt(doc.material_transferred_for_manufacturing))
- && frm.doc.status != 'Stopped') {
- frm.has_finish_btn = true;
+ if (flt(doc.material_transferred_for_manufacturing) > 0 && frm.doc.status != 'Stopped') {
+ if ((flt(doc.produced_qty) < flt(doc.material_transferred_for_manufacturing))) {
+ frm.has_finish_btn = true;
- if (frm.doc.__onload && frm.doc.__onload.material_consumption == 1) {
- // Only show "Material Consumption" when required_qty > consumed_qty
- var counter = 0;
- var tbl = frm.doc.required_items || [];
- var tbl_lenght = tbl.length;
- for (var i = 0, len = tbl_lenght; i < len; i++) {
- let wo_item_qty = frm.doc.required_items[i].transferred_qty || frm.doc.required_items[i].required_qty;
- if (flt(wo_item_qty) > flt(frm.doc.required_items[i].consumed_qty)) {
- counter += 1;
+ if (frm.doc.__onload && frm.doc.__onload.material_consumption == 1) {
+ // Only show "Material Consumption" when required_qty > consumed_qty
+ var counter = 0;
+ var tbl = frm.doc.required_items || [];
+ var tbl_lenght = tbl.length;
+ for (var i = 0, len = tbl_lenght; i < len; i++) {
+ let wo_item_qty = frm.doc.required_items[i].transferred_qty || frm.doc.required_items[i].required_qty;
+ if (flt(wo_item_qty) > flt(frm.doc.required_items[i].consumed_qty)) {
+ counter += 1;
+ }
+ }
+ if (counter > 0) {
+ var consumption_btn = frm.add_custom_button(__('Material Consumption'), function() {
+ const backflush_raw_materials_based_on = frm.doc.__onload.backflush_raw_materials_based_on;
+ erpnext.work_order.make_consumption_se(frm, backflush_raw_materials_based_on);
+ });
+ consumption_btn.addClass('btn-primary');
}
}
- if (counter > 0) {
- var consumption_btn = frm.add_custom_button(__('Material Consumption'), function() {
- const backflush_raw_materials_based_on = frm.doc.__onload.backflush_raw_materials_based_on;
- erpnext.work_order.make_consumption_se(frm, backflush_raw_materials_based_on);
- });
- consumption_btn.addClass('btn-primary');
+
+ var finish_btn = frm.add_custom_button(__('Finish'), function() {
+ erpnext.work_order.make_se(frm, 'Manufacture');
+ });
+
+ if(doc.material_transferred_for_manufacturing>=doc.qty) {
+ // all materials transferred for manufacturing, make this primary
+ finish_btn.addClass('btn-primary');
}
- }
+ } else {
+ frappe.db.get_doc("Manufacturing Settings").then((doc) => {
+ let allowance_percentage = doc.overproduction_percentage_for_work_order;
- var finish_btn = frm.add_custom_button(__('Finish'), function() {
- erpnext.work_order.make_se(frm, 'Manufacture');
- });
+ if (allowance_percentage > 0) {
+ let allowed_qty = frm.doc.qty + ((allowance_percentage / 100) * frm.doc.qty);
- if(doc.material_transferred_for_manufacturing>=doc.qty) {
- // all materials transferred for manufacturing, make this primary
- finish_btn.addClass('btn-primary');
+ if ((flt(doc.produced_qty) < allowed_qty)) {
+ frm.add_custom_button(__('Finish'), function() {
+ erpnext.work_order.make_se(frm, 'Manufacture');
+ });
+ }
+ }
+ });
}
}
} else {
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py
index 7b86253..1e6d982 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -20,6 +20,7 @@
nowdate,
time_diff_in_hours,
)
+from pypika import functions as fn
from erpnext.manufacturing.doctype.bom.bom import (
get_bom_item_rate,
@@ -859,6 +860,7 @@
if self.docstatus == 1:
# calculate transferred qty based on submitted stock entries
self.update_transferred_qty_for_required_items()
+ self.update_returned_qty()
# update in bin
self.update_reserved_qty_for_production()
@@ -930,23 +932,62 @@
self.set_available_qty()
def update_transferred_qty_for_required_items(self):
- """update transferred qty from submitted stock entries for that item against
- the work order"""
+ ste = frappe.qb.DocType("Stock Entry")
+ ste_child = frappe.qb.DocType("Stock Entry Detail")
- for d in self.required_items:
- transferred_qty = frappe.db.sql(
- """select sum(qty)
- from `tabStock Entry` entry, `tabStock Entry Detail` detail
- where
- entry.work_order = %(name)s
- and entry.purpose = 'Material Transfer for Manufacture'
- and entry.docstatus = 1
- and detail.parent = entry.name
- and (detail.item_code = %(item)s or detail.original_item = %(item)s)""",
- {"name": self.name, "item": d.item_code},
- )[0][0]
+ query = (
+ frappe.qb.from_(ste)
+ .inner_join(ste_child)
+ .on((ste_child.parent == ste.name))
+ .select(
+ ste_child.item_code,
+ ste_child.original_item,
+ fn.Sum(ste_child.qty).as_("qty"),
+ )
+ .where(
+ (ste.docstatus == 1)
+ & (ste.work_order == self.name)
+ & (ste.purpose == "Material Transfer for Manufacture")
+ & (ste.is_return == 0)
+ )
+ .groupby(ste_child.item_code)
+ )
- d.db_set("transferred_qty", flt(transferred_qty), update_modified=False)
+ data = query.run(as_dict=1) or []
+ transferred_items = frappe._dict({d.original_item or d.item_code: d.qty for d in data})
+
+ for row in self.required_items:
+ row.db_set(
+ "transferred_qty", (transferred_items.get(row.item_code) or 0.0), update_modified=False
+ )
+
+ def update_returned_qty(self):
+ ste = frappe.qb.DocType("Stock Entry")
+ ste_child = frappe.qb.DocType("Stock Entry Detail")
+
+ query = (
+ frappe.qb.from_(ste)
+ .inner_join(ste_child)
+ .on((ste_child.parent == ste.name))
+ .select(
+ ste_child.item_code,
+ ste_child.original_item,
+ fn.Sum(ste_child.qty).as_("qty"),
+ )
+ .where(
+ (ste.docstatus == 1)
+ & (ste.work_order == self.name)
+ & (ste.purpose == "Material Transfer for Manufacture")
+ & (ste.is_return == 1)
+ )
+ .groupby(ste_child.item_code)
+ )
+
+ data = query.run(as_dict=1) or []
+ returned_dict = frappe._dict({d.original_item or d.item_code: d.qty for d in data})
+
+ for row in self.required_items:
+ row.db_set("returned_qty", (returned_dict.get(row.item_code) or 0.0), update_modified=False)
def update_consumed_qty_for_required_items(self):
"""
@@ -1470,3 +1511,25 @@
)
)
).run()[0][0] or 0.0
+
+
+@frappe.whitelist()
+def make_stock_return_entry(work_order):
+ from erpnext.stock.doctype.stock_entry.stock_entry import get_available_materials
+
+ non_consumed_items = get_available_materials(work_order)
+ if not non_consumed_items:
+ return
+
+ wo_doc = frappe.get_cached_doc("Work Order", work_order)
+
+ stock_entry = frappe.new_doc("Stock Entry")
+ stock_entry.from_bom = 1
+ stock_entry.is_return = 1
+ stock_entry.work_order = work_order
+ stock_entry.purpose = "Material Transfer for Manufacture"
+ stock_entry.bom_no = wo_doc.bom_no
+ stock_entry.add_transfered_raw_materials_in_items()
+ stock_entry.set_stock_entry_type()
+
+ return stock_entry
diff --git a/erpnext/manufacturing/doctype/work_order_item/work_order_item.json b/erpnext/manufacturing/doctype/work_order_item/work_order_item.json
index 3acf572..f354d45 100644
--- a/erpnext/manufacturing/doctype/work_order_item/work_order_item.json
+++ b/erpnext/manufacturing/doctype/work_order_item/work_order_item.json
@@ -20,6 +20,7 @@
"column_break_11",
"transferred_qty",
"consumed_qty",
+ "returned_qty",
"available_qty_at_source_warehouse",
"available_qty_at_wip_warehouse"
],
@@ -97,6 +98,7 @@
"fieldtype": "Column Break"
},
{
+ "columns": 1,
"depends_on": "eval:!parent.skip_transfer",
"fieldname": "consumed_qty",
"fieldtype": "Float",
@@ -127,11 +129,19 @@
"fieldtype": "Currency",
"label": "Amount",
"read_only": 1
+ },
+ {
+ "columns": 1,
+ "fieldname": "returned_qty",
+ "fieldtype": "Float",
+ "in_list_view": 1,
+ "label": "Returned Qty ",
+ "read_only": 1
}
],
"istable": 1,
"links": [],
- "modified": "2020-04-13 18:46:32.966416",
+ "modified": "2022-09-28 10:50:43.512562",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Work Order Item",
@@ -140,5 +150,6 @@
"quick_entry": 1,
"sort_field": "modified",
"sort_order": "DESC",
+ "states": [],
"track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/manufacturing/report/job_card_summary/job_card_summary.py b/erpnext/manufacturing/report/job_card_summary/job_card_summary.py
index 5083b73..63c2d97 100644
--- a/erpnext/manufacturing/report/job_card_summary/job_card_summary.py
+++ b/erpnext/manufacturing/report/job_card_summary/job_card_summary.py
@@ -85,8 +85,8 @@
open_job_cards.append(periodic_data.get("Open").get(d))
completed.append(periodic_data.get("Completed").get(d))
- datasets.append({"name": "Open", "values": open_job_cards})
- datasets.append({"name": "Completed", "values": completed})
+ datasets.append({"name": _("Open"), "values": open_job_cards})
+ datasets.append({"name": _("Completed"), "values": completed})
chart = {"data": {"labels": labels, "datasets": datasets}, "type": "bar"}
diff --git a/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.js b/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.js
index b2428e8..2fb4ec6 100644
--- a/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.js
+++ b/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.js
@@ -50,7 +50,7 @@
label: __("Status"),
fieldname: "status",
fieldtype: "Select",
- options: ["In Process", "Completed", "Stopped"]
+ options: ["", "In Process", "Completed", "Stopped"]
},
{
label: __("Excess Materials Consumed"),
diff --git a/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.py b/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.py
index 8158bc9..14e97d3 100644
--- a/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.py
+++ b/erpnext/manufacturing/report/work_order_consumed_materials/work_order_consumed_materials.py
@@ -1,6 +1,8 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
+from collections import defaultdict
+
import frappe
from frappe import _
@@ -18,7 +20,11 @@
filters = get_filter_condition(report_filters)
wo_items = {}
- for d in frappe.get_all("Work Order", filters=filters, fields=fields):
+
+ work_orders = frappe.get_all("Work Order", filters=filters, fields=fields)
+ returned_materials = get_returned_materials(work_orders)
+
+ for d in work_orders:
d.extra_consumed_qty = 0.0
if d.consumed_qty and d.consumed_qty > d.required_qty:
d.extra_consumed_qty = d.consumed_qty - d.required_qty
@@ -39,6 +45,28 @@
return data
+def get_returned_materials(work_orders):
+ raw_materials_qty = defaultdict(float)
+
+ raw_materials = frappe.get_all(
+ "Stock Entry",
+ fields=["`tabStock Entry Detail`.`item_code`", "`tabStock Entry Detail`.`qty`"],
+ filters=[
+ ["Stock Entry", "is_return", "=", 1],
+ ["Stock Entry Detail", "docstatus", "=", 1],
+ ["Stock Entry", "work_order", "in", [d.name for d in work_orders]],
+ ],
+ )
+
+ for d in raw_materials:
+ raw_materials_qty[d.item_code] += d.qty
+
+ for row in work_orders:
+ row.returned_qty = 0.0
+ if raw_materials_qty.get(row.raw_material_item_code):
+ row.returned_qty = raw_materials_qty.get(row.raw_material_item_code)
+
+
def get_fields():
return [
"`tabWork Order Item`.`parent`",
@@ -65,7 +93,7 @@
for field in ["name", "production_item", "company", "status"]:
value = report_filters.get(field)
if value:
- key = f"`{field}`"
+ key = f"{field}"
filters.update({key: value})
return filters
@@ -112,4 +140,10 @@
"fieldtype": "Float",
"width": 100,
},
+ {
+ "label": _("Returned Qty"),
+ "fieldname": "returned_qty",
+ "fieldtype": "Float",
+ "width": 100,
+ },
]
diff --git a/erpnext/manufacturing/report/work_order_summary/work_order_summary.py b/erpnext/manufacturing/report/work_order_summary/work_order_summary.py
index 2368bfd..41ffcbb 100644
--- a/erpnext/manufacturing/report/work_order_summary/work_order_summary.py
+++ b/erpnext/manufacturing/report/work_order_summary/work_order_summary.py
@@ -83,6 +83,7 @@
for d in data:
status_wise_data[d.status] += 1
+ labels = [_(label) for label in labels]
values = [status_wise_data[label] for label in labels]
chart = {
@@ -95,7 +96,7 @@
def get_chart_based_on_age(data):
- labels = ["0-30 Days", "30-60 Days", "60-90 Days", "90 Above"]
+ labels = [_("0-30 Days"), _("30-60 Days"), _("60-90 Days"), _("90 Above")]
age_wise_data = {"0-30 Days": 0, "30-60 Days": 0, "60-90 Days": 0, "90 Above": 0}
@@ -135,8 +136,8 @@
pending.append(periodic_data.get("Pending").get(d))
completed.append(periodic_data.get("Completed").get(d))
- datasets.append({"name": "Pending", "values": pending})
- datasets.append({"name": "Completed", "values": completed})
+ datasets.append({"name": _("Pending"), "values": pending})
+ datasets.append({"name": _("Completed"), "values": completed})
chart = {
"data": {"labels": labels, "datasets": datasets},
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 2a0ca8c..fc63f12 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -315,3 +315,4 @@
erpnext.patches.v14_0.create_accounting_dimensions_in_subcontracting_doctypes
erpnext.patches.v14_0.fix_subcontracting_receipt_gl_entries
erpnext.patches.v14_0.migrate_remarks_from_gl_to_payment_ledger
+erpnext.patches.v14_0.create_accounting_dimensions_for_asset_capitalization
diff --git a/erpnext/patches/v13_0/update_old_loans.py b/erpnext/patches/v13_0/update_old_loans.py
index a1d40b7..0bd3fcd 100644
--- a/erpnext/patches/v13_0/update_old_loans.py
+++ b/erpnext/patches/v13_0/update_old_loans.py
@@ -100,6 +100,7 @@
"mode_of_payment": loan.mode_of_payment,
"loan_account": loan.loan_account,
"payment_account": loan.payment_account,
+ "disbursement_account": loan.payment_account,
"interest_income_account": loan.interest_income_account,
"penalty_income_account": loan.penalty_income_account,
},
@@ -190,6 +191,7 @@
loan_type_doc.company = loan.company
loan_type_doc.mode_of_payment = loan.mode_of_payment
loan_type_doc.payment_account = loan.payment_account
+ loan_type_doc.disbursement_account = loan.payment_account
loan_type_doc.loan_account = loan.loan_account
loan_type_doc.interest_income_account = loan.interest_income_account
loan_type_doc.penalty_income_account = penalty_account
diff --git a/erpnext/patches/v14_0/create_accounting_dimensions_for_asset_capitalization.py b/erpnext/patches/v14_0/create_accounting_dimensions_for_asset_capitalization.py
new file mode 100644
index 0000000..09e20a9
--- /dev/null
+++ b/erpnext/patches/v14_0/create_accounting_dimensions_for_asset_capitalization.py
@@ -0,0 +1,31 @@
+import frappe
+from frappe.custom.doctype.custom_field.custom_field import create_custom_field
+
+
+def execute():
+ accounting_dimensions = frappe.db.get_all(
+ "Accounting Dimension", fields=["fieldname", "label", "document_type", "disabled"]
+ )
+
+ if not accounting_dimensions:
+ return
+
+ doctype = "Asset Capitalization"
+
+ for d in accounting_dimensions:
+ field = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": d.fieldname})
+
+ if field:
+ continue
+
+ df = {
+ "fieldname": d.fieldname,
+ "label": d.label,
+ "fieldtype": "Link",
+ "options": d.document_type,
+ "insert_after": "accounting_dimensions_section",
+ }
+
+ create_custom_field(doctype, df, ignore_validate=True)
+
+ frappe.clear_cache(doctype=doctype)
diff --git a/erpnext/patches/v14_0/migrate_remarks_from_gl_to_payment_ledger.py b/erpnext/patches/v14_0/migrate_remarks_from_gl_to_payment_ledger.py
index 062d24b..fd2a2a3 100644
--- a/erpnext/patches/v14_0/migrate_remarks_from_gl_to_payment_ledger.py
+++ b/erpnext/patches/v14_0/migrate_remarks_from_gl_to_payment_ledger.py
@@ -3,6 +3,29 @@
from frappe.utils import create_batch
+def remove_duplicate_entries(pl_entries):
+ unique_vouchers = set()
+ for x in pl_entries:
+ unique_vouchers.add(
+ (x.company, x.account, x.party_type, x.party, x.voucher_type, x.voucher_no, x.gle_remarks)
+ )
+
+ entries = []
+ for x in unique_vouchers:
+ entries.append(
+ frappe._dict(
+ company=x[0],
+ account=x[1],
+ party_type=x[2],
+ party=x[3],
+ voucher_type=x[4],
+ voucher_no=x[5],
+ gle_remarks=x[6],
+ )
+ )
+ return entries
+
+
def execute():
if frappe.reload_doc("accounts", "doctype", "payment_ledger_entry"):
@@ -34,6 +57,8 @@
.run(as_dict=True)
)
+ pl_entries = remove_duplicate_entries(pl_entries)
+
if pl_entries:
# split into multiple batches, update and commit for each batch
batch_size = 1000
diff --git a/erpnext/projects/report/project_summary/project_summary.py b/erpnext/projects/report/project_summary/project_summary.py
index 606c0c2..7a35fd2 100644
--- a/erpnext/projects/report/project_summary/project_summary.py
+++ b/erpnext/projects/report/project_summary/project_summary.py
@@ -91,9 +91,9 @@
"data": {
"labels": labels[:30],
"datasets": [
- {"name": "Overdue", "values": overdue[:30]},
- {"name": "Completed", "values": completed[:30]},
- {"name": "Total Tasks", "values": total[:30]},
+ {"name": _("Overdue"), "values": overdue[:30]},
+ {"name": _("Completed"), "values": completed[:30]},
+ {"name": _("Total Tasks"), "values": total[:30]},
],
},
"type": "bar",
diff --git a/erpnext/public/js/help_links.js b/erpnext/public/js/help_links.js
index b643cca..1c3f43e 100644
--- a/erpnext/public/js/help_links.js
+++ b/erpnext/public/js/help_links.js
@@ -671,7 +671,7 @@
label: "Item Valuation",
url:
docsUrl +
- "user/manual/en/stock/articles/item-valuation-fifo-and-moving-average",
+ "user/manual/en/stock/articles/calculation-of-valuation-rate-in-fifo-and-moving-average",
},
];
diff --git a/erpnext/public/js/utils/barcode_scanner.js b/erpnext/public/js/utils/barcode_scanner.js
index a6bff2c..83b108b 100644
--- a/erpnext/public/js/utils/barcode_scanner.js
+++ b/erpnext/public/js/utils/barcode_scanner.js
@@ -21,6 +21,11 @@
this.items_table_name = opts.items_table_name || "items";
this.items_table = this.frm.doc[this.items_table_name];
+ // optional sound name to play when scan either fails or passes.
+ // see https://frappeframework.com/docs/v14/user/en/python-api/hooks#sounds
+ this.success_sound = opts.play_success_sound;
+ this.fail_sound = opts.play_fail_sound;
+
// any API that takes `search_value` as input and returns dictionary as follows
// {
// item_code: "HORSESHOE", // present if any item was found
@@ -54,19 +59,24 @@
if (!data || Object.keys(data).length === 0) {
this.show_alert(__("Cannot find Item with this Barcode"), "red");
this.clean_up();
+ this.play_fail_sound();
reject();
return;
}
me.update_table(data).then(row => {
- row ? resolve(row) : reject();
+ this.play_success_sound();
+ resolve(row);
+ }).catch(() => {
+ this.play_fail_sound();
+ reject();
});
});
});
}
update_table(data) {
- return new Promise(resolve => {
+ return new Promise((resolve, reject) => {
let cur_grid = this.frm.fields_dict[this.items_table_name].grid;
const {item_code, barcode, batch_no, serial_no, uom} = data;
@@ -77,6 +87,7 @@
if (this.dont_allow_new_row) {
this.show_alert(__("Maximum quantity scanned for item {0}.", [item_code]), "red");
this.clean_up();
+ reject();
return;
}
@@ -88,6 +99,7 @@
if (this.is_duplicate_serial_no(row, serial_no)) {
this.clean_up();
+ reject();
return;
}
@@ -219,6 +231,14 @@
return this.items_table.find((d) => !d.item_code);
}
+ play_success_sound() {
+ this.success_sound && frappe.utils.play_sound(this.success_sound);
+ }
+
+ play_fail_sound() {
+ this.fail_sound && frappe.utils.play_sound(this.fail_sound);
+ }
+
clean_up() {
this.scan_barcode_field.set_value("");
refresh_field(this.items_table_name);
diff --git a/erpnext/public/scss/order-page.scss b/erpnext/public/scss/order-page.scss
new file mode 100644
index 0000000..6f5fe5d
--- /dev/null
+++ b/erpnext/public/scss/order-page.scss
@@ -0,0 +1,115 @@
+#page-order {
+ .main-column {
+ .page-content-wrapper {
+
+ .breadcrumb-container {
+ @media screen and (min-width: 567px) {
+ padding-left: var(--padding-sm);
+ }
+ }
+
+ .container.my-4 {
+ background-color: var(--fg-color);
+
+ @media screen and (min-width: 567px) {
+ padding: 1.25rem 1.5rem;
+ border-radius: var(--border-radius-md);
+ box-shadow: var(--card-shadow);
+ }
+ }
+ }
+ }
+}
+
+.indicator-container {
+ @media screen and (max-width: 567px) {
+ padding-bottom: 0.8rem;
+ }
+}
+
+.order-items {
+ padding: 1.5rem 0;
+ border-bottom: 1px solid var(--border-color);
+ color: var(--gray-700);
+
+ @media screen and (max-width: 567px) {
+ align-items: flex-start !important;
+ }
+ .col-2 {
+ @media screen and (max-width: 567px) {
+ flex: auto;
+ max-width: 28%;
+ }
+ }
+
+ .order-item-name {
+ font-size: var(--text-base);
+ font-weight: 500;
+ }
+
+ .btn:focus,
+ .btn:hover {
+ background-color: var(--control-bg);
+ }
+
+
+ .col-6 {
+ @media screen and (max-width: 567px) {
+ max-width: 100%;
+ }
+
+ &.order-item-name {
+ font-size: var(--text-base);
+ }
+ }
+}
+
+.item-grand-total {
+ font-size: var(--text-base);
+}
+
+.list-item-name,
+.item-total,
+.order-container,
+.order-qty {
+ font-size: var(--text-md);
+}
+
+.d-s-n {
+ @media screen and (max-width: 567px) {
+ display: none;
+ }
+}
+
+.d-l-n {
+ @media screen and (min-width: 567px) {
+ display: none;
+ }
+}
+
+.border-btm {
+ border-bottom: 1px solid var(--border-color);
+}
+
+.order-taxes {
+ display: flex;
+
+ @media screen and (min-width: 567px) {
+ justify-content: flex-end;
+ }
+
+ .col-4 {
+ padding-right: 0;
+
+ .col-8 {
+ padding-left: 0;
+ padding-right: 0;
+ }
+
+ @media screen and (max-width: 567px) {
+ padding-left: 0;
+ flex: auto;
+ max-width: 100%;
+ }
+ }
+}
\ No newline at end of file
diff --git a/erpnext/public/scss/website.scss b/erpnext/public/scss/website.scss
index 9ea8416..b5e97f1 100644
--- a/erpnext/public/scss/website.scss
+++ b/erpnext/public/scss/website.scss
@@ -1,3 +1,4 @@
+@import './order-page';
.filter-options {
max-height: 300px;
@@ -32,19 +33,29 @@
height: 24px;
}
-.website-list .result {
- margin-top: 2rem;
-}
+.website-list {
+ background-color: var(--fg-color);
+ padding: 0 var(--padding-lg);
+ border-radius: var(--border-radius-md);
-.result {
- border-bottom: 1px solid var(--border-color);
+ @media screen and (max-width: 567px) {
+ margin-left: -2rem;
+ }
+
+ &.result {
+ border-bottom: 1px solid var(--border-color);
+ }
}
.transaction-list-item {
padding: 1rem 0;
- border-top: 1px solid var(--border-color);
+ border-bottom: 1px solid var(--border-color);
position: relative;
+ &:only-child, &:last-child {
+ border: 0;
+ }
+
a.transaction-item-link {
position: absolute;
top: 0;
@@ -68,3 +79,13 @@
line-height: 1.3;
}
}
+
+.list-item-name, .item-total {
+ font-size: var(--font-size-sm);
+}
+
+.items-preview {
+ @media screen and (max-width: 567px) {
+ margin-top: 1rem;
+ }
+}
\ No newline at end of file
diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js
index da7576e..24375d8 100644
--- a/erpnext/selling/page/point_of_sale/pos_controller.js
+++ b/erpnext/selling/page/point_of_sale/pos_controller.js
@@ -660,7 +660,7 @@
} else {
return;
}
- } else if (available_qty < qty_needed) {
+ } else if (is_stock_item && available_qty < qty_needed) {
frappe.throw({
message: __('Stock quantity not enough for Item Code: {0} under warehouse {1}. Available quantity {2}.', [bold_item_code, bold_warehouse, bold_available_qty]),
indicator: 'orange'
@@ -694,7 +694,7 @@
callback(res) {
if (!me.item_stock_map[item_code])
me.item_stock_map[item_code] = {};
- me.item_stock_map[item_code][warehouse] = res.message[0];
+ me.item_stock_map[item_code][warehouse] = res.message;
}
});
}
diff --git a/erpnext/selling/page/point_of_sale/pos_item_details.js b/erpnext/selling/page/point_of_sale/pos_item_details.js
index b75ffb2..f9b5bb2 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_details.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_details.js
@@ -242,13 +242,14 @@
if (this.value) {
me.events.form_updated(me.current_item, 'warehouse', this.value).then(() => {
me.item_stock_map = me.events.get_item_stock_map();
- const available_qty = me.item_stock_map[me.item_row.item_code] && me.item_stock_map[me.item_row.item_code][this.value];
+ const available_qty = me.item_stock_map[me.item_row.item_code][this.value][0];
+ const is_stock_item = Boolean(me.item_stock_map[me.item_row.item_code][this.value][1]);
if (available_qty === undefined) {
me.events.get_available_stock(me.item_row.item_code, this.value).then(() => {
// item stock map is updated now reset warehouse
me.warehouse_control.set_value(this.value);
})
- } else if (available_qty === 0) {
+ } else if (available_qty === 0 && is_stock_item) {
me.warehouse_control.set_value('');
const bold_item_code = me.item_row.item_code.bold();
const bold_warehouse = this.value.bold();
diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py
index 548df31..c28f45a 100644
--- a/erpnext/stock/doctype/bin/bin.py
+++ b/erpnext/stock/doctype/bin/bin.py
@@ -37,8 +37,10 @@
self.set_projected_qty()
- self.db_set("reserved_qty_for_production", flt(self.reserved_qty_for_production))
- self.db_set("projected_qty", self.projected_qty)
+ self.db_set(
+ "reserved_qty_for_production", flt(self.reserved_qty_for_production), update_modified=True
+ )
+ self.db_set("projected_qty", self.projected_qty, update_modified=True)
def update_reserved_qty_for_sub_contracting(self, subcontract_doctype="Subcontracting Order"):
# reserved qty
@@ -118,9 +120,9 @@
else:
reserved_qty_for_sub_contract = 0
- self.db_set("reserved_qty_for_sub_contract", reserved_qty_for_sub_contract)
+ self.db_set("reserved_qty_for_sub_contract", reserved_qty_for_sub_contract, update_modified=True)
self.set_projected_qty()
- self.db_set("projected_qty", self.projected_qty)
+ self.db_set("projected_qty", self.projected_qty, update_modified=True)
def on_doctype_update():
@@ -193,4 +195,5 @@
"planned_qty": planned_qty,
"projected_qty": projected_qty,
},
+ update_modified=True,
)
diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
index 6bcab73..1b9f168 100644
--- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
@@ -6,7 +6,7 @@
import frappe
from frappe.tests.utils import FrappeTestCase
-from frappe.utils import cstr, flt, nowdate, nowtime
+from frappe.utils import add_days, cstr, flt, nowdate, nowtime, today
from erpnext.accounts.doctype.account.test_account import get_inventory_account
from erpnext.accounts.utils import get_balance_on
@@ -1091,6 +1091,36 @@
frappe.db.exists("GL Entry", {"voucher_no": dn.name, "voucher_type": dn.doctype})
)
+ def test_batch_expiry_for_delivery_note(self):
+ from erpnext.controllers.sales_and_purchase_return import make_return_doc
+ from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
+
+ item = make_item(
+ "_Test Batch Item For Return Check",
+ {
+ "is_purchase_item": 1,
+ "is_stock_item": 1,
+ "has_batch_no": 1,
+ "create_new_batch": 1,
+ "batch_number_series": "TBIRC.#####",
+ },
+ )
+
+ pi = make_purchase_receipt(qty=1, item_code=item.name)
+
+ dn = create_delivery_note(qty=1, item_code=item.name, batch_no=pi.items[0].batch_no)
+
+ dn.load_from_db()
+ batch_no = dn.items[0].batch_no
+ self.assertTrue(batch_no)
+
+ frappe.db.set_value("Batch", batch_no, "expiry_date", add_days(today(), -1))
+
+ return_dn = make_return_doc(dn.doctype, dn.name)
+ return_dn.save().submit()
+
+ self.assertTrue(return_dn.docstatus == 1)
+
def create_delivery_note(**args):
dn = frappe.new_doc("Delivery Note")
@@ -1117,6 +1147,7 @@
"expense_account": args.expense_account or "Cost of Goods Sold - _TC",
"cost_center": args.cost_center or "_Test Cost Center - _TC",
"serial_no": args.serial_no,
+ "batch_no": args.batch_no or None,
"target_warehouse": args.target_warehouse,
},
)
diff --git a/erpnext/stock/doctype/item/item.js b/erpnext/stock/doctype/item/item.js
index 7e1476d..e61f0f5 100644
--- a/erpnext/stock/doctype/item/item.js
+++ b/erpnext/stock/doctype/item/item.js
@@ -10,6 +10,31 @@
frm.add_fetch('attribute', 'to_range', 'to_range');
frm.add_fetch('attribute', 'increment', 'increment');
frm.add_fetch('tax_type', 'tax_rate', 'tax_rate');
+
+ frm.make_methods = {
+ 'Sales Order': () => {
+ open_form(frm, "Sales Order", "Sales Order Item", "items");
+ },
+ 'Delivery Note': () => {
+ open_form(frm, "Delivery Note", "Delivery Note Item", "items");
+ },
+ 'Sales Invoice': () => {
+ open_form(frm, "Sales Invoice", "Sales Invoice Item", "items");
+ },
+ 'Purchase Order': () => {
+ open_form(frm, "Purchase Order", "Purchase Order Item", "items");
+ },
+ 'Purchase Receipt': () => {
+ open_form(frm, "Purchase Receipt", "Purchase Receipt Item", "items");
+ },
+ 'Purchase Invoice': () => {
+ open_form(frm, "Purchase Invoice", "Purchase Invoice Item", "items");
+ },
+ 'Material Request': () => {
+ open_form(frm, "Material Request", "Material Request Item", "items");
+ },
+ };
+
},
onload: function(frm) {
erpnext.item.setup_queries(frm);
@@ -858,3 +883,17 @@
];
+
+function open_form(frm, doctype, child_doctype, parentfield) {
+ frappe.model.with_doctype(doctype, () => {
+ let new_doc = frappe.model.get_new_doc(doctype);
+
+ let new_child_doc = frappe.model.add_child(new_doc, child_doctype, parentfield);
+ new_child_doc.item_code = frm.doc.name;
+ new_child_doc.item_name = frm.doc.item_name;
+ new_child_doc.uom = frm.doc.stock_uom;
+ new_child_doc.description = frm.doc.description;
+
+ frappe.ui.form.make_quick_entry(doctype, null, null, new_doc);
+ });
+}
diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py
index 143fe40..c8bb1b9 100644
--- a/erpnext/stock/doctype/item/item.py
+++ b/erpnext/stock/doctype/item/item.py
@@ -937,17 +937,21 @@
"Purchase Order Item",
"Material Request Item",
"Product Bundle",
+ "BOM",
]
for doctype in linked_doctypes:
filters = {"item_code": self.name, "docstatus": 1}
- if doctype == "Product Bundle":
- filters = {"new_item_code": self.name}
+ if doctype in ("Product Bundle", "BOM"):
+ if doctype == "Product Bundle":
+ filters = {"new_item_code": self.name}
+ fieldname = "new_item_code as docname"
+ else:
+ filters = {"item": self.name, "docstatus": 1}
+ fieldname = "name as docname"
- if linked_doc := frappe.db.get_value(
- doctype, filters, ["new_item_code as docname"], as_dict=True
- ):
+ if linked_doc := frappe.db.get_value(doctype, filters, fieldname, as_dict=True):
return linked_doc.update({"doctype": doctype})
elif doctype in (
diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py
index 1cee553..e35c8bf 100644
--- a/erpnext/stock/doctype/item/test_item.py
+++ b/erpnext/stock/doctype/item/test_item.py
@@ -5,6 +5,7 @@
import json
import frappe
+from frappe.custom.doctype.property_setter.property_setter import make_property_setter
from frappe.test_runner import make_test_objects
from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import add_days, today
@@ -816,6 +817,30 @@
item.reload()
self.assertEqual(item.is_stock_item, 1)
+ def test_serach_fields_for_item(self):
+ from erpnext.controllers.queries import item_query
+
+ make_property_setter("Item", None, "search_fields", "item_name", "Data", for_doctype="Doctype")
+
+ item = make_item(properties={"item_name": "Test Item", "description": "Test Description"})
+ data = item_query(
+ "Item", "Test Item", "", 0, 20, filters={"item_name": "Test Item"}, as_dict=True
+ )
+ self.assertEqual(data[0].name, item.name)
+ self.assertEqual(data[0].item_name, item.item_name)
+ self.assertTrue("description" not in data[0])
+
+ make_property_setter(
+ "Item", None, "search_fields", "item_name, description", "Data", for_doctype="Doctype"
+ )
+ data = item_query(
+ "Item", "Test Item", "", 0, 20, filters={"item_name": "Test Item"}, as_dict=True
+ )
+ self.assertEqual(data[0].name, item.name)
+ self.assertEqual(data[0].item_name, item.item_name)
+ self.assertEqual(data[0].description, item.description)
+ self.assertTrue("description" in data[0])
+
def set_item_variant_settings(fields):
doc = frappe.get_doc("Item Variant Settings")
diff --git a/erpnext/stock/doctype/pick_list/pick_list.py b/erpnext/stock/doctype/pick_list/pick_list.py
index adddb41..9c1c7e5 100644
--- a/erpnext/stock/doctype/pick_list/pick_list.py
+++ b/erpnext/stock/doctype/pick_list/pick_list.py
@@ -183,7 +183,7 @@
frappe.throw("Row #{0}: Item Code is Mandatory".format(item.idx))
item_code = item.item_code
reference = item.sales_order_item or item.material_request_item
- key = (item_code, item.uom, item.warehouse, reference)
+ key = (item_code, item.uom, item.warehouse, item.batch_no, reference)
item.idx = None
item.name = None
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index b77c3a5..6269724 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -1241,6 +1241,37 @@
self.assertEqual(query[0].value, 0)
+ def test_batch_expiry_for_purchase_receipt(self):
+ from erpnext.controllers.sales_and_purchase_return import make_return_doc
+
+ item = make_item(
+ "_Test Batch Item For Return Check",
+ {
+ "is_purchase_item": 1,
+ "is_stock_item": 1,
+ "has_batch_no": 1,
+ "create_new_batch": 1,
+ "batch_number_series": "TBIRC.#####",
+ },
+ )
+
+ pi = make_purchase_receipt(
+ qty=1,
+ item_code=item.name,
+ update_stock=True,
+ )
+
+ pi.load_from_db()
+ batch_no = pi.items[0].batch_no
+ self.assertTrue(batch_no)
+
+ frappe.db.set_value("Batch", batch_no, "expiry_date", add_days(today(), -1))
+
+ return_pi = make_return_doc(pi.doctype, pi.name)
+ return_pi.save().submit()
+
+ self.assertTrue(return_pi.docstatus == 1)
+
def prepare_data_for_internal_transfer():
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier
diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.json b/erpnext/stock/doctype/quality_inspection/quality_inspection.json
index edfe7e9..db9322f 100644
--- a/erpnext/stock/doctype/quality_inspection/quality_inspection.json
+++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.json
@@ -10,6 +10,7 @@
"naming_series",
"report_date",
"status",
+ "manual_inspection",
"column_break_4",
"inspection_type",
"reference_type",
@@ -231,6 +232,12 @@
"label": "Status",
"options": "\nAccepted\nRejected",
"reqd": 1
+ },
+ {
+ "default": "0",
+ "fieldname": "manual_inspection",
+ "fieldtype": "Check",
+ "label": "Manual Inspection"
}
],
"icon": "fa fa-search",
@@ -238,10 +245,11 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2020-12-18 19:59:55.710300",
+ "modified": "2022-10-04 22:00:13.995221",
"modified_by": "Administrator",
"module": "Stock",
"name": "Quality Inspection",
+ "naming_rule": "By \"Naming Series\" field",
"owner": "Administrator",
"permissions": [
{
@@ -262,5 +270,6 @@
"search_fields": "item_code, report_date, reference_name",
"show_name_in_global_search": 1,
"sort_field": "modified",
- "sort_order": "ASC"
+ "sort_order": "ASC",
+ "states": []
}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/quality_inspection/quality_inspection.py b/erpnext/stock/doctype/quality_inspection/quality_inspection.py
index 13abfad..8ffd3f2 100644
--- a/erpnext/stock/doctype/quality_inspection/quality_inspection.py
+++ b/erpnext/stock/doctype/quality_inspection/quality_inspection.py
@@ -30,6 +30,9 @@
if self.readings:
self.inspect_and_set_status()
+ def before_submit(self):
+ self.validate_readings_status_mandatory()
+
@frappe.whitelist()
def get_item_specification_details(self):
if not self.quality_inspection_template:
@@ -65,6 +68,11 @@
def on_cancel(self):
self.update_qc_reference()
+ def validate_readings_status_mandatory(self):
+ for reading in self.readings:
+ if not reading.status:
+ frappe.throw(_("Row #{0}: Status is mandatory").format(reading.idx))
+
def update_qc_reference(self):
quality_inspection = self.name if self.docstatus == 1 else ""
@@ -124,6 +132,16 @@
# if not formula based check acceptance values set
self.set_status_based_on_acceptance_values(reading)
+ if not self.manual_inspection:
+ self.status = "Accepted"
+ for reading in self.readings:
+ if reading.status == "Rejected":
+ self.status = "Rejected"
+ frappe.msgprint(
+ _("Status set to rejected as there are one or more rejected readings."), alert=True
+ )
+ break
+
def set_status_based_on_acceptance_values(self, reading):
if not cint(reading.numeric):
result = reading.get("reading_value") == reading.get("value")
diff --git a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py
index 144f138..4f19643 100644
--- a/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py
+++ b/erpnext/stock/doctype/quality_inspection/test_quality_inspection.py
@@ -160,7 +160,7 @@
)
readings = [
- {"specification": "Iron Content", "min_value": 0.1, "max_value": 0.9, "reading_1": "0.4"}
+ {"specification": "Iron Content", "min_value": 0.1, "max_value": 0.9, "reading_1": "1.0"}
]
qa = create_quality_inspection(
@@ -184,6 +184,38 @@
se.cancel()
frappe.db.set_value("Stock Settings", None, "action_if_quality_inspection_is_rejected", "Stop")
+ def test_qi_status(self):
+ make_stock_entry(
+ item_code="_Test Item with QA", target="_Test Warehouse - _TC", qty=1, basic_rate=100
+ )
+ dn = create_delivery_note(item_code="_Test Item with QA", do_not_submit=True)
+ qa = create_quality_inspection(
+ reference_type="Delivery Note", reference_name=dn.name, status="Accepted", do_not_save=True
+ )
+ qa.readings[0].manual_inspection = 1
+ qa.save()
+
+ # Case - 1: When there are one or more readings with rejected status and parent manual inspection is unchecked, then parent status should be set to rejected.
+ qa.status = "Accepted"
+ qa.manual_inspection = 0
+ qa.readings[0].status = "Rejected"
+ qa.save()
+ self.assertEqual(qa.status, "Rejected")
+
+ # Case - 2: When all readings have accepted status and parent manual inspection is unchecked, then parent status should be set to accepted.
+ qa.status = "Rejected"
+ qa.manual_inspection = 0
+ qa.readings[0].status = "Accepted"
+ qa.save()
+ self.assertEqual(qa.status, "Accepted")
+
+ # Case - 3: When parent manual inspection is checked, then parent status should not be changed.
+ qa.status = "Accepted"
+ qa.manual_inspection = 1
+ qa.readings[0].status = "Rejected"
+ qa.save()
+ self.assertEqual(qa.status, "Accepted")
+
def create_quality_inspection(**args):
args = frappe._dict(args)
diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
index c470524..d6f9bae 100644
--- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
+++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.py
@@ -128,6 +128,9 @@
if not frappe.db.exists("Repost Item Valuation", doc.name):
return
+ # This is to avoid TooManyWritesError in case of large reposts
+ frappe.db.MAX_WRITES_PER_TRANSACTION *= 4
+
doc.set_status("In Progress")
if not frappe.flags.in_test:
frappe.db.commit()
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.json b/erpnext/stock/doctype/stock_entry/stock_entry.json
index abe98e2..7e9420d 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.json
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.json
@@ -148,19 +148,19 @@
"search_index": 1
},
{
- "depends_on": "eval:doc.purpose==\"Send to Subcontractor\"",
- "fieldname": "purchase_order",
- "fieldtype": "Link",
- "label": "Purchase Order",
- "options": "Purchase Order"
+ "depends_on": "eval:doc.purpose==\"Send to Subcontractor\"",
+ "fieldname": "purchase_order",
+ "fieldtype": "Link",
+ "label": "Purchase Order",
+ "options": "Purchase Order"
},
{
- "depends_on": "eval:doc.purpose==\"Send to Subcontractor\"",
- "fieldname": "subcontracting_order",
- "fieldtype": "Link",
- "label": "Subcontracting Order",
- "options": "Subcontracting Order"
- },
+ "depends_on": "eval:doc.purpose==\"Send to Subcontractor\"",
+ "fieldname": "subcontracting_order",
+ "fieldtype": "Link",
+ "label": "Subcontracting Order",
+ "options": "Subcontracting Order"
+ },
{
"depends_on": "eval:doc.purpose==\"Sales Return\"",
"fieldname": "delivery_note_no",
@@ -616,6 +616,7 @@
"fieldname": "is_return",
"fieldtype": "Check",
"hidden": 1,
+ "in_list_view": 1,
"label": "Is Return",
"no_copy": 1,
"print_hide": 1,
@@ -627,7 +628,7 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2022-05-02 05:21:39.060501",
+ "modified": "2022-10-07 14:39:51.943770",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Entry",
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 738ac33..b116735 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -1073,8 +1073,8 @@
# No work order could mean independent Manufacture entry, if so skip validation
if self.work_order and self.fg_completed_qty > allowed_qty:
frappe.throw(
- _("For quantity {0} should not be greater than work order quantity {1}").format(
- flt(self.fg_completed_qty), wo_qty
+ _("For quantity {0} should not be greater than allowed quantity {1}").format(
+ flt(self.fg_completed_qty), allowed_qty
)
)
@@ -1212,13 +1212,19 @@
def update_work_order(self):
def _validate_work_order(pro_doc):
+ msg, title = "", ""
if flt(pro_doc.docstatus) != 1:
- frappe.throw(_("Work Order {0} must be submitted").format(self.work_order))
+ msg = f"Work Order {self.work_order} must be submitted"
if pro_doc.status == "Stopped":
- frappe.throw(
- _("Transaction not allowed against stopped Work Order {0}").format(self.work_order)
- )
+ msg = f"Transaction not allowed against stopped Work Order {self.work_order}"
+
+ if self.is_return and pro_doc.status not in ["Completed", "Closed"]:
+ title = _("Stock Return")
+ msg = f"Work Order {self.work_order} must be completed or closed"
+
+ if msg:
+ frappe.throw(_(msg), title=title)
if self.job_card:
job_doc = frappe.get_doc("Job Card", self.job_card)
@@ -1754,10 +1760,12 @@
for key, row in available_materials.items():
remaining_qty_to_produce = flt(wo_data.trans_qty) - flt(wo_data.produced_qty)
- if remaining_qty_to_produce <= 0:
+ if remaining_qty_to_produce <= 0 and not self.is_return:
continue
- qty = (flt(row.qty) * flt(self.fg_completed_qty)) / remaining_qty_to_produce
+ qty = flt(row.qty)
+ if not self.is_return:
+ qty = (flt(row.qty) * flt(self.fg_completed_qty)) / remaining_qty_to_produce
item = row.item_details
if cint(frappe.get_cached_value("UOM", item.stock_uom, "must_be_whole_number")):
@@ -1781,6 +1789,9 @@
self.update_item_in_stock_entry_detail(row, item, qty)
def update_item_in_stock_entry_detail(self, row, item, qty) -> None:
+ if not qty:
+ return
+
ste_item_details = {
"from_warehouse": item.warehouse,
"to_warehouse": "",
@@ -1794,6 +1805,9 @@
"original_item": item.original_item,
}
+ if self.is_return:
+ ste_item_details["to_warehouse"] = item.s_warehouse
+
if row.serial_nos:
serial_nos = row.serial_nos
if item.batch_no:
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry_list.js b/erpnext/stock/doctype/stock_entry/stock_entry_list.js
index cbc3491..4eb0da1 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry_list.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry_list.js
@@ -1,8 +1,13 @@
frappe.listview_settings['Stock Entry'] = {
add_fields: ["`tabStock Entry`.`from_warehouse`", "`tabStock Entry`.`to_warehouse`",
- "`tabStock Entry`.`purpose`", "`tabStock Entry`.`work_order`", "`tabStock Entry`.`bom_no`"],
+ "`tabStock Entry`.`purpose`", "`tabStock Entry`.`work_order`", "`tabStock Entry`.`bom_no`",
+ "`tabStock Entry`.`is_return`"],
get_indicator: function (doc) {
- if (doc.docstatus === 0) {
+ debugger
+ if(doc.is_return===1 && doc.purpose === "Material Transfer for Manufacture") {
+ return [__("Material Returned from WIP"), "orange",
+ "is_return,=,1|purpose,=,Material Transfer for Manufacture|docstatus,<,2"];
+ } else if (doc.docstatus === 0) {
return [__("Draft"), "red", "docstatus,=,0"];
} else if (doc.purpose === 'Send to Warehouse' && doc.per_transferred < 100) {
diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
index 329cd7d..c64370d 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.py
@@ -153,6 +153,11 @@
def validate_batch(self):
if self.batch_no and self.voucher_type != "Stock Entry":
+ if (self.voucher_type in ["Purchase Receipt", "Purchase Invoice"] and self.actual_qty < 0) or (
+ self.voucher_type in ["Delivery Note", "Sales Invoice"] and self.actual_qty > 0
+ ):
+ return
+
expiry_date = frappe.db.get_value("Batch", self.batch_no, "expiry_date")
if expiry_date:
if getdate(self.posting_date) > getdate(expiry_date):
diff --git a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py
index 7a1b8c0..0ec4e1c 100644
--- a/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py
+++ b/erpnext/stock/report/delivery_note_trends/delivery_note_trends.py
@@ -45,4 +45,5 @@
"datasets": [{"name": _("Total Delivered Amount"), "values": datapoints}],
},
"type": "bar",
+ "fieldtype": "Currency",
}
diff --git a/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.py b/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.py
index 23e3c8a..df01b14 100644
--- a/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.py
+++ b/erpnext/stock/report/incorrect_stock_value_report/incorrect_stock_value_report.py
@@ -4,6 +4,8 @@
import frappe
from frappe import _
+from frappe.query_builder import Field
+from frappe.query_builder.functions import Min, Timestamp
from frappe.utils import add_days, getdate, today
import erpnext
@@ -28,7 +30,7 @@
def get_unsync_date(filters):
date = filters.from_date
if not date:
- date = frappe.db.sql(""" SELECT min(posting_date) from `tabStock Ledger Entry`""")
+ date = (frappe.qb.from_("Stock Ledger Entry").select(Min(Field("posting_date")))).run()
date = date[0][0]
if not date:
@@ -54,22 +56,27 @@
result = []
voucher_wise_dict = {}
- data = frappe.db.sql(
- """
- SELECT
- name, posting_date, posting_time, voucher_type, voucher_no,
- stock_value_difference, stock_value, warehouse, item_code
- FROM
- `tabStock Ledger Entry`
- WHERE
- posting_date
- = %s and company = %s
- and is_cancelled = 0
- ORDER BY timestamp(posting_date, posting_time) asc, creation asc
- """,
- (from_date, report_filters.company),
- as_dict=1,
- )
+ sle = frappe.qb.DocType("Stock Ledger Entry")
+ data = (
+ frappe.qb.from_(sle)
+ .select(
+ sle.name,
+ sle.posting_date,
+ sle.posting_time,
+ sle.voucher_type,
+ sle.voucher_no,
+ sle.stock_value_difference,
+ sle.stock_value,
+ sle.warehouse,
+ sle.item_code,
+ )
+ .where(
+ (sle.posting_date == from_date)
+ & (sle.company == report_filters.company)
+ & (sle.is_cancelled == 0)
+ )
+ .orderby(Timestamp(sle.posting_date, sle.posting_time), sle.creation)
+ ).run(as_dict=True)
for d in data:
voucher_wise_dict.setdefault((d.item_code, d.warehouse), []).append(d)
diff --git a/erpnext/stock/report/item_price_stock/item_price_stock.py b/erpnext/stock/report/item_price_stock/item_price_stock.py
index 15218e6..1b07f59 100644
--- a/erpnext/stock/report/item_price_stock/item_price_stock.py
+++ b/erpnext/stock/report/item_price_stock/item_price_stock.py
@@ -62,22 +62,28 @@
def get_item_price_qty_data(filters):
- conditions = ""
- if filters.get("item_code"):
- conditions += "where a.item_code=%(item_code)s"
+ item_price = frappe.qb.DocType("Item Price")
+ bin = frappe.qb.DocType("Bin")
- item_results = frappe.db.sql(
- """select a.item_code, a.item_name, a.name as price_list_name,
- a.brand as brand, b.warehouse as warehouse, b.actual_qty as actual_qty
- from `tabItem Price` a left join `tabBin` b
- ON a.item_code = b.item_code
- {conditions}""".format(
- conditions=conditions
- ),
- filters,
- as_dict=1,
+ query = (
+ frappe.qb.from_(item_price)
+ .left_join(bin)
+ .on(item_price.item_code == bin.item_code)
+ .select(
+ item_price.item_code,
+ item_price.item_name,
+ item_price.name.as_("price_list_name"),
+ item_price.brand.as_("brand"),
+ bin.warehouse.as_("warehouse"),
+ bin.actual_qty.as_("actual_qty"),
+ )
)
+ if filters.get("item_code"):
+ query = query.where(item_price.item_code == filters.get("item_code"))
+
+ item_results = query.run(as_dict=True)
+
price_list_names = list(set(item.price_list_name for item in item_results))
buying_price_map = get_price_map(price_list_names, buying=1)
diff --git a/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py b/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py
index fe2d55a..b62a6ee 100644
--- a/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py
+++ b/erpnext/stock/report/purchase_receipt_trends/purchase_receipt_trends.py
@@ -46,4 +46,5 @@
},
"type": "bar",
"colors": ["#5e64ff"],
+ "fieldtype": "Currency",
}
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index 5030964..9ca40c3 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -1053,7 +1053,7 @@
updated_values = {"actual_qty": data.qty_after_transaction, "stock_value": data.stock_value}
if data.valuation_rate is not None:
updated_values["valuation_rate"] = data.valuation_rate
- frappe.db.set_value("Bin", bin_name, updated_values)
+ frappe.db.set_value("Bin", bin_name, updated_values, update_modified=True)
def get_previous_sle_of_current_voucher(args, exclude_current_voucher=False):
diff --git a/erpnext/templates/includes/footer/footer_extension.html b/erpnext/templates/includes/footer/footer_extension.html
index c7f0d06..0072dc2 100644
--- a/erpnext/templates/includes/footer/footer_extension.html
+++ b/erpnext/templates/includes/footer/footer_extension.html
@@ -6,7 +6,7 @@
aria-label="{{ _('Your email address...') }}"
aria-describedby="footer-subscribe-button">
<div class="input-group-append">
- <button class="btn btn-sm btn-default"
+ <button class="btn btn-sm btn-secondary pl-3 pr-3 ml-2"
type="button" id="footer-subscribe-button">{{ _("Get Updates") }}</button>
</div>
</div>
diff --git a/erpnext/templates/includes/macros.html b/erpnext/templates/includes/macros.html
index f56dc3a..dc9ee23 100644
--- a/erpnext/templates/includes/macros.html
+++ b/erpnext/templates/includes/macros.html
@@ -1,5 +1,5 @@
{% macro product_image_square(website_image, css_class="") %}
-<div class="product-image product-image-square
+<div class="product-image product-image-square h-100 rounded
{% if not website_image -%} missing-image {%- endif %} {{ css_class }}"
{% if website_image -%}
style="background-image: url('{{ frappe.utils.quoted(website_image) | abs_url }}');"
diff --git a/erpnext/templates/includes/order/order_macros.html b/erpnext/templates/includes/order/order_macros.html
index 3f2c1f2..d95b289 100644
--- a/erpnext/templates/includes/order/order_macros.html
+++ b/erpnext/templates/includes/order/order_macros.html
@@ -3,7 +3,7 @@
{% macro item_name_and_description(d) %}
<div class="row item_name_and_description">
<div class="col-xs-4 col-sm-2 order-image-col">
- <div class="order-image">
+ <div class="order-image h-100">
{% if d.thumbnail or d.image %}
{{ product_image(d.thumbnail or d.image, no_border=True) }}
{% else %}
@@ -18,6 +18,9 @@
<div class="text-muted small item-description">
{{ html2text(d.description) | truncate(140) }}
</div>
+ <span class="text-muted mt-2 d-l-n order-qty">
+ {{ _("Qty ") }}({{ d.get_formatted("qty") }})
+ </span>
</div>
</div>
{% endmacro %}
diff --git a/erpnext/templates/includes/order/order_taxes.html b/erpnext/templates/includes/order/order_taxes.html
index b821e62..0060ab3 100644
--- a/erpnext/templates/includes/order/order_taxes.html
+++ b/erpnext/templates/includes/order/order_taxes.html
@@ -1,84 +1,111 @@
{% if doc.taxes %}
-<tr>
- <td class="text-left" colspan="1">
- {{ _("Net Total") }}
- </td>
- <td class="text-right totals" colspan="3">
- {{ doc.get_formatted("net_total") }}
- </td>
-</tr>
+ <div class="w-100 order-taxes mt-5">
+ <div class="col-4 d-flex border-btm pb-5">
+ <div class="item-grand-total col-8">
+ {{ _("Net Total") }}
+ </div>
+ <div class="item-grand-total col-4 text-right pr-0">
+ {{ doc.get_formatted("net_total") }}
+ </div>
+ </div>
+ </div>
{% endif %}
{% for d in doc.taxes %}
{% if d.base_tax_amount %}
- <tr>
- <td class="text-left" colspan="1">
- {{ d.description }}
- </td>
- <td class="text-right totals" colspan="3">
- {{ d.get_formatted("base_tax_amount") }}
- </td>
- </tr>
+ <div class="order-taxes w-100 mt-5">
+ <div class="col-4 d-flex border-btm pb-5">
+ <div class="item-grand-total col-8">
+ {{ d.description }}
+ </div>
+ <div class="item-grand-total col-4 text-right pr-0">
+ {{ doc.get_formatted("net_total") }}
+ </div>
+ </div>
+ </div>
{% endif %}
{% endfor %}
{% if doc.doctype == 'Quotation' %}
{% if doc.coupon_code %}
- <tr>
- <td class="text-left total-discount" colspan="1">
- {{ _("Savings") }}
- </td>
- <td class="text-right tot_quotation_discount total-discount totals" colspan="3">
- {% set tot_quotation_discount = [] %}
- {%- for item in doc.items -%}
- {% if tot_quotation_discount.append((((item.price_list_rate * item.qty)
- * item.discount_percentage) / 100)) %}
- {% endif %}
- {% endfor %}
- {{ frappe.utils.fmt_money((tot_quotation_discount | sum),currency=doc.currency) }}
- </td>
- </tr>
+ <div class="w-100 mt-5 order-taxes font-weight-bold">
+ <div class="col-4 d-flex border-btm pb-5">
+ <div class="item-grand-total col-8">
+ {{ _("Savings") }}
+ </div>
+ <div class="item-grand-total col-4 text-right pr-0">
+ {% set tot_quotation_discount = [] %}
+ {%- for item in doc.items -%}
+ {% if tot_quotation_discount.append((((item.price_list_rate * item.qty)
+ * item.discount_percentage) / 100)) %}
+ {% endif %}
+ {% endfor %}
+ {{ frappe.utils.fmt_money((tot_quotation_discount | sum),currency=doc.currency) }} </div>
+ </div>
+ </div>
{% endif %}
{% endif %}
{% if doc.doctype == 'Sales Order' %}
{% if doc.coupon_code %}
- <tr>
- <td class="text-left total-discount" colspan="2" style="padding-right: 2rem;">
- {{ _("Applied Coupon Code") }}
- </td>
- <td class="text-right total-discount">
- <span>
- {%- for row in frappe.get_all(doctype="Coupon Code",
- fields=["coupon_code"], filters={ "name":doc.coupon_code}) -%}
- <span>{{ row.coupon_code }}</span>
- {% endfor %}
- </span>
- </td>
- </tr>
- <tr>
- <td class="text-left total-discount" colspan="2">
- {{ _("Savings") }}
- </td>
- <td class="text-right total-discount">
- <span>
- {% set tot_SO_discount = [] %}
- {%- for item in doc.items -%}
- {% if tot_SO_discount.append((((item.price_list_rate * item.qty)
- * item.discount_percentage) / 100)) %}{% endif %}
- {% endfor %}
- {{ frappe.utils.fmt_money((tot_SO_discount | sum),currency=doc.currency) }}
- </span>
- </td>
- </tr>
+ <div class="w-100 order-taxes mt-5">
+ <div class="col-4 d-flex border-btm pb-5">
+ <div class="item-grand-total col-8">
+ {{ _("Total Amount") }}
+ </div>
+ <div class="item-grand-total col-4 text-right pr-0">
+ <span>
+ {% set total_amount = [] %}
+ {%- for item in doc.items -%}
+ {% if total_amount.append((item.price_list_rate * item.qty)) %}{% endif %}
+ {% endfor %}
+ {{ frappe.utils.fmt_money((total_amount | sum),currency=doc.currency) }}
+ </span>
+ </div>
+ </div>
+ </div>
+ <div class="order-taxes w-100 mt-5">
+ <div class="col-4 d-flex">
+ <div class="item-grand-total col-8">
+ {{ _("Applied Coupon Code") }}
+ </div>
+ <div class="item-grand-total col-4 text-right pr-0">
+ <span>
+ {%- for row in frappe.get_all(doctype="Coupon Code",
+ fields=["coupon_code"], filters={ "name":doc.coupon_code}) -%}
+ <span>{{ row.coupon_code }}</span>
+ {% endfor %}
+ </span>
+ </div>
+ </div>
+ </div>
+ <div class="order-taxes mt-5">
+ <div class="col-4 d-flex border-btm pb-5">
+ <div class="item-grand-total col-8">
+ {{ _("Savings") }}
+ </div>
+ <div class="item-grand-total col-4 text-right pr-0">
+ <span>
+ {% set tot_SO_discount = [] %}
+ {%- for item in doc.items -%}
+ {% if tot_SO_discount.append((((item.price_list_rate * item.qty)
+ * item.discount_percentage) / 100)) %}{% endif %}
+ {% endfor %}
+ {{ frappe.utils.fmt_money((tot_SO_discount | sum),currency=doc.currency) }}
+ </span>
+ </div>
+ </div>
+ </div>
{% endif %}
{% endif %}
-<tr>
- <th class="text-left item-grand-total" colspan="1">
- {{ _("Grand Total") }}
- </th>
- <th class="text-right item-grand-total totals" colspan="3">
- {{ doc.get_formatted("grand_total") }}
- </th>
-</tr>
+<div class="w-100 mt-5 order-taxes font-weight-bold">
+ <div class="col-4 d-flex">
+ <div class="item-grand-total col-8">
+ {{ _("Grand Total") }}
+ </div>
+ <div class="item-grand-total col-4 text-right pr-0">
+ {{ doc.get_formatted("grand_total") }}
+ </div>
+ </div>
+</div>
diff --git a/erpnext/templates/includes/transaction_row.html b/erpnext/templates/includes/transaction_row.html
index 3cfb8d8..72d498c 100644
--- a/erpnext/templates/includes/transaction_row.html
+++ b/erpnext/templates/includes/transaction_row.html
@@ -1,20 +1,22 @@
<div class="web-list-item transaction-list-item">
- <div class="row">
+ <div class="row align-items-center">
<div class="col-sm-4">
- <span class="indicator small {{ doc.indicator_color or ("blue" if doc.docstatus==1 else "gray") }}">
- {{ doc.name }}</span>
+ <span class="list-item-name font-weight-bold">{{ doc.name }}</span>
<div class="small text-muted transaction-time"
title="{{ frappe.utils.format_datetime(doc.modified, "medium") }}">
{{ frappe.utils.global_date_format(doc.modified) }}
</div>
</div>
- <div class="col-sm-5">
+ <div class="col-sm-3">
+ <span class="indicator-pill {{ doc.indicator_color or ("blue" if doc.docstatus==1 else "gray") }} list-item-status">{{doc.status}}</span>
+ </div>
+ <div class="col-sm-2">
<div class="small text-muted items-preview ellipsis ellipsis-width">
{{ doc.items_preview }}
</div>
</div>
{% if doc.get('grand_total') %}
- <div class="col-sm-3 text-right bold">
+ <div class="col-sm-3 text-right font-weight-bold item-total">
{{ doc.get_formatted("grand_total") }}
</div>
{% endif %}
diff --git a/erpnext/templates/pages/demo.html b/erpnext/templates/pages/demo.html
deleted file mode 100644
index f9934a3..0000000
--- a/erpnext/templates/pages/demo.html
+++ /dev/null
@@ -1,77 +0,0 @@
-{% extends "templates/web.html" %}
-
-{% block script %}
-<script>
-$(document).ready(function() {
- $("#login_btn").click(function() {
- var me = this;
- $(this).html("Logging In...").prop("disabled", true);
- frappe.call({
- "method": "login",
- args: {
- usr: "demo@erpnext.com",
- pwd: "Demo1234567!!!",
- lead_email: $("#lead-email").val(),
- },
- callback: function(r) {
- $(me).prop("disabled", false);
- if(r.exc) {
- alert("Error, please contact support@erpnext.com");
- } else {
- console.log("Logged In");
- window.location.href = "desk";
- }
- }
- })
- return false;
- })
- .prop("disabled", false);
-})
-
-</script>
-{% endblock %}
-
-{% block style %}
-<style>
- footer, .navbar {
- display: none;
- }
- .page-content {
- right: 0%;
- width: 100%;
- }
- {% include "templates/styles/card_style.css" %}
- header, footer {
- display: none;
- }
- html, body {
- background-color: #f5f7fa;
- }
-</style>
-{% endblock %}
-
-{% block title %}
-{{ _("ERPNext Demo") }}
-{% endblock %}
-
-{% block page_content %}
-<div class='page-card'>
-
- <div class='page-card-head'>
- <span class='indicator blue'>
- {{ _("ERPNext Demo") }}</span>
- </div>
- <!-- <img src="/assets/erpnext/images/erp-icon.svg" style="max-width: 40px; max-height: 40px;"> -->
- <p>Some functionality is disabled for the demo and the data will be cleared regularly.</p>
- <div><button type="submit" id="login_btn" class="btn btn-primary btn-sm">Launch Demo</button></div>
-</div>
-
-
-<p class='text-muted text-center small' style='margin-top: -20px;'><a href="https://erpnext.com/pricing">Start a free 14-day trial </a>
-</p>
-<style>
-html, body {
- background-color: #f5f7fa;
-}
-</style>
-{% endblock %}
diff --git a/erpnext/templates/pages/order.html b/erpnext/templates/pages/order.html
index a10870d..6b354b2 100644
--- a/erpnext/templates/pages/order.html
+++ b/erpnext/templates/pages/order.html
@@ -5,149 +5,159 @@
{% include "templates/includes/breadcrumbs.html" %}
{% endblock %}
-{% block title %}{{ doc.name }}{% endblock %}
+{% block title %}
+ {{ doc.name }}
+{% endblock %}
{% block header %}
- <h2 class="m-0">{{ doc.name }}</h2>
+ <h3 class="m-0">{{ doc.name }}</h3>
{% endblock %}
{% block header_actions %}
- <div class="dropdown">
- <button class="btn btn-outline-secondary dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
- <span class="font-md">{{ _('Actions') }}</span>
- <b class="caret"></b>
- </button>
- <ul class="dropdown-menu dropdown-menu-right" role="menu">
- {% if doc.doctype == 'Purchase Order' %}
- <a class="dropdown-item" href="/api/method/erpnext.buying.doctype.purchase_order.purchase_order.make_purchase_invoice_from_portal?purchase_order_name={{ doc.name }}" data-action="make_purchase_invoice">{{ _("Make Purchase Invoice") }}</a>
- {% endif %}
- <a class="dropdown-item" href='/printview?doctype={{ doc.doctype}}&name={{ doc.name }}&format={{ print_format }}'
- target="_blank" rel="noopener noreferrer">
- {{ _("Print") }}
- </a>
- </ul>
+ <div class="row">
+ <div class="dropdown">
+ <button class="btn btn-sm btn-secondary dropdown-toggle" data-toggle="dropdown" aria-expanded="false">
+ <span class="font-md">{{ _('Actions') }}</span>
+ <b class="caret"></b>
+ </button>
+ <ul class="dropdown-menu dropdown-menu-right" role="menu">
+ {% if doc.doctype == 'Purchase Order' and show_make_pi_button %}
+ <a class="dropdown-item"
+ href="/api/method/erpnext.buying.doctype.purchase_order.purchase_order.make_purchase_invoice_from_portal?purchase_order_name={{ doc.name }}"
+ data-action="make_purchase_invoice">{{ _("Make Purchase Invoice") }}
+ </a>
+ {% endif %}
+ <a class="dropdown-item"
+ href='/printview?doctype={{ doc.doctype}}&name={{ doc.name }}&format={{ print_format }}' target="_blank"
+ rel="noopener noreferrer">
+ {{ _("Print") }}
+ </a>
+ </ul>
+ </div>
+ <div class="form-column col-sm-6">
+ <div class="page-header-actions-block" data-html-block="header-actions">
+ <p>
+ <a href="/api/method/erpnext.accounts.doctype.payment_request.payment_request.make_payment_request?dn={{ doc.name }}&dt={{ doc.doctype }}&submit_doc=1&order_type=Shopping Cart"
+ class="btn btn-primary btn-sm" id="pay-for-order">
+ {{ _("Pay") }} {{doc.get_formatted("grand_total") }}
+ </a>
+ </p>
+ </div>
+ </div>
</div>
{% endblock %}
{% block page_content %}
- <div class="row transaction-subheading">
- <div class="col-6">
- <span class="font-md indicator-pill {{ doc.indicator_color or ("blue" if doc.docstatus==1 else "darkgrey") }}">
- {% if doc.doctype == "Quotation" and not doc.docstatus %}
- {{ _("Pending") }}
- {% else %}
- {{ _(doc.get('indicator_title')) or _(doc.status) or _("Submitted") }}
+ <div>
+ <div class="row transaction-subheading mt-1">
+ <div class="col-6 text-muted small mt-1">
+ {{ frappe.utils.format_date(doc.transaction_date, 'medium') }}
+ {% if doc.valid_till %}
+ <p>
+ {{ _("Valid Till") }}: {{ frappe.utils.format_date(doc.valid_till, 'medium') }}
+ </p>
{% endif %}
- </span>
+ </div>
</div>
- <div class="col-6 text-muted text-right small pt-3">
- {{ frappe.utils.format_date(doc.transaction_date, 'medium') }}
- {% if doc.valid_till %}
- <p>
- {{ _("Valid Till") }}: {{ frappe.utils.format_date(doc.valid_till, 'medium') }}
- </p>
- {% endif %}
- </div>
- </div>
-
- <p class="small my-3">
- {%- set party_name = doc.supplier_name if doc.doctype in ['Supplier Quotation', 'Purchase Invoice', 'Purchase Order'] else doc.customer_name %}
- <b>{{ party_name }}</b>
-
- {% if doc.contact_display and doc.contact_display != party_name %}
- <br>
- {{ doc.contact_display }}
- {% endif %}
- </p>
-
- {% if doc._header %}
- {{ doc._header }}
- {% endif %}
-
- <div class="order-container">
- <!-- items -->
- <table class="order-item-table w-100 table">
- <thead class="order-items order-item-header">
- <th width="60%">
- {{ _("Item") }}
- </th>
- <th width="20%" class="text-right">
- {{ _("Quantity") }}
- </th>
- <th width="20%" class="text-right">
- {{ _("Amount") }}
- </th>
- </thead>
- <tbody>
- {% for d in doc.items %}
- <tr class="order-items">
- <td>
- {{ item_name_and_description(d) }}
- </td>
- <td class="text-right">
- {{ d.qty }}
- {% if d.delivered_qty is defined and d.delivered_qty != None %}
- <p class="text-muted small">{{ _("Delivered") }} {{ d.delivered_qty }}</p>
+ <div class="row indicator-container mt-2">
+ <div class="col-10">
+ <span class="indicator-pill {{ doc.indicator_color or (" blue" if doc.docstatus==1 else "darkgrey" ) }}">
+ {% if doc.doctype == "Quotation" and not doc.docstatus %}
+ {{ _("Pending") }}
+ {% else %}
+ {{ _(doc.get('indicator_title')) or _(doc.status) or _("Submitted") }}
{% endif %}
- </td>
- <td class="text-right">
- {{ d.get_formatted("amount") }}
- <p class="text-muted small">{{ _("Rate:") }} {{ d.get_formatted("rate") }}</p>
- </td>
- </tr>
- {% endfor %}
- </tbody>
- </table>
- <!-- taxes -->
- <div class="order-taxes d-flex justify-content-end">
- <table>
+ </span>
+ </div>
+ <div class="text-right col-2">
+ {%- set party_name = doc.supplier_name if doc.doctype in ['Supplier Quotation', 'Purchase Invoice', 'Purchase
+ Order'] else doc.customer_name %}
+ <b>{{ party_name }}</b>
+
+ {% if doc.contact_display and doc.contact_display != party_name %}
+ <br>
+ {{ doc.contact_display }}
+ {% endif %}
+ </div>
+ </div>
+
+ {% if doc._header %}
+ {{ doc._header }}
+ {% endif %}
+
+ <div class="order-container mt-4">
+ <!-- items -->
+ <div class="w-100">
+ <div class="order-items order-item-header mb-1 row text-muted">
+ <span class="col-5">
+ {{ _("Item") }}
+ </span>
+ <span class="d-s-n col-3">
+ {{ _("Quantity") }}
+ </span>
+ <span class="col-2 pl-10">
+ {{ _("Rate") }}
+ </span>
+ <span class="col-2 text-right">
+ {{ _("Amount") }}
+ </span>
+ </div>
+ {% for d in doc.items %}
+ <div class="order-items row align-items-center">
+ <span class="order-item-name col-5 pr-0">
+ {{ item_name_and_description(d) }}
+ </span>
+
+ <span class="d-s-n col-3 pl-10">
+ {{ d.get_formatted("qty") }}
+ </span>
+ <span class="order-rate pl-4 col-2">
+ {{ d.get_formatted("rate") }}
+ </span>
+ <span class="col-2 text-right">
+ {{ d.get_formatted("amount") }}
+ </span>
+ </div>
+ {% endfor %}
+ </div>
+
+ <!-- taxes -->
+ <div class="">
{% include "erpnext/templates/includes/order/order_taxes.html" %}
- </table>
+ </div>
</div>
</div>
{% if enabled_checkout and ((doc.doctype=="Sales Order" and doc.per_billed <= 0)
- or (doc.doctype=="Sales Invoice" and doc.outstanding_amount > 0)) %}
+ or (doc.doctype=="Sales Invoice" and doc.outstanding_amount> 0)) %}
<div class="panel panel-default">
- <div class="panel-heading">
- <div class="row">
- <div class="form-column col-sm-6 address-title">
- <strong>Payment</strong>
- </div>
- </div>
- </div>
<div class="panel-collapse">
<div class="panel-body text-muted small">
<div class="row">
<div class="form-column col-sm-6">
{% if available_loyalty_points %}
+ <div class="panel-heading">
+ <div class="row">
+ <div class="form-column col-sm-6 address-title">
+ <strong>Loyalty Points</strong>
+ </div>
+ </div>
+ </div>
+
<div class="form-group">
<div class="h6">Enter Loyalty Points</div>
<div class="control-input-wrapper">
<div class="control-input">
- <input class="form-control" type="number" min="0" max="{{ available_loyalty_points }}" id="loyalty-point-to-redeem">
+ <input class="form-control" type="number" min="0"
+ max="{{ available_loyalty_points }}" id="loyalty-point-to-redeem">
</div>
- <p class="help-box small text-muted d-none d-sm-block"> Available Points: {{ available_loyalty_points }} </p>
+ <p class="help-box small text-muted d-none d-sm-block"> Available Points: {{
+ available_loyalty_points }} </p>
</div>
</div>
{% endif %}
</div>
-
- <div class="form-column col-sm-6">
- <div id="loyalty-points-status" style="text-align: right"></div>
- <div class="page-header-actions-block" data-html-block="header-actions">
- <p class="mt-2" style="float: right;">
- <a href="/api/method/erpnext.accounts.doctype.payment_request.payment_request.make_payment_request?dn={{ doc.name }}&dt={{ doc.doctype }}&submit_doc=1&order_type=Shopping Cart"
- class="btn btn-primary btn-sm"
- id="pay-for-order">
- {{ _("Pay") }} {{ doc.get_formatted("grand_total") }}
- </a>
- </p>
- </div>
- </div>
-
</div>
-
</div>
</div>
</div>
@@ -172,17 +182,17 @@
</div>
</div>
{% endif %}
- </div>
{% if doc.terms %}
<div class="terms-and-condition text-muted small">
- <hr><p>{{ doc.terms }}</p>
+ <hr>
+ <p>{{ doc.terms }}</p>
</div>
{% endif %}
{% endblock %}
{% block script %}
- <script> {% include "templates/pages/order.js" %} </script>
+ <script> {% include "templates/pages/order.js" %}</script>
<script>
window.doc_info = {
customer: '{{doc.customer}}',
@@ -192,4 +202,4 @@
currency: '{{ doc.currency }}'
}
</script>
-{% endblock %}
+{% endblock %}
\ No newline at end of file
diff --git a/erpnext/templates/pages/order.py b/erpnext/templates/pages/order.py
index 3e6d57a..e1e12bd 100644
--- a/erpnext/templates/pages/order.py
+++ b/erpnext/templates/pages/order.py
@@ -52,6 +52,9 @@
)
context.available_loyalty_points = int(loyalty_program_details.get("loyalty_points"))
+ # show Make Purchase Invoice button based on permission
+ context.show_make_pi_button = frappe.has_permission("Purchase Invoice", "create")
+
def get_attachments(dt, dn):
return frappe.get_all(
diff --git a/erpnext/translations/fr.csv b/erpnext/translations/fr.csv
index b207461..3ba5ade 100644
--- a/erpnext/translations/fr.csv
+++ b/erpnext/translations/fr.csv
@@ -785,7 +785,7 @@
Default BOM not found for Item {0} and Project {1},La nomenclature par défaut n'a pas été trouvée pour l'Article {0} et le Projet {1},
Default Letter Head,En-Tête de Courrier par Défaut,
Default Tax Template,Modèle de Taxes par Défaut,
-Default Unit of Measure for Item {0} cannot be changed directly because you have already made some transaction(s) with another UOM. You will need to create a new Item to use a different Default UOM.,L’Unité de Mesure par Défaut pour l’Article {0} ne peut pas être modifiée directement parce que vous avez déjà fait une (des) transaction (s) avec une autre unité de mesure. Vous devez créer un nouvel article pour utiliser une UDM par défaut différente.,
+Default Unit of Measure for Item {0} cannot be changed directly because you have already made some transaction(s) with another UOM. You will need to create a new Item to use a different Default UOM.,L’Unité de Mesure par Défaut pour l’Article {0} ne peut pas être modifiée directement parce que vous avez déjà fait une (des) transaction (s) avec une autre unité de mesure. Vous devez créer un nouvel article pour utiliser une UdM par défaut différente.,
Default Unit of Measure for Variant '{0}' must be same as in Template '{1}',L’Unité de mesure par défaut pour la variante '{0}' doit être la même que dans le Modèle '{1}',
Default settings for buying transactions.,Paramètres par défaut pour les transactions d'achat.,
Default settings for selling transactions.,Paramètres par défaut pour les transactions de vente.,
@@ -838,7 +838,7 @@
"Difference Account must be a Asset/Liability type account, since this Stock Reconciliation is an Opening Entry","Le Compte d’Écart doit être un compte de type Actif / Passif, puisque cette Réconciliation de Stock est une écriture d'à-nouveau",
Difference Amount,Écart de Montant,
Difference Amount must be zero,L’Écart de Montant doit être égal à zéro,
-Different UOM for items will lead to incorrect (Total) Net Weight value. Make sure that Net Weight of each item is in the same UOM.,Différentes UDM pour les articles conduira à un Poids Net (Total) incorrect . Assurez-vous que le Poids Net de chaque article a la même unité de mesure .,
+Different UOM for items will lead to incorrect (Total) Net Weight value. Make sure that Net Weight of each item is in the same UOM.,Différentes UdM pour les articles conduira à un Poids Net (Total) incorrect . Assurez-vous que le Poids Net de chaque article a la même unité de mesure .,
Direct Expenses,Charges Directes,
Direct Income,Revenu direct,
Disable,Désactiver,
@@ -1409,7 +1409,7 @@
Lab Test Report,Rapport de test de laboratoire,
Lab Test Sample,Échantillon de test de laboratoire,
Lab Test Template,Modèle de test de laboratoire,
-Lab Test UOM,UDM de test de laboratoire,
+Lab Test UOM,UdM de test de laboratoire,
Lab Tests and Vital Signs,Tests de laboratoire et signes vitaux,
Lab result datetime cannot be before testing datetime,La date et l'heure du résultat de laboratoire ne peuvent pas être avant la date et l'heure du test,
Lab testing datetime cannot be before collection datetime,La date et l'heure du test de laboratoire ne peuvent pas être avant la date et l'heure de collecte,
@@ -2806,7 +2806,7 @@
Stock Reports,Rapports de stock,
Stock Summary,Résumé du Stock,
Stock Transactions,Transactions du Stock,
-Stock UOM,UDM du Stock,
+Stock UOM,UdM du Stock,
Stock Value,Valeur du Stock,
Stock balance in Batch {0} will become negative {1} for Item {2} at Warehouse {3},Solde du stock dans le Lot {0} deviendra négatif {1} pour l'Article {2} à l'Entrepôt {3},
Stock cannot be updated against Delivery Note {0},Stock ne peut pas être mis à jour pour le Bon de Livraison {0},
@@ -3161,9 +3161,9 @@
Trialling,Essai,
Type of Business,Type de commerce,
Types of activities for Time Logs,Types d'activités pour Journaux de Temps,
-UOM,UDM,
-UOM Conversion factor is required in row {0},Facteur de conversion de l'UDM est obligatoire dans la ligne {0},
-UOM coversion factor required for UOM: {0} in Item: {1},Facteur de coversion UDM requis pour l'UDM : {0} dans l'Article : {1},
+UOM,UdM,
+UOM Conversion factor is required in row {0},Facteur de conversion de l'UdM est obligatoire dans la ligne {0},
+UOM coversion factor required for UOM: {0} in Item: {1},Facteur de coversion UdM requis pour l'UdM : {0} dans l'Article : {1},
URL,URL,
Unable to find DocType {0},Impossible de trouver le DocType {0},
Unable to find exchange rate for {0} to {1} for key date {2}. Please create a Currency Exchange record manually,Impossible de trouver le taux de change pour {0} à {1} pour la date clé {2}. Veuillez créer une entrée de taux de change manuellement,
@@ -3294,7 +3294,7 @@
Week,Semaine,
Weekdays,Jours de la semaine,
Weekly,Hebdomadaire,
-"Weight is mentioned,\nPlease mention ""Weight UOM"" too","Poids est mentionné,\nVeuillez aussi mentionner ""UDM de Poids""",
+"Weight is mentioned,\nPlease mention ""Weight UOM"" too","Poids est mentionné,\nVeuillez aussi mentionner ""UdM de Poids""",
Welcome email sent,Email de bienvenue envoyé,
Welcome to ERPNext,Bienvenue sur ERPNext,
What do you need help with?,Avec quoi avez vous besoin d'aide ?,
@@ -4938,12 +4938,15 @@
Coupon Code Based,Code de coupon basé,
Discount on Other Item,Remise sur un autre article,
Apply Rule On Other,Appliquer la règle sur autre,
-Party Information,Informations sur la fête,
+Party Information,Informations sur le tier,
Quantity and Amount,Quantité et montant,
Min Qty,Qté Min,
Max Qty,Qté Max,
-Min Amt,Min Amt,
-Max Amt,Max Amt,
+Min Amt,Montant Min,
+Max Amt,Montant Max,
+"If rate is zero them item will be treated as ""Free Item""",Si le prix est à 0 alors l'article sera traité comme article gratuit
+Is Recursive,Est récursif
+"Discounts to be applied in sequential ranges like buy 1 get 1, buy 2 get 2, buy 3 get 3 and so on","La remise sera appliquée séquentiellement telque : acheter 1 => recupérer 1, acheter 2 => recupérer 2, acheter 3 => recupérer 3, etc..."
Period Settings,Paramètres de période,
Margin,Marge,
Margin Type,Type de Marge,
@@ -5053,7 +5056,7 @@
Received Qty,Qté Reçue,
Accepted Qty,Quantité acceptée,
Rejected Qty,Qté Rejetée,
-UOM Conversion Factor,Facteur de Conversion de l'UDM,
+UOM Conversion Factor,Facteur de Conversion de l'UdM,
Discount on Price List Rate (%),Remise sur la Liste des Prix (%),
Price List Rate (Company Currency),Taux de la Liste de Prix (Devise Société),
Rate (Company Currency),Prix (Devise Société),
@@ -5085,7 +5088,7 @@
Item Weight Details,Détails du poids de l'article,
Weight Per Unit,Poids par unité,
Total Weight,Poids total,
-Weight UOM,UDM de Poids,
+Weight UOM,UdM de Poids,
Page Break,Saut de Page,
Consider Tax or Charge for,Tenir Compte de la Taxe et des Frais pour,
Valuation and Total,Valorisation et Total,
@@ -5153,7 +5156,7 @@
Sales Invoice Item,Article de la Facture de Vente,
Customer's Item Code,Code de l'Article du Client,
Brand Name,Nom de la Marque,
-Qty as per Stock UOM,Qté par UDM du Stock,
+Qty as per Stock UOM,Qté par UdM du Stock,
Discount and Margin,Remise et Marge,
Rate With Margin,Prix Avec Marge,
Discount (%) on Price List Rate with Margin,Remise (%) sur le prix de la Liste de Prix avec la Marge,
@@ -5501,7 +5504,7 @@
Returned Qty,Qté Retournée,
Purchase Order Item Supplied,Article Fourni depuis la Commande d'Achat,
BOM Detail No,N° de Détail de la nomenclature,
-Stock Uom,UDM du Stock,
+Stock Uom,UdM du Stock,
Raw Material Item Code,Code d’Article de Matière Première,
Supplied Qty,Qté Fournie,
Purchase Receipt Item Supplied,Articles Fournis du Reçus d’Achat,
@@ -6149,7 +6152,7 @@
Dosage,Dosage,
Dosage by Time Interval,Dosage par intervalle de temps,
Interval,Intervalle,
-Interval UOM,UDM d'Intervalle,
+Interval UOM,UdM d'Intervalle,
Hour,Heure,
Update Schedule,Mettre à Jour le Calendrier,
Exercise,Exercice,
@@ -7023,7 +7026,7 @@
Diesel,Diesel,
Natural Gas,Gaz Naturel,
Electric,Électrique,
-Fuel UOM,UDM Carburant,
+Fuel UOM,UdM Carburant,
Last Carbon Check,Dernière Vérification Carbone,
Wheels,Roues,
Doors,Portes,
@@ -7182,7 +7185,7 @@
Quantity of item obtained after manufacturing / repacking from given quantities of raw materials,Quantité d'article obtenue après production / reconditionnement des quantités données de matières premières,
Set rate of sub-assembly item based on BOM,Définir le prix des articles de sous-assemblage en fonction de la nomenclature,
Allow Alternative Item,Autoriser un article alternatif,
-Item UOM,UDM de l'Article,
+Item UOM,UdM de l'Article,
Conversion Rate,Taux de Conversion,
Rate Of Materials Based On,Prix des Matériaux Basé sur,
With Operations,Avec des Opérations,
@@ -7926,7 +7929,7 @@
For reference,Pour référence,
Territory Targets,Objectifs Régionaux,
Set Item Group-wise budgets on this Territory. You can also include seasonality by setting the Distribution.,Définir des budgets par Groupes d'Articles sur ce Territoire. Vous pouvez également inclure de la saisonnalité en définissant la Répartition.,
-UOM Name,Nom UDM,
+UOM Name,Nom UdM,
Check this to disallow fractions. (for Nos),Cochez cette case pour interdire les fractions. (Pour les numéros),
Website Item Group,Groupe d'Articles du Site Web,
Cross Listing of Item in multiple groups,Liste Croisée d'Articles dans plusieurs groupes,
@@ -8198,10 +8201,10 @@
If more than one package of the same type (for print),Si plus d'un paquet du même type (pour l'impression),
Package Weight Details,Détails du Poids du Paquet,
The net weight of this package. (calculated automatically as sum of net weight of items),Le poids net de ce paquet. (Calculé automatiquement comme la somme du poids net des articles),
-Net Weight UOM,UDM Poids Net,
+Net Weight UOM,UdM Poids Net,
Gross Weight,Poids Brut,
The gross weight of the package. Usually net weight + packaging material weight. (for print),Le poids brut du colis. Habituellement poids net + poids du matériau d'emballage. (Pour l'impression),
-Gross Weight UOM,UDM du Poids Brut,
+Gross Weight UOM,UdM du Poids Brut,
Packing Slip Item,Article Emballé,
DN Detail,Détail du Bon de Livraison,
STO-PICK-.YYYY.-,STO-PICK-.YYYY.-,
@@ -8215,7 +8218,7 @@
Picked Qty,Quantité choisie,
Price List Master,Données de Base des Listes de Prix,
Price List Name,Nom de la Liste de Prix,
-Price Not UOM Dependent,Prix non dépendant de l'UOM,
+Price Not UOM Dependent,Prix non dépendant de l'UdM,
Applicable for Countries,Applicable pour les Pays,
Price List Country,Pays de la Liste des Prix,
MAT-PRE-.YYYY.-,MAT-PRE-YYYY.-,
@@ -8294,7 +8297,7 @@
Inspection Required,Inspection obligatoire,
From BOM,Depuis la nomenclature,
For Quantity,Pour la Quantité,
-As per Stock UOM,Selon UDM du Stock,
+As per Stock UOM,Selon UdM du Stock,
Including items for sub assemblies,Incluant les articles pour des sous-ensembles,
Default Source Warehouse,Entrepôt Source par Défaut,
Source Warehouse Address,Adresse de l'entrepôt source,
@@ -8309,7 +8312,7 @@
Customer or Supplier Details,Détails du Client ou du Fournisseur,
Per Transferred,Par transféré,
Stock Entry Detail,Détails de l'Écriture de Stock,
-Basic Rate (as per Stock UOM),Prix de base (comme l’UDM du Stock),
+Basic Rate (as per Stock UOM),Prix de base (comme l’UdM du Stock),
Basic Amount,Montant de Base,
Additional Cost,Frais Supplémentaire,
Serial No / Batch,N° de Série / Lot,
@@ -8339,7 +8342,7 @@
Amount Difference,Différence de Montant,
Item Naming By,Nomenclature d'Article Par,
Default Item Group,Groupe d'Éléments par Défaut,
-Default Stock UOM,UDM par Défaut des Articles,
+Default Stock UOM,UdM par Défaut des Articles,
Sample Retention Warehouse,Entrepôt de stockage des échantillons,
Default Valuation Method,Méthode de Valorisation par Défaut,
Show Barcode Field,Afficher Champ Code Barre,
@@ -8353,8 +8356,8 @@
Batch Identification,Identification par lots,
Use Naming Series,Utiliser la série de noms,
Naming Series Prefix,Préfix du nom de série,
-UOM Category,Catégorie d'unité de mesure (UDM),
-UOM Conversion Detail,Détails de Conversion de l'UDM,
+UOM Category,Catégorie d'unité de mesure (UdM),
+UOM Conversion Detail,Détails de Conversion de l'UdM,
Variant Field,Champ de Variante,
A logical Warehouse against which stock entries are made.,Un Entrepôt logique dans lequel les entrées en stock sont faites.,
Warehouse Detail,Détail de l'Entrepôt,
@@ -9869,8 +9872,8 @@
Party Specific Item,Restriction d'article disponible
Restrict Items Based On,Type de critére de restriction
Based On Value,critére de restriction
-Unit of Measure (UOM),Unité de mesure (UDM),
-Unit Of Measure (UOM),Unité de mesure (UDM),
+Unit of Measure (UOM),Unité de mesure (UdM),
+Unit Of Measure (UOM),Unité de mesure (UdM),
CRM Settings,Paramètres CRM
Do Not Explode,Ne pas décomposer
Quick Access, Accés rapides
diff --git a/erpnext/utilities/bulk_transaction.py b/erpnext/utilities/bulk_transaction.py
index bfcba07..eed7c12 100644
--- a/erpnext/utilities/bulk_transaction.py
+++ b/erpnext/utilities/bulk_transaction.py
@@ -9,7 +9,6 @@
def transaction_processing(data, from_doctype, to_doctype):
if isinstance(data, str):
deserialized_data = json.loads(data)
-
else:
deserialized_data = data
@@ -30,30 +29,29 @@
def job(deserialized_data, from_doctype, to_doctype):
- failed_history = []
- i = 0
+ fail_count = 0
for d in deserialized_data:
- failed = []
-
try:
- i += 1
doc_name = d.get("name")
frappe.db.savepoint("before_creation_state")
task(doc_name, from_doctype, to_doctype)
-
except Exception as e:
frappe.db.rollback(save_point="before_creation_state")
- failed_history.append(e)
- failed.append(e)
+ fail_count += 1
update_logger(
- doc_name, e, from_doctype, to_doctype, status="Failed", log_date=str(date.today())
+ doc_name,
+ str(frappe.get_traceback()),
+ from_doctype,
+ to_doctype,
+ status="Failed",
+ log_date=str(date.today()),
)
- if not failed:
+ else:
update_logger(
doc_name, None, from_doctype, to_doctype, status="Success", log_date=str(date.today())
)
- show_job_status(failed_history, deserialized_data, to_doctype)
+ show_job_status(fail_count, len(deserialized_data), to_doctype)
def task(doc_name, from_doctype, to_doctype):
@@ -94,7 +92,7 @@
"Purchase Invoice": purchase_order.make_purchase_invoice,
"Purchase Receipt": purchase_order.make_purchase_receipt,
},
- "Purhcase Invoice": {
+ "Purchase Invoice": {
"Purchase Receipt": purchase_invoice.make_purchase_receipt,
"Payment": payment_entry.get_payment_entry,
},
@@ -150,15 +148,14 @@
log_doc.save()
-def show_job_status(failed_history, deserialized_data, to_doctype):
- if not failed_history:
+def show_job_status(fail_count, deserialized_data_count, to_doctype):
+ if not fail_count:
frappe.msgprint(
_("Creation of {0} successful").format(to_doctype),
title="Successful",
indicator="green",
)
-
- if len(failed_history) != 0 and len(failed_history) < len(deserialized_data):
+ elif fail_count != 0 and fail_count < deserialized_data_count:
frappe.msgprint(
_(
"""Creation of {0} partially successful.
@@ -167,8 +164,7 @@
title="Partially successful",
indicator="orange",
)
-
- if len(failed_history) == len(deserialized_data):
+ else:
frappe.msgprint(
_(
"""Creation of {0} failed.
@@ -180,9 +176,7 @@
def record_exists(log_doc, doc_name, status):
-
record = mark_retrired_transaction(log_doc, doc_name)
-
if record and status == "Failed":
return False
elif record and status == "Success":
diff --git a/erpnext/www/book_appointment/index.css b/erpnext/www/book_appointment/index.css
index 2776108..3b1b97c 100644
--- a/erpnext/www/book_appointment/index.css
+++ b/erpnext/www/book_appointment/index.css
@@ -45,7 +45,7 @@
.time-slot.selected {
color: white;
- background: #5e64ff;
+ background: var(--primary-color);
}
.time-slot.selected .text-muted {
diff --git a/erpnext/www/book_appointment/index.html b/erpnext/www/book_appointment/index.html
index 207175f..ad964d9 100644
--- a/erpnext/www/book_appointment/index.html
+++ b/erpnext/www/book_appointment/index.html
@@ -12,8 +12,8 @@
<!-- title: Book an appointment -->
<div id="select-date-time">
<div class="text-center mt-5">
- <h3>Book an appointment</h3>
- <p class="lead text-muted" id="lead-text">Select the date and your timezone</p>
+ <h3>{{ _("Book an appointment") }}</h3>
+ <p class="lead text-muted" id="lead-text">{{ _("Select the date and your timezone") }}</p>
</div>
<div class="row justify-content-center mt-3">
<div class="col-md-6 align-self-center ">
@@ -31,7 +31,7 @@
</div>
<div class="row justify-content-center mt-3">
<div class="col-md-4 mb-3">
- <button class="btn btn-primary form-control" id="next-button">Next</button>
+ <button class="btn btn-primary form-control" id="next-button">{{ _("Next") }}</button>
</div>
</div>
</div>
@@ -39,24 +39,24 @@
<!--Enter Details-->
<div id="enter-details" class="mb-5">
<div class="text-center mt-5">
- <h3>Add details</h3>
- <p class="lead text-muted">Selected date is <span class="date-span"></span> at <span class="time-span">
+ <h3>{{ _("Add details") }}</h3>
+ <p class="lead text-muted">{{ _("Selected date is") }} <span class="date-span"></span> {{ _("at") }} <span class="time-span">
</span></p>
</div>
<div class="row justify-content-center mt-3">
<div class="col-md-4 align-items-center">
<form id="customer-form" action='#'>
- <input class="form-control mt-3" type="text" name="customer_name" id="customer_name" placeholder="Your Name (required)" required>
+ <input class="form-control mt-3" type="text" name="customer_name" id="customer_name" placeholder="{{ _('Your Name (required)') }}" required>
<input class="form-control mt-3" type="tel" name="customer_number" id="customer_number" placeholder="+910000000000">
<input class="form-control mt-3" type="text" name="customer_skype" id="customer_skype" placeholder="Skype">
- <input class="form-control mt-3"type="email" name="customer_email" id="customer_email" placeholder="Email Address (required)" required>
+ <input class="form-control mt-3"type="email" name="customer_email" id="customer_email" placeholder="{{ _('Email Address (required)') }}" required>
<textarea class="form-control mt-3" name="customer_notes" id="customer_notes" cols="30" rows="10"
- placeholder="Notes"></textarea>
+ placeholder="{{ _('Notes') }}"></textarea>
</form>
<div class="row mt-3 " id="submit-button-area">
- <div class="col-md mt-3" style="grid-area: back;"><button class="btn btn-dark form-control" onclick="initialise_select_date()">Go back</button></div>
- <div class="col-md mt-3" style="grid-area: submit;"><button class="btn btn-primary form-control " onclick="submit()" id="submit-button">Submit</button></div>
+ <div class="col-md mt-3" style="grid-area: back;"><button class="btn btn-dark form-control" onclick="initialise_select_date()">{{ _("Go back") }}</button></div>
+ <div class="col-md mt-3" style="grid-area: submit;"><button class="btn btn-primary form-control " onclick="submit()" id="submit-button">{{ _("Submit") }}</button></div>
</div>
</div>
</div>
diff --git a/erpnext/www/book_appointment/index.js b/erpnext/www/book_appointment/index.js
index 5562cbd..46ac155 100644
--- a/erpnext/www/book_appointment/index.js
+++ b/erpnext/www/book_appointment/index.js
@@ -69,7 +69,7 @@
window.selected_timezone = timezone.value;
update_time_slots(date_picker.value, timezone.value);
let lead_text = document.getElementById('lead-text');
- lead_text.innerHTML = "Select Time"
+ lead_text.innerHTML = __("Select Time")
}
async function get_time_slots(date, timezone) {
@@ -89,7 +89,7 @@
clear_time_slots();
if (window.slots.length <= 0) {
let message_div = document.createElement('p');
- message_div.innerHTML = "There are no slots available on this date";
+ message_div.innerHTML = __("There are no slots available on this date");
timeslot_container.appendChild(message_div);
return
}
@@ -128,7 +128,7 @@
let start_time_string = moment(time).tz(timezone).format("LT");
let end_time = moment(time).tz(timezone).add(window.appointment_settings.appointment_duration, 'minutes');
let end_time_string = end_time.format("LT");
- return `<span style="font-size: 1.2em;">${start_time_string}</span><br><span class="text-muted small">to ${end_time_string}</span>`;
+ return `<span style="font-size: 1.2em;">${start_time_string}</span><br><span class="text-muted small">${__("to") } ${end_time_string}</span>`;
}
function select_time() {
@@ -227,9 +227,9 @@
},
callback: (response)=>{
if (response.message.status == "Unverified") {
- frappe.show_alert("Please check your email to confirm the appointment")
+ frappe.show_alert(__("Please check your email to confirm the appointment"))
} else {
- frappe.show_alert("Appointment Created Successfully");
+ frappe.show_alert(__("Appointment Created Successfully"));
}
setTimeout(()=>{
let redirect_url = "/";
@@ -239,7 +239,7 @@
window.location.href = redirect_url;},5000)
},
error: (err)=>{
- frappe.show_alert("Something went wrong please try again");
+ frappe.show_alert(__("Something went wrong please try again"));
button.disabled = false;
}
});
diff --git a/erpnext/www/book_appointment/verify/index.html b/erpnext/www/book_appointment/verify/index.html
index 9bcd3d2..58c07e8 100644
--- a/erpnext/www/book_appointment/verify/index.html
+++ b/erpnext/www/book_appointment/verify/index.html
@@ -8,11 +8,11 @@
{% if success==True %}
<div class="alert alert-success">
- Your email has been verified and your appointment has been scheduled
+ {{ _("Your email has been verified and your appointment has been scheduled") }}
</div>
{% else %}
<div class="alert alert-danger">
- Verification failed please check the link
+ {{ _("Verification failed please check the link") }}
</div>
{% endif %}
{% endblock%}