diff --git a/.github/workflows/server-tests-mariadb.yml b/.github/workflows/server-tests-mariadb.yml index e3b92fd269..ed731b8a31 100644 --- a/.github/workflows/server-tests-mariadb.yml +++ b/.github/workflows/server-tests-mariadb.yml @@ -120,7 +120,7 @@ jobs: FRAPPE_BRANCH: ${{ github.event.inputs.branch }} - name: Run Tests - run: cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --app erpnext --use-orchestrator --with-coverage + run: 'cd ~/frappe-bench/ && bench --site test_site run-parallel-tests --app erpnext --with-coverage --total-builds 4 --build-number ${{ matrix.container }}' env: TYPE: server CI_BUILD_ID: ${{ github.run_id }} 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 3b07d5181e..58d67beb67 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/budget/budget.json b/erpnext/accounts/doctype/budget/budget.json index fc4dd200ea..f0566f4436 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 5527f9fb99..6ac3350c3b 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 DuplicateBudgetError(frappe.ValidationError): 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 @@ class Budget(Document): ): 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_account/journal_entry_account.json b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json index dff883aef9..a0ea43332c 100644 --- a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json +++ b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json @@ -202,6 +202,7 @@ "fieldname": "reference_type", "fieldtype": "Select", "label": "Reference Type", + "no_copy": 1, "options": "\nSales Invoice\nPurchase Invoice\nJournal Entry\nSales Order\nPurchase Order\nExpense Claim\nAsset\nLoan\nPayroll Entry\nEmployee Advance\nExchange Rate Revaluation\nInvoice Discounting\nFees\nFull and Final Statement" }, { @@ -209,13 +210,15 @@ "fieldtype": "Dynamic Link", "in_list_view": 1, "label": "Reference Name", + "no_copy": 1, "options": "reference_type" }, { "depends_on": "eval:doc.reference_type&&!in_list(doc.reference_type, ['Expense Claim', 'Asset', 'Employee Loan', 'Employee Advance'])", "fieldname": "reference_due_date", "fieldtype": "Select", - "label": "Reference Due Date" + "label": "Reference Due Date", + "no_copy": 1 }, { "fieldname": "project", @@ -274,19 +277,22 @@ "fieldname": "reference_detail_no", "fieldtype": "Data", "hidden": 1, - "label": "Reference Detail No" + "label": "Reference Detail No", + "no_copy": 1 } ], "idx": 1, "istable": 1, "links": [], - "modified": "2021-08-30 21:27:32.200299", + "modified": "2022-10-13 17:07:17.999191", "modified_by": "Administrator", "module": "Accounts", "name": "Journal Entry Account", + "naming_rule": "Random", "owner": "Administrator", "permissions": [], "sort_field": "modified", "sort_order": "DESC", + "states": [], "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index 7f245fd083..94874894b0 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -995,7 +995,9 @@ class PaymentEntry(AccountsController): if self.payment_type in ("Receive", "Pay") and self.party: for d in self.get("references"): if d.allocated_amount and d.reference_doctype in frappe.get_hooks("advance_payment_doctypes"): - frappe.get_doc(d.reference_doctype, d.reference_name).set_total_advance_paid() + frappe.get_doc( + d.reference_doctype, d.reference_name, for_update=True + ).set_total_advance_paid() def on_recurring(self, reference_doc, auto_repeat_doc): self.reference_no = reference_doc.name diff --git a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py index 70f128e0e3..3132fdd259 100644 --- a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py +++ b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py @@ -495,6 +495,67 @@ class TestPOSInvoice(unittest.TestCase): self.assertRaises(frappe.ValidationError, pos.submit) + def test_value_error_on_serial_no_validation(self): + from erpnext.stock.doctype.stock_entry.test_stock_entry import make_serialized_item + + se = make_serialized_item( + company="_Test Company", + target_warehouse="Stores - _TC", + cost_center="Main - _TC", + expense_account="Cost of Goods Sold - _TC", + ) + serial_nos = se.get("items")[0].serial_no + + # make a pos invoice + pos = create_pos_invoice( + company="_Test Company", + debit_to="Debtors - _TC", + account_for_change_amount="Cash - _TC", + warehouse="Stores - _TC", + income_account="Sales - _TC", + expense_account="Cost of Goods Sold - _TC", + cost_center="Main - _TC", + item=se.get("items")[0].item_code, + rate=1000, + qty=1, + do_not_save=1, + ) + pos.get("items")[0].has_serial_no = 1 + pos.get("items")[0].serial_no = serial_nos.split("\n")[0] + pos.set("payments", []) + pos.append( + "payments", {"mode_of_payment": "Cash", "account": "Cash - _TC", "amount": 1000, "default": 1} + ) + pos = pos.save().submit() + + # make a return + pos_return = make_sales_return(pos.name) + pos_return.paid_amount = pos_return.grand_total + pos_return.save() + pos_return.submit() + + # set docstatus to 2 for pos to trigger this issue + frappe.db.set_value("POS Invoice", pos.name, "docstatus", 2) + + pos2 = create_pos_invoice( + company="_Test Company", + debit_to="Debtors - _TC", + account_for_change_amount="Cash - _TC", + warehouse="Stores - _TC", + income_account="Sales - _TC", + expense_account="Cost of Goods Sold - _TC", + cost_center="Main - _TC", + item=se.get("items")[0].item_code, + rate=1000, + qty=1, + do_not_save=1, + ) + + pos2.get("items")[0].has_serial_no = 1 + pos2.get("items")[0].serial_no = serial_nos.split("\n")[0] + # Value error should not be triggered on validation + pos2.save() + def test_loyalty_points(self): from erpnext.accounts.doctype.loyalty_program.loyalty_program import ( get_loyalty_program_details_with_points, diff --git a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py index 9af3188e47..826d71b12e 100644 --- a/erpnext/accounts/doctype/pricing_rule/pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/pricing_rule.py @@ -268,6 +268,18 @@ def get_serial_no_for_item(args): return item_details +def update_pricing_rule_uom(pricing_rule, args): + child_doc = {"Item Code": "items", "Item Group": "item_groups", "Brand": "brands"}.get( + pricing_rule.apply_on + ) + + apply_on_field = frappe.scrub(pricing_rule.apply_on) + + for row in pricing_rule.get(child_doc): + if row.get(apply_on_field) == args.get(apply_on_field): + pricing_rule.uom = row.uom + + def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=False): from erpnext.accounts.doctype.pricing_rule.utils import ( get_applied_pricing_rules, @@ -324,6 +336,7 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=Fa if isinstance(pricing_rule, str): pricing_rule = frappe.get_cached_doc("Pricing Rule", pricing_rule) + update_pricing_rule_uom(pricing_rule, args) pricing_rule.apply_rule_on_other_items = get_pricing_rule_items(pricing_rule) or [] if pricing_rule.get("suggestion"): @@ -440,12 +453,15 @@ def apply_price_discount_rule(pricing_rule, item_details, args): if pricing_rule.currency == args.currency: pricing_rule_rate = pricing_rule.rate + # TODO https://github.com/frappe/erpnext/pull/23636 solve this in some other way. if pricing_rule_rate: + is_blank_uom = pricing_rule.get("uom") != args.get("uom") # Override already set price list rate (from item price) # if pricing_rule_rate > 0 item_details.update( { - "price_list_rate": pricing_rule_rate * args.get("conversion_factor", 1), + "price_list_rate": pricing_rule_rate + * (args.get("conversion_factor", 1) if is_blank_uom else 1), } ) item_details.update({"discount_percentage": 0.0}) diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index 0a9db6b0f5..fbe567824f 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -595,6 +595,121 @@ class TestPricingRule(unittest.TestCase): frappe.get_doc("Item Price", {"item_code": "Water Flask"}).delete() item.delete() + def test_item_price_with_blank_uom_pricing_rule(self): + properties = { + "item_code": "Item Blank UOM", + "stock_uom": "Nos", + "sales_uom": "Box", + "uoms": [dict(uom="Box", conversion_factor=10)], + } + item = make_item(properties=properties) + + make_item_price("Item Blank UOM", "_Test Price List", 100) + + pricing_rule_record = { + "doctype": "Pricing Rule", + "title": "_Test Item Blank UOM Rule", + "apply_on": "Item Code", + "items": [ + { + "item_code": "Item Blank UOM", + } + ], + "selling": 1, + "currency": "INR", + "rate_or_discount": "Rate", + "rate": 101, + "company": "_Test Company", + } + rule = frappe.get_doc(pricing_rule_record) + rule.insert() + + si = create_sales_invoice( + do_not_save=True, item_code="Item Blank UOM", uom="Box", conversion_factor=10 + ) + si.selling_price_list = "_Test Price List" + si.save() + + # If UOM is blank consider it as stock UOM and apply pricing_rule on all UOM. + # rate is 101, Selling UOM is Box that have conversion_factor of 10 so 101 * 10 = 1010 + self.assertEqual(si.items[0].price_list_rate, 1010) + self.assertEqual(si.items[0].rate, 1010) + + si.delete() + + si = create_sales_invoice(do_not_save=True, item_code="Item Blank UOM", uom="Nos") + si.selling_price_list = "_Test Price List" + si.save() + + # UOM is blank so consider it as stock UOM and apply pricing_rule on all UOM. + # rate is 101, Selling UOM is Nos that have conversion_factor of 1 so 101 * 1 = 101 + self.assertEqual(si.items[0].price_list_rate, 101) + self.assertEqual(si.items[0].rate, 101) + + si.delete() + rule.delete() + frappe.get_doc("Item Price", {"item_code": "Item Blank UOM"}).delete() + + item.delete() + + def test_item_price_with_selling_uom_pricing_rule(self): + properties = { + "item_code": "Item UOM other than Stock", + "stock_uom": "Nos", + "sales_uom": "Box", + "uoms": [dict(uom="Box", conversion_factor=10)], + } + item = make_item(properties=properties) + + make_item_price("Item UOM other than Stock", "_Test Price List", 100) + + pricing_rule_record = { + "doctype": "Pricing Rule", + "title": "_Test Item UOM other than Stock Rule", + "apply_on": "Item Code", + "items": [ + { + "item_code": "Item UOM other than Stock", + "uom": "Box", + } + ], + "selling": 1, + "currency": "INR", + "rate_or_discount": "Rate", + "rate": 101, + "company": "_Test Company", + } + rule = frappe.get_doc(pricing_rule_record) + rule.insert() + + si = create_sales_invoice( + do_not_save=True, item_code="Item UOM other than Stock", uom="Box", conversion_factor=10 + ) + si.selling_price_list = "_Test Price List" + si.save() + + # UOM is Box so apply pricing_rule only on Box UOM. + # Selling UOM is Box and as both UOM are same no need to multiply by conversion_factor. + self.assertEqual(si.items[0].price_list_rate, 101) + self.assertEqual(si.items[0].rate, 101) + + si.delete() + + si = create_sales_invoice(do_not_save=True, item_code="Item UOM other than Stock", uom="Nos") + si.selling_price_list = "_Test Price List" + si.save() + + # UOM is Box so pricing_rule won't apply as selling_uom is Nos. + # As Pricing Rule is not applied price of 100 will be fetched from Item Price List. + self.assertEqual(si.items[0].price_list_rate, 100) + self.assertEqual(si.items[0].rate, 100) + + si.delete() + rule.delete() + frappe.get_doc("Item Price", {"item_code": "Item UOM other than Stock"}).delete() + + item.delete() + def test_pricing_rule_for_different_currency(self): make_item("Test Sanitizer Item") diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index 1f29d732ba..4c78d7261d 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -111,6 +111,12 @@ def _get_pricing_rules(apply_on, args, values): ) if apply_on_field == "item_code": + if args.get("uom", None): + item_conditions += ( + " and ({child_doc}.uom='{item_uom}' or IFNULL({child_doc}.uom, '')='')".format( + child_doc=child_doc, item_uom=args.get("uom") + ) + ) if "variant_of" not in args: args.variant_of = frappe.get_cached_value("Item", args.item_code, "variant_of") diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index ec861a2787..c3a9855ff4 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -31,7 +31,7 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying. super.onload(); // Ignore linked advances - this.frm.ignore_doctypes_on_cancel_all = ['Journal Entry', 'Payment Entry']; + this.frm.ignore_doctypes_on_cancel_all = ['Journal Entry', 'Payment Entry', 'Purchase Invoice']; if(!this.frm.doc.__islocal) { // show credit_to in print format diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index 1e477776e2..e73d602332 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -12,39 +12,23 @@ "supplier", "supplier_name", "tax_id", - "due_date", - "tax_withholding_category", - "column_break1", "company", + "column_break_6", "posting_date", "posting_time", "set_posting_time", + "due_date", + "column_break1", "is_paid", "is_return", + "return_against", "apply_tds", + "tax_withholding_category", "amended_from", "accounting_dimensions_section", "cost_center", "dimension_col_break", "project", - "supplier_invoice_details", - "bill_no", - "column_break_15", - "bill_date", - "returns", - "return_against", - "section_addresses", - "supplier_address", - "address_display", - "contact_person", - "contact_display", - "contact_mobile", - "contact_email", - "col_break_address", - "shipping_address", - "shipping_address_display", - "billing_address", - "billing_address_display", "currency_and_price_list", "currency", "conversion_rate", @@ -54,39 +38,33 @@ "plc_conversion_rate", "ignore_pricing_rule", "sec_warehouse", - "set_warehouse", - "rejected_warehouse", - "col_break_warehouse", - "set_from_warehouse", - "supplier_warehouse", - "is_subcontracted", - "items_section", - "update_stock", "scan_barcode", + "col_break_warehouse", + "update_stock", + "set_warehouse", + "set_from_warehouse", + "is_subcontracted", + "rejected_warehouse", + "supplier_warehouse", + "items_section", "items", - "pricing_rule_details", - "pricing_rules", - "raw_materials_supplied", - "supplied_items", "section_break_26", "total_qty", + "total_net_weight", + "column_break_50", "base_total", "base_net_total", "column_break_28", - "total_net_weight", "total", "net_total", "taxes_section", + "taxes_and_charges", + "column_break_58", "tax_category", "column_break_49", "shipping_rule", "section_break_51", - "taxes_and_charges", "taxes", - "tax_withheld_vouchers_section", - "tax_withheld_vouchers", - "sec_tax_breakup", - "other_charges_calculation", "totals", "base_taxes_and_charges_added", "base_taxes_and_charges_deducted", @@ -95,12 +73,6 @@ "taxes_and_charges_added", "taxes_and_charges_deducted", "total_taxes_and_charges", - "section_break_44", - "apply_discount_on", - "base_discount_amount", - "column_break_46", - "additional_discount_percentage", - "discount_amount", "section_break_49", "base_grand_total", "base_rounding_adjustment", @@ -114,24 +86,58 @@ "total_advance", "outstanding_amount", "disable_rounded_total", + "section_break_44", + "apply_discount_on", + "base_discount_amount", + "additional_discount_account", + "column_break_46", + "additional_discount_percentage", + "discount_amount", + "tax_withheld_vouchers_section", + "tax_withheld_vouchers", + "sec_tax_breakup", + "other_charges_calculation", + "pricing_rule_details", + "pricing_rules", + "raw_materials_supplied", + "supplied_items", + "payments_tab", "payments_section", "mode_of_payment", - "cash_bank_account", + "base_paid_amount", "clearance_date", "col_br_payments", + "cash_bank_account", "paid_amount", - "base_paid_amount", + "advances_section", + "allocate_advances_automatically", + "get_advances", + "advances", + "advance_tax", "write_off", "write_off_amount", "base_write_off_amount", "column_break_61", "write_off_account", "write_off_cost_center", - "advances_section", - "allocate_advances_automatically", - "get_advances", - "advances", - "advance_tax", + "address_and_contact_tab", + "section_addresses", + "supplier_address", + "address_display", + "col_break_address", + "contact_person", + "contact_display", + "contact_mobile", + "contact_email", + "company_shipping_address_section", + "shipping_address", + "column_break_126", + "shipping_address_display", + "company_billing_address_section", + "billing_address", + "column_break_130", + "billing_address_display", + "terms_tab", "payment_schedule_section", "payment_terms_template", "ignore_default_payment_terms_template", @@ -139,23 +145,15 @@ "terms_section_break", "tc_name", "terms", - "printing_settings", - "letter_head", - "select_print_heading", - "column_break_112", - "group_same_items", - "language", - "sb_14", - "on_hold", - "release_date", - "cb_17", - "hold_comment", - "more_info", + "more_info_tab", + "status_section", "status", - "inter_company_invoice_reference", - "represents_company", - "column_break_147", - "is_internal_supplier", + "column_break_177", + "per_received", + "supplier_invoice_details", + "bill_no", + "column_break_15", + "bill_date", "accounting_details_section", "credit_to", "party_account_currency", @@ -163,15 +161,32 @@ "against_expense_account", "column_break_63", "unrealized_profit_loss_account", - "remarks", "subscription_section", - "from_date", - "to_date", - "column_break_114", "auto_repeat", "update_auto_repeat_reference", - "per_received", - "is_old_subcontracting_flow" + "column_break_114", + "from_date", + "to_date", + "printing_settings", + "letter_head", + "group_same_items", + "column_break_112", + "select_print_heading", + "language", + "sb_14", + "on_hold", + "release_date", + "cb_17", + "hold_comment", + "additional_info_section", + "is_internal_supplier", + "represents_company", + "column_break_147", + "inter_company_invoice_reference", + "is_old_subcontracting_flow", + "remarks", + "connections_tab", + "column_break_38" ], "fields": [ { @@ -354,7 +369,7 @@ "collapsible_depends_on": "bill_no", "fieldname": "supplier_invoice_details", "fieldtype": "Section Break", - "label": "Supplier Invoice Details" + "label": "Supplier Invoice" }, { "fieldname": "bill_no", @@ -377,12 +392,6 @@ "oldfieldtype": "Date", "print_hide": 1 }, - { - "depends_on": "return_against", - "fieldname": "returns", - "fieldtype": "Section Break", - "label": "Returns" - }, { "depends_on": "return_against", "fieldname": "return_against", @@ -394,10 +403,9 @@ "read_only": 1 }, { - "collapsible": 1, "fieldname": "section_addresses", "fieldtype": "Section Break", - "label": "Address and Contact" + "label": "Supplier Address" }, { "fieldname": "supplier_address", @@ -518,11 +526,12 @@ }, { "fieldname": "sec_warehouse", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "hide_border": 1, + "label": "Items" }, { "depends_on": "update_stock", - "description": "Sets 'Accepted Warehouse' in each row of the items table.", "fieldname": "set_warehouse", "fieldtype": "Link", "label": "Set Accepted Warehouse", @@ -531,7 +540,6 @@ }, { "depends_on": "update_stock", - "description": "Warehouse where you are maintaining stock of rejected items", "fieldname": "rejected_warehouse", "fieldtype": "Link", "label": "Rejected Warehouse", @@ -554,6 +562,7 @@ { "fieldname": "items_section", "fieldtype": "Section Break", + "hide_border": 1, "oldfieldtype": "Section Break", "options": "fa fa-shopping-cart" }, @@ -581,6 +590,7 @@ "reqd": 1 }, { + "collapsible": 1, "fieldname": "pricing_rule_details", "fieldtype": "Section Break", "label": "Pricing Rules" @@ -593,6 +603,7 @@ "read_only": 1 }, { + "collapsible": 1, "collapsible_depends_on": "supplied_items", "fieldname": "raw_materials_supplied", "fieldtype": "Section Break", @@ -656,6 +667,7 @@ "read_only": 1 }, { + "depends_on": "total_net_weight", "fieldname": "total_net_weight", "fieldtype": "Float", "label": "Total Net Weight", @@ -665,6 +677,8 @@ { "fieldname": "taxes_section", "fieldtype": "Section Break", + "hide_border": 1, + "label": "Taxes and Charges", "oldfieldtype": "Section Break", "options": "fa fa-money" }, @@ -688,7 +702,8 @@ }, { "fieldname": "section_break_51", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "hide_border": 1 }, { "fieldname": "taxes_and_charges", @@ -792,7 +807,6 @@ }, { "collapsible": 1, - "collapsible_depends_on": "discount_amount", "fieldname": "section_break_44", "fieldtype": "Section Break", "label": "Additional Discount" @@ -832,7 +846,8 @@ }, { "fieldname": "section_break_49", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "label": "Totals" }, { "fieldname": "base_grand_total", @@ -1003,8 +1018,6 @@ }, { "collapsible": 1, - "collapsible_depends_on": "write_off_amount", - "depends_on": "grand_total", "fieldname": "write_off", "fieldtype": "Section Break", "label": "Write Off" @@ -1081,7 +1094,6 @@ "print_hide": 1 }, { - "collapsible": 1, "collapsible_depends_on": "eval:(!doc.is_return)", "fieldname": "payment_schedule_section", "fieldtype": "Section Break", @@ -1102,8 +1114,6 @@ "print_hide": 1 }, { - "collapsible": 1, - "collapsible_depends_on": "terms", "fieldname": "terms_section_break", "fieldtype": "Section Break", "label": "Terms and Conditions", @@ -1119,13 +1129,13 @@ { "fieldname": "terms", "fieldtype": "Text Editor", - "label": "Terms and Conditions1" + "label": "Terms and Conditions" }, { "collapsible": 1, "fieldname": "printing_settings", "fieldtype": "Section Break", - "label": "Printing Settings" + "label": "Print Settings" }, { "allow_on_submit": 1, @@ -1166,15 +1176,6 @@ "print_hide": 1, "read_only": 1 }, - { - "collapsible": 1, - "fieldname": "more_info", - "fieldtype": "Section Break", - "label": "More Information", - "oldfieldtype": "Section Break", - "options": "fa fa-file-text", - "print_hide": 1 - }, { "default": "0", "fetch_from": "supplier.is_internal_supplier", @@ -1260,7 +1261,7 @@ "collapsible": 1, "fieldname": "subscription_section", "fieldtype": "Section Break", - "label": "Subscription Section", + "label": "Subscription", "print_hide": 1 }, { @@ -1339,7 +1340,7 @@ }, { "depends_on": "eval:doc.is_internal_supplier", - "description": "Unrealized Profit / Loss account for intra-company transfers", + "description": "Unrealized Profit/Loss account for intra-company transfers", "fieldname": "unrealized_profit_loss_account", "fieldtype": "Link", "label": "Unrealized Profit / Loss Account", @@ -1356,7 +1357,6 @@ }, { "depends_on": "eval:doc.update_stock && doc.is_internal_supplier", - "description": "Sets 'From Warehouse' in each row of the items table.", "fieldname": "set_from_warehouse", "fieldtype": "Link", "label": "Set From Warehouse", @@ -1422,6 +1422,7 @@ "read_only": 1 }, { + "collapsible_depends_on": "tax_withheld_vouchers", "fieldname": "tax_withheld_vouchers_section", "fieldtype": "Section Break", "label": "Tax Withheld Vouchers" @@ -1433,13 +1434,92 @@ "no_copy": 1, "options": "Tax Withheld Vouchers", "read_only": 1 + }, + { + "fieldname": "payments_tab", + "fieldtype": "Tab Break", + "label": "Payments" + }, + { + "fieldname": "address_and_contact_tab", + "fieldtype": "Tab Break", + "label": "Address & Contact" + }, + { + "fieldname": "terms_tab", + "fieldtype": "Tab Break", + "label": "Terms" + }, + { + "fieldname": "more_info_tab", + "fieldtype": "Tab Break", + "label": "More Info" + }, + { + "fieldname": "connections_tab", + "fieldtype": "Tab Break", + "label": "Connections", + "show_dashboard": 1 + }, + { + "fieldname": "column_break_6", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_38", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_50", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_58", + "fieldtype": "Column Break" + }, + { + "fieldname": "company_shipping_address_section", + "fieldtype": "Section Break", + "label": "Company Shipping Address" + }, + { + "fieldname": "column_break_126", + "fieldtype": "Column Break" + }, + { + "fieldname": "company_billing_address_section", + "fieldtype": "Section Break", + "label": "Company Billing Address" + }, + { + "fieldname": "column_break_130", + "fieldtype": "Column Break" + }, + { + "collapsible": 1, + "fieldname": "status_section", + "fieldtype": "Section Break", + "label": "Status" + }, + { + "fieldname": "column_break_177", + "fieldtype": "Column Break" + }, + { + "collapsible": 1, + "fieldname": "additional_info_section", + "fieldtype": "Section Break", + "label": "Additional Info", + "oldfieldtype": "Section Break", + "options": "fa fa-file-text", + "print_hide": 1 } ], "icon": "fa fa-file-text", "idx": 204, "is_submittable": 1, "links": [], - "modified": "2022-10-07 14:19:14.214157", + "modified": "2022-10-11 13:04:44.304389", "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 2b633cb8c3..3d74b8f139 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -705,6 +705,10 @@ class PurchaseInvoice(BuyingController): ) ) + credit_amount = item.base_net_amount + if self.is_internal_supplier and item.valuation_rate: + credit_amount = flt(item.valuation_rate * item.stock_qty) + # Intentionally passed negative debit amount to avoid incorrect GL Entry validation gl_entries.append( self.get_gl_dict( @@ -714,7 +718,7 @@ class PurchaseInvoice(BuyingController): "cost_center": item.cost_center, "project": item.project or self.project, "remarks": self.get("remarks") or _("Accounting Entry for Stock"), - "debit": -1 * flt(item.base_net_amount, item.precision("base_net_amount")), + "debit": -1 * flt(credit_amount, item.precision("base_net_amount")), }, warehouse_account[item.from_warehouse]["account_currency"], item=item, @@ -1411,6 +1415,7 @@ class PurchaseInvoice(BuyingController): "Stock Ledger Entry", "Repost Item Valuation", "Payment Ledger Entry", + "Purchase Invoice", ) self.update_advance_tax_references(cancel=1) 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 fca7e3a887..9de9036887 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -711,6 +711,7 @@ "label": "Valuation Rate", "no_copy": 1, "options": "Company:company:default_currency", + "precision": "6", "print_hide": 1, "read_only": 1 }, @@ -870,7 +871,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2022-09-27 10:54:23.980713", + "modified": "2022-10-12 03:37:29.032732", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index 2da515737a..97e5f4017e 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -12,44 +12,29 @@ "customer", "customer_name", "tax_id", - "pos_profile", - "is_pos", - "is_consolidated", - "is_return", - "is_debit_note", - "update_billed_amount_in_sales_order", - "column_break1", "company", "company_tax_id", + "column_break1", "posting_date", "posting_time", "set_posting_time", "due_date", + "column_break_14", + "is_pos", + "pos_profile", + "is_consolidated", + "is_return", "return_against", + "update_billed_amount_in_sales_order", + "is_debit_note", "amended_from", "accounting_dimensions_section", - "project", - "dimension_col_break", "cost_center", - "customer_po_details", - "po_no", - "column_break_23", - "po_date", - "address_and_contact", - "customer_address", - "address_display", - "contact_person", - "contact_display", - "contact_mobile", - "contact_email", - "territory", - "col_break4", - "shipping_address_name", - "shipping_address", - "company_address", - "company_address_display", - "dispatch_address_name", - "dispatch_address", + "dimension_col_break", + "project", + "column_break_27", + "campaign", + "source", "currency_and_price_list", "currency", "conversion_rate", @@ -58,60 +43,35 @@ "price_list_currency", "plc_conversion_rate", "ignore_pricing_rule", - "sec_warehouse", - "set_warehouse", - "column_break_55", - "set_target_warehouse", "items_section", - "update_stock", "scan_barcode", + "update_stock", + "column_break_39", + "set_warehouse", + "set_target_warehouse", + "section_break_42", "items", - "pricing_rule_details", - "pricing_rules", - "packing_list", - "packed_items", - "product_bundle_help", - "time_sheet_list", - "timesheets", - "total_billing_amount", - "total_billing_hours", "section_break_30", "total_qty", + "total_net_weight", + "column_break_32", "base_total", "base_net_total", - "column_break_32", - "total_net_weight", + "column_break_52", "total", "net_total", "taxes_section", "taxes_and_charges", "column_break_38", "shipping_rule", + "column_break_55", "tax_category", "section_break_40", "taxes", - "sec_tax_breakup", - "other_charges_calculation", "section_break_43", "base_total_taxes_and_charges", "column_break_47", "total_taxes_and_charges", - "loyalty_points_redemption", - "loyalty_points", - "loyalty_amount", - "redeem_loyalty_points", - "column_break_77", - "loyalty_program", - "loyalty_redemption_account", - "loyalty_redemption_cost_center", - "section_break_49", - "apply_discount_on", - "is_cash_or_non_trade_discount", - "base_discount_amount", - "additional_discount_account", - "column_break_51", - "additional_discount_percentage", - "discount_amount", "totals", "base_grand_total", "base_rounding_adjustment", @@ -125,21 +85,28 @@ "total_advance", "outstanding_amount", "disable_rounded_total", - "column_break4", - "write_off_amount", - "base_write_off_amount", - "write_off_outstanding_amount_automatically", - "column_break_74", - "write_off_account", - "write_off_cost_center", - "advances_section", - "allocate_advances_automatically", - "get_advances", - "advances", - "payment_schedule_section", - "ignore_default_payment_terms_template", - "payment_terms_template", - "payment_schedule", + "section_break_49", + "apply_discount_on", + "base_discount_amount", + "is_cash_or_non_trade_discount", + "additional_discount_account", + "column_break_51", + "additional_discount_percentage", + "discount_amount", + "sec_tax_breakup", + "other_charges_calculation", + "pricing_rule_details", + "pricing_rules", + "packing_list", + "packed_items", + "product_bundle_help", + "time_sheet_list", + "timesheets", + "section_break_104", + "total_billing_hours", + "column_break_106", + "total_billing_amount", + "payments_tab", "payments_section", "cash_bank_account", "payments", @@ -152,47 +119,95 @@ "column_break_90", "change_amount", "account_for_change_amount", + "advances_section", + "allocate_advances_automatically", + "get_advances", + "advances", + "write_off_section", + "write_off_amount", + "base_write_off_amount", + "write_off_outstanding_amount_automatically", + "column_break_74", + "write_off_account", + "write_off_cost_center", + "loyalty_points_redemption", + "redeem_loyalty_points", + "loyalty_points", + "loyalty_amount", + "column_break_77", + "loyalty_program", + "loyalty_redemption_account", + "loyalty_redemption_cost_center", + "contact_and_address_tab", + "address_and_contact", + "customer_address", + "address_display", + "col_break4", + "contact_person", + "contact_display", + "contact_mobile", + "contact_email", + "territory", + "shipping_address_section", + "shipping_address_name", + "shipping_address", + "shipping_addr_col_break", + "dispatch_address_name", + "dispatch_address", + "company_address_section", + "company_address", + "company_addr_col_break", + "company_address_display", + "terms_tab", + "payment_schedule_section", + "ignore_default_payment_terms_template", + "payment_terms_template", + "payment_schedule", "terms_section_break", "tc_name", "terms", - "edit_printing_settings", - "letter_head", - "group_same_items", - "select_print_heading", - "column_break_84", - "language", - "more_information", - "status", - "inter_company_invoice_reference", - "represents_company", - "customer_group", - "campaign", - "col_break23", - "is_internal_customer", - "is_discounted", - "source", + "more_info_tab", + "customer_po_details", + "po_no", + "column_break_23", + "po_date", "more_info", "debit_to", "party_account_currency", "is_opening", "column_break8", "unrealized_profit_loss_account", - "remarks", + "against_income_account", "sales_team_section_break", "sales_partner", - "column_break10", "amount_eligible_for_commission", + "column_break10", "commission_rate", "total_commission", "section_break2", "sales_team", + "edit_printing_settings", + "letter_head", + "group_same_items", + "column_break_84", + "select_print_heading", + "language", "subscription_section", "from_date", - "to_date", - "column_break_140", "auto_repeat", + "column_break_140", + "to_date", "update_auto_repeat_reference", - "against_income_account" + "more_information", + "status", + "inter_company_invoice_reference", + "represents_company", + "customer_group", + "col_break23", + "is_internal_customer", + "is_discounted", + "remarks", + "connections_tab" ], "fields": [ { @@ -453,12 +468,11 @@ "label": "Customer's Purchase Order Date" }, { - "collapsible": 1, "fieldname": "address_and_contact", "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Address and Contact" + "label": "Billing Address" }, { "fieldname": "customer_address", @@ -560,7 +574,6 @@ { "fieldname": "company_address_display", "fieldtype": "Small Text", - "hidden": 1, "hide_days": 1, "hide_seconds": 1, "label": "Company Address", @@ -651,13 +664,6 @@ "label": "Ignore Pricing Rule", "print_hide": 1 }, - { - "fieldname": "sec_warehouse", - "fieldtype": "Section Break", - "hide_days": 1, - "hide_seconds": 1, - "label": "Warehouse" - }, { "depends_on": "update_stock", "fieldname": "set_warehouse", @@ -671,6 +677,7 @@ { "fieldname": "items_section", "fieldtype": "Section Break", + "hide_border": 1, "hide_days": 1, "hide_seconds": 1, "label": "Items", @@ -702,7 +709,6 @@ "fieldtype": "Table", "hide_days": 1, "hide_seconds": 1, - "label": "Items", "oldfieldname": "entries", "oldfieldtype": "Table", "options": "Sales Invoice Item", @@ -755,9 +761,10 @@ { "collapsible": 1, "collapsible_depends_on": "eval:doc.total_billing_amount > 0", - "depends_on": "eval: !doc.is_return", + "depends_on": "eval:!doc.is_return", "fieldname": "time_sheet_list", "fieldtype": "Section Break", + "hide_border": 1, "hide_days": 1, "hide_seconds": 1, "label": "Time Sheet List" @@ -845,6 +852,7 @@ "read_only": 1 }, { + "depends_on": "total_net_weight", "fieldname": "total_net_weight", "fieldtype": "Float", "hide_days": 1, @@ -856,8 +864,10 @@ { "fieldname": "taxes_section", "fieldtype": "Section Break", + "hide_border": 1, "hide_days": 1, "hide_seconds": 1, + "label": "Taxes and Charges", "oldfieldtype": "Section Break", "options": "fa fa-money" }, @@ -900,6 +910,7 @@ { "fieldname": "section_break_40", "fieldtype": "Section Break", + "hide_border": 1, "hide_days": 1, "hide_seconds": 1 }, @@ -908,7 +919,6 @@ "fieldtype": "Table", "hide_days": 1, "hide_seconds": 1, - "label": "Sales Taxes and Charges", "oldfieldname": "other_charges", "oldfieldtype": "Table", "options": "Sales Taxes and Charges" @@ -1046,7 +1056,6 @@ }, { "collapsible": 1, - "collapsible_depends_on": "discount_amount", "fieldname": "section_break_49", "fieldtype": "Section Break", "hide_days": 1, @@ -1102,6 +1111,7 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, + "label": "Totals", "oldfieldtype": "Section Break", "options": "fa fa-money", "print_hide": 1 @@ -1283,8 +1293,6 @@ "print_hide": 1 }, { - "collapsible": 1, - "collapsible_depends_on": "eval:(!doc.is_pos && !doc.is_return)", "fieldname": "payment_schedule_section", "fieldtype": "Section Break", "hide_days": 1, @@ -1314,7 +1322,9 @@ "print_hide": 1 }, { - "depends_on": "eval:doc.is_pos===1||(doc.advances && doc.advances.length>0)", + "collapsible": 1, + "collapsible_depends_on": "eval:!doc.is_pos", + "depends_on": "eval:doc.is_pos===1", "fieldname": "payments_section", "fieldtype": "Section Break", "hide_days": 1, @@ -1352,6 +1362,7 @@ "hide_seconds": 1 }, { + "depends_on": "eval: doc.is_pos || doc.redeem_loyalty_points", "fieldname": "base_paid_amount", "fieldtype": "Currency", "hide_days": 1, @@ -1383,10 +1394,13 @@ "read_only": 1 }, { + "collapsible": 1, + "depends_on": "is_pos", "fieldname": "section_break_88", "fieldtype": "Section Break", "hide_days": 1, - "hide_seconds": 1 + "hide_seconds": 1, + "label": "Changes" }, { "depends_on": "is_pos", @@ -1427,17 +1441,6 @@ "options": "Account", "print_hide": 1 }, - { - "collapsible": 1, - "collapsible_depends_on": "write_off_amount", - "depends_on": "grand_total", - "fieldname": "column_break4", - "fieldtype": "Section Break", - "hide_days": 1, - "hide_seconds": 1, - "label": "Write Off", - "width": "50%" - }, { "fieldname": "write_off_amount", "fieldtype": "Currency", @@ -1495,8 +1498,6 @@ "print_hide": 1 }, { - "collapsible": 1, - "collapsible_depends_on": "terms", "fieldname": "terms_section_break", "fieldtype": "Section Break", "hide_days": 1, @@ -1530,7 +1531,7 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Printing Settings" + "label": "Print Settings" }, { "allow_on_submit": 1, @@ -1591,7 +1592,7 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "More Information" + "label": "Additional Info" }, { "fieldname": "inter_company_invoice_reference", @@ -1814,7 +1815,7 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Subscription Section" + "label": "Subscription" }, { "allow_on_submit": 1, @@ -1935,10 +1936,6 @@ "options": "Company", "read_only": 1 }, - { - "fieldname": "column_break_55", - "fieldtype": "Column Break" - }, { "depends_on": "eval: doc.is_internal_customer && doc.update_stock", "fieldname": "set_target_warehouse", @@ -2009,6 +2006,97 @@ "fieldname": "is_cash_or_non_trade_discount", "fieldtype": "Check", "label": "Is Cash or Non Trade Discount" + }, + { + "fieldname": "contact_and_address_tab", + "fieldtype": "Tab Break", + "label": "Contact & Address" + }, + { + "fieldname": "payments_tab", + "fieldtype": "Tab Break", + "label": "Payments" + }, + { + "fieldname": "terms_tab", + "fieldtype": "Tab Break", + "label": "Terms" + }, + { + "fieldname": "more_info_tab", + "fieldtype": "Tab Break", + "label": "More Info" + }, + { + "fieldname": "connections_tab", + "fieldtype": "Tab Break", + "label": "Connections", + "show_dashboard": 1 + }, + { + "fieldname": "column_break_14", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_39", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_42", + "fieldtype": "Section Break", + "hide_border": 1, + "hide_days": 1, + "hide_seconds": 1 + }, + { + "fieldname": "column_break_55", + "fieldtype": "Column Break" + }, + { + "fieldname": "shipping_address_section", + "fieldtype": "Section Break", + "label": "Shipping Address" + }, + { + "fieldname": "company_address_section", + "fieldtype": "Section Break", + "label": "Company Address" + }, + { + "fieldname": "shipping_addr_col_break", + "fieldtype": "Column Break" + }, + { + "fieldname": "company_addr_col_break", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_27", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_52", + "fieldtype": "Column Break" + }, + { + "depends_on": "eval:(!doc.is_return && doc.total_billing_amount > 0)", + "fieldname": "section_break_104", + "fieldtype": "Section Break" + }, + { + "fieldname": "column_break_106", + "fieldtype": "Column Break" + }, + { + "collapsible": 1, + "collapsible_depends_on": "write_off_amount", + "depends_on": "grand_total", + "fieldname": "write_off_section", + "fieldtype": "Section Break", + "hide_days": 1, + "hide_seconds": 1, + "label": "Write Off", + "width": "50%" } ], "icon": "fa fa-file-text", @@ -2021,7 +2109,7 @@ "link_fieldname": "consolidated_invoice" } ], - "modified": "2022-09-16 17:44:22.227332", + "modified": "2022-10-11 13:07:36.488095", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index afd5a59df4..0c03c550ba 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -2017,6 +2017,9 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None): update_address( target_doc, "shipping_address", "shipping_address_display", source_doc.customer_address ) + update_address( + target_doc, "billing_address", "billing_address_display", source_doc.customer_address + ) if currency: target_doc.currency = currency diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index ce44ae304b..1ba782451b 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -32,10 +32,20 @@ from erpnext.stock.doctype.stock_entry.test_stock_entry import ( get_qty_after_transaction, make_stock_entry, ) -from erpnext.stock.utils import get_incoming_rate +from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import ( + create_stock_reconciliation, +) +from erpnext.stock.utils import get_incoming_rate, get_stock_balance class TestSalesInvoice(unittest.TestCase): + def setUp(self): + from erpnext.stock.doctype.stock_ledger_entry.test_stock_ledger_entry import create_items + + create_items(["_Test Internal Transfer Item"], uoms=[{"uom": "Box", "conversion_factor": 10}]) + create_internal_parties() + setup_accounts() + def make(self): w = frappe.copy_doc(test_records[0]) w.is_pos = 0 @@ -1705,7 +1715,7 @@ class TestSalesInvoice(unittest.TestCase): si.save() self.assertEqual(si.get("items")[0].rate, flt((price_list_rate * 25) / 100 + price_list_rate)) - def test_outstanding_amount_after_advance_jv_cancelation(self): + def test_outstanding_amount_after_advance_jv_cancellation(self): from erpnext.accounts.doctype.journal_entry.test_journal_entry import ( test_records as jv_test_records, ) @@ -1749,7 +1759,7 @@ class TestSalesInvoice(unittest.TestCase): flt(si.rounded_total + si.total_advance, si.precision("outstanding_amount")), ) - def test_outstanding_amount_after_advance_payment_entry_cancelation(self): + def test_outstanding_amount_after_advance_payment_entry_cancellation(self): pe = frappe.get_doc( { "doctype": "Payment Entry", @@ -2367,29 +2377,6 @@ class TestSalesInvoice(unittest.TestCase): acc_settings.save() def test_inter_company_transaction(self): - from erpnext.selling.doctype.customer.test_customer import create_internal_customer - - create_internal_customer( - customer_name="_Test Internal Customer", - represents_company="_Test Company 1", - allowed_to_interact_with="Wind Power LLC", - ) - - if not frappe.db.exists("Supplier", "_Test Internal Supplier"): - supplier = frappe.get_doc( - { - "supplier_group": "_Test Supplier Group", - "supplier_name": "_Test Internal Supplier", - "doctype": "Supplier", - "is_internal_supplier": 1, - "represents_company": "Wind Power LLC", - } - ) - - supplier.append("companies", {"company": "_Test Company 1"}) - - supplier.insert() - si = create_sales_invoice( company="Wind Power LLC", customer="_Test Internal Customer", @@ -2440,38 +2427,6 @@ class TestSalesInvoice(unittest.TestCase): "Expenses Included In Valuation - _TC1", ) - if not frappe.db.exists("Customer", "_Test Internal Customer"): - customer = frappe.get_doc( - { - "customer_group": "_Test Customer Group", - "customer_name": "_Test Internal Customer", - "customer_type": "Individual", - "doctype": "Customer", - "territory": "_Test Territory", - "is_internal_customer": 1, - "represents_company": "_Test Company 1", - } - ) - - customer.append("companies", {"company": "Wind Power LLC"}) - - customer.insert() - - if not frappe.db.exists("Supplier", "_Test Internal Supplier"): - supplier = frappe.get_doc( - { - "supplier_group": "_Test Supplier Group", - "supplier_name": "_Test Internal Supplier", - "doctype": "Supplier", - "is_internal_supplier": 1, - "represents_company": "Wind Power LLC", - } - ) - - supplier.append("companies", {"company": "_Test Company 1"}) - - supplier.insert() - # begin test si = create_sales_invoice( company="Wind Power LLC", @@ -2541,34 +2496,9 @@ class TestSalesInvoice(unittest.TestCase): se.cancel() def test_internal_transfer_gl_entry(self): - ## Create internal transfer account - from erpnext.selling.doctype.customer.test_customer import create_internal_customer - - account = create_account( - account_name="Unrealized Profit", - parent_account="Current Liabilities - TCP1", - company="_Test Company with perpetual inventory", - ) - - frappe.db.set_value( - "Company", "_Test Company with perpetual inventory", "unrealized_profit_loss_account", account - ) - - customer = create_internal_customer( - "_Test Internal Customer 2", - "_Test Company with perpetual inventory", - "_Test Company with perpetual inventory", - ) - - create_internal_supplier( - "_Test Internal Supplier 2", - "_Test Company with perpetual inventory", - "_Test Company with perpetual inventory", - ) - si = create_sales_invoice( company="_Test Company with perpetual inventory", - customer=customer, + customer="_Test Internal Customer 2", debit_to="Debtors - TCP1", warehouse="Stores - TCP1", income_account="Sales - TCP1", @@ -2582,7 +2512,7 @@ class TestSalesInvoice(unittest.TestCase): si.update_stock = 1 si.items[0].target_warehouse = "Work In Progress - TCP1" - # Add stock to stores for succesful stock transfer + # Add stock to stores for successful stock transfer make_stock_entry( target="Stores - TCP1", company="_Test Company with perpetual inventory", qty=1, basic_rate=100 ) @@ -2638,6 +2568,77 @@ class TestSalesInvoice(unittest.TestCase): check_gl_entries(self, target_doc.name, pi_gl_entries, add_days(nowdate(), -1)) + def test_internal_transfer_gl_precision_issues(self): + # Make a stock queue of an item with two valuations + + # Remove all existing stock for this + if get_stock_balance("_Test Internal Transfer Item", "Stores - TCP1", "2022-04-10"): + create_stock_reconciliation( + item_code="_Test Internal Transfer Item", + warehouse="Stores - TCP1", + qty=0, + rate=0, + company="_Test Company with perpetual inventory", + expense_account="Stock Adjustment - TCP1" + if frappe.get_all("Stock Ledger Entry") + else "Temporary Opening - TCP1", + posting_date="2020-04-10", + posting_time="14:00", + ) + + make_stock_entry( + item_code="_Test Internal Transfer Item", + target="Stores - TCP1", + qty=9000000, + basic_rate=52.0, + posting_date="2020-04-10", + posting_time="14:00", + ) + make_stock_entry( + item_code="_Test Internal Transfer Item", + target="Stores - TCP1", + qty=60000000, + basic_rate=52.349777, + posting_date="2020-04-10", + posting_time="14:00", + ) + + # Make an internal transfer Sales Invoice Stock in non stock uom to check + # for rounding errors while converting to stock uom + si = create_sales_invoice( + company="_Test Company with perpetual inventory", + customer="_Test Internal Customer 2", + item_code="_Test Internal Transfer Item", + qty=5000000, + uom="Box", + debit_to="Debtors - TCP1", + warehouse="Stores - TCP1", + income_account="Sales - TCP1", + expense_account="Cost of Goods Sold - TCP1", + cost_center="Main - TCP1", + currency="INR", + do_not_save=1, + ) + + # Check GL Entries with precision + si.update_stock = 1 + si.items[0].target_warehouse = "Work In Progress - TCP1" + si.items[0].conversion_factor = 10 + si.save() + si.submit() + + # Check if adjustment entry is created + self.assertTrue( + frappe.db.exists( + "GL Entry", + { + "voucher_type": "Sales Invoice", + "voucher_no": si.name, + "remarks": "Rounding gain/loss Entry for Stock Transfer", + }, + ) + ) + def test_item_tax_net_range(self): item = create_item("T Shirt") @@ -3077,7 +3078,7 @@ class TestSalesInvoice(unittest.TestCase): [deferred_account, 2022.47, 0.0, "2019-03-15"], ] - gl_entries = gl_entries = frappe.db.sql( + gl_entries = frappe.db.sql( """select account, debit, credit, posting_date from `tabGL Entry` where voucher_type='Journal Entry' and voucher_detail_no=%s and posting_date <= %s @@ -3227,6 +3228,22 @@ class TestSalesInvoice(unittest.TestCase): self.assertTrue(return_si.docstatus == 1) + def test_sales_invoice_with_payable_tax_account(self): + si = create_sales_invoice(do_not_submit=True) + si.append( + "taxes", + { + "charge_type": "Actual", + "account_head": "Creditors - _TC", + "description": "Test", + "cost_center": "Main - _TC", + "tax_amount": 10, + "total": 10, + "dont_recompute_tax": 0, + }, + ) + self.assertRaises(frappe.ValidationError, si.submit) + def get_sales_invoice_for_e_invoice(): si = make_sales_invoice_for_ewaybill() @@ -3306,6 +3323,7 @@ def create_sales_invoice(**args): "item_name": args.item_name or "_Test Item", "description": args.description or "_Test Item", "warehouse": args.warehouse or "_Test Warehouse - _TC", + "target_warehouse": args.target_warehouse, "qty": args.qty or 1, "uom": args.uom or "Nos", "stock_uom": args.uom or "Nos", @@ -3318,7 +3336,7 @@ def create_sales_invoice(**args): "asset": args.asset or None, "cost_center": args.cost_center or "_Test Cost Center - _TC", "serial_no": args.serial_no, - "conversion_factor": 1, + "conversion_factor": args.get("conversion_factor", 1), "incoming_rate": args.incoming_rate or 0, "batch_no": args.batch_no or None, }, @@ -3431,6 +3449,34 @@ def get_taxes_and_charges(): ] +def create_internal_parties(): + from erpnext.selling.doctype.customer.test_customer import create_internal_customer + + create_internal_customer( + customer_name="_Test Internal Customer", + represents_company="_Test Company 1", + allowed_to_interact_with="Wind Power LLC", + ) + + create_internal_customer( + customer_name="_Test Internal Customer 2", + represents_company="_Test Company with perpetual inventory", + allowed_to_interact_with="_Test Company with perpetual inventory", + ) + + create_internal_supplier( + supplier_name="_Test Internal Supplier", + represents_company="Wind Power LLC", + allowed_to_interact_with="_Test Company 1", + ) + + create_internal_supplier( + supplier_name="_Test Internal Supplier 2", + represents_company="_Test Company with perpetual inventory", + allowed_to_interact_with="_Test Company with perpetual inventory", + ) + + def create_internal_supplier(supplier_name, represents_company, allowed_to_interact_with): if not frappe.db.exists("Supplier", supplier_name): supplier = frappe.get_doc( @@ -3453,6 +3499,19 @@ def create_internal_supplier(supplier_name, represents_company, allowed_to_inter return supplier_name +def setup_accounts(): + ## Create internal transfer account + account = create_account( + account_name="Unrealized Profit", + parent_account="Current Liabilities - TCP1", + company="_Test Company with perpetual inventory", + ) + + frappe.db.set_value( + "Company", "_Test Company with perpetual inventory", "unrealized_profit_loss_account", account + ) + + def add_taxes(doc): doc.append( "taxes", diff --git a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json index 4f97b63789..a307a6c17c 100644 --- a/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json +++ b/erpnext/accounts/doctype/sales_invoice_item/sales_invoice_item.json @@ -820,6 +820,7 @@ "label": "Incoming Rate (Costing)", "no_copy": 1, "options": "Company:company:default_currency", + "precision": "6", "print_hide": 1 }, { @@ -875,7 +876,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2022-09-06 14:17:43.394309", + "modified": "2022-10-10 20:57:38.340026", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Item", diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 95ba3d86ce..d7bf991688 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -460,10 +460,6 @@ def reconcile_against_document(args): # nosemgrep frappe.flags.ignore_party_validation = False - if entry.voucher_type in ("Payment Entry", "Journal Entry"): - if hasattr(doc, "update_expense_claim"): - doc.update_expense_claim() - def check_if_advance_entry_modified(args): """ @@ -1171,6 +1167,10 @@ def _delete_gl_entries(voucher_type, voucher_no): where voucher_type=%s and voucher_no=%s""", (voucher_type, voucher_no), ) + ple = qb.DocType("Payment Ledger Entry") + qb.from_(ple).delete().where( + (ple.voucher_type == voucher_type) & (ple.voucher_no == voucher_no) + ).run() def sort_stock_vouchers_by_posting_date( @@ -1368,9 +1368,8 @@ def check_and_delete_linked_reports(report): frappe.delete_doc("Desktop Icon", icon) -def create_payment_ledger_entry( - gl_entries, cancel=0, adv_adj=0, update_outstanding="Yes", from_repost=0 -): +def get_payment_ledger_entries(gl_entries, cancel=0): + ple_map = [] if gl_entries: ple = None @@ -1410,44 +1409,57 @@ def create_payment_ledger_entry( dr_or_cr *= -1 dr_or_cr_account_currency *= -1 - ple = frappe.get_doc( - { - "doctype": "Payment Ledger Entry", - "posting_date": gle.posting_date, - "company": gle.company, - "account_type": account_type, - "account": gle.account, - "party_type": gle.party_type, - "party": gle.party, - "cost_center": gle.cost_center, - "finance_book": gle.finance_book, - "due_date": gle.due_date, - "voucher_type": gle.voucher_type, - "voucher_no": gle.voucher_no, - "against_voucher_type": gle.against_voucher_type - if gle.against_voucher_type - else gle.voucher_type, - "against_voucher_no": gle.against_voucher if gle.against_voucher else gle.voucher_no, - "account_currency": gle.account_currency, - "amount": dr_or_cr, - "amount_in_account_currency": dr_or_cr_account_currency, - "delinked": True if cancel else False, - "remarks": gle.remarks, - } + ple = frappe._dict( + doctype="Payment Ledger Entry", + posting_date=gle.posting_date, + company=gle.company, + account_type=account_type, + account=gle.account, + party_type=gle.party_type, + party=gle.party, + cost_center=gle.cost_center, + finance_book=gle.finance_book, + due_date=gle.due_date, + voucher_type=gle.voucher_type, + voucher_no=gle.voucher_no, + against_voucher_type=gle.against_voucher_type + if gle.against_voucher_type + else gle.voucher_type, + against_voucher_no=gle.against_voucher if gle.against_voucher else gle.voucher_no, + account_currency=gle.account_currency, + amount=dr_or_cr, + amount_in_account_currency=dr_or_cr_account_currency, + delinked=True if cancel else False, + remarks=gle.remarks, ) dimensions_and_defaults = get_dimensions() if dimensions_and_defaults: for dimension in dimensions_and_defaults[0]: - ple.set(dimension.fieldname, gle.get(dimension.fieldname)) + ple[dimension.fieldname] = gle.get(dimension.fieldname) - if cancel: - delink_original_entry(ple) - ple.flags.ignore_permissions = 1 - ple.flags.adv_adj = adv_adj - ple.flags.from_repost = from_repost - ple.flags.update_outstanding = update_outstanding - ple.submit() + ple_map.append(ple) + return ple_map + + +def create_payment_ledger_entry( + gl_entries, cancel=0, adv_adj=0, update_outstanding="Yes", from_repost=0 +): + if gl_entries: + ple_map = get_payment_ledger_entries(gl_entries, cancel=cancel) + + for entry in ple_map: + + ple = frappe.get_doc(entry) + + if cancel: + delink_original_entry(ple) + + ple.flags.ignore_permissions = 1 + ple.flags.adv_adj = adv_adj + ple.flags.from_repost = from_repost + ple.flags.update_outstanding = update_outstanding + ple.submit() def update_voucher_outstanding(voucher_type, voucher_no, account, party_type, party): @@ -1467,7 +1479,12 @@ def update_voucher_outstanding(voucher_type, voucher_no, account, party_type, pa # on cancellation outstanding can be an empty list voucher_outstanding = ple_query.get_voucher_outstandings(vouchers, common_filter=common_filter) - if voucher_type in ["Sales Invoice", "Purchase Invoice", "Fees"] and voucher_outstanding: + if ( + voucher_type in ["Sales Invoice", "Purchase Invoice", "Fees"] + and party_type + and party + and voucher_outstanding + ): outstanding = voucher_outstanding[0] ref_doc = frappe.get_doc(voucher_type, voucher_no) diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json index fb8f25a0df..2193985ff2 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.json +++ b/erpnext/buying/doctype/purchase_order/purchase_order.json @@ -12,43 +12,24 @@ "title", "naming_series", "supplier", - "get_items_from_open_material_requests", "supplier_name", + "order_confirmation_no", + "order_confirmation_date", + "get_items_from_open_material_requests", + "column_break_7", + "transaction_date", + "schedule_date", + "column_break1", + "company", "apply_tds", "tax_withholding_category", "is_subcontracted", "supplier_warehouse", - "column_break1", - "company", - "transaction_date", - "schedule_date", - "order_confirmation_no", - "order_confirmation_date", "amended_from", "accounting_dimensions_section", "cost_center", "dimension_col_break", "project", - "drop_ship", - "customer", - "customer_name", - "column_break_19", - "customer_contact_person", - "customer_contact_display", - "customer_contact_mobile", - "customer_contact_email", - "section_addresses", - "supplier_address", - "address_display", - "contact_person", - "contact_display", - "contact_mobile", - "contact_email", - "col_break_address", - "shipping_address", - "shipping_address_display", - "billing_address", - "billing_address_display", "currency_and_price_list", "currency", "conversion_rate", @@ -57,7 +38,6 @@ "price_list_currency", "plc_conversion_rate", "ignore_pricing_rule", - "section_break_45", "before_items_section", "scan_barcode", "set_from_warehouse", @@ -67,10 +47,11 @@ "items", "sb_last_purchase", "total_qty", + "total_net_weight", + "column_break_40", "base_total", "base_net_total", "column_break_26", - "total_net_weight", "total", "net_total", "section_break_48", @@ -79,14 +60,13 @@ "set_reserve_warehouse", "supplied_items", "taxes_section", + "taxes_and_charges", + "column_break_53", "tax_category", "column_break_50", "shipping_rule", "section_break_52", - "taxes_and_charges", "taxes", - "sec_tax_breakup", - "other_charges_calculation", "totals", "base_taxes_and_charges_added", "base_taxes_and_charges_deducted", @@ -95,12 +75,6 @@ "taxes_and_charges_added", "taxes_and_charges_deducted", "total_taxes_and_charges", - "discount_section", - "apply_discount_on", - "base_discount_amount", - "column_break_45", - "additional_discount_percentage", - "discount_amount", "totals_section", "base_grand_total", "base_rounding_adjustment", @@ -113,37 +87,73 @@ "disable_rounded_total", "in_words", "advance_paid", + "discount_section", + "apply_discount_on", + "base_discount_amount", + "column_break_45", + "additional_discount_percentage", + "discount_amount", + "sec_tax_breakup", + "other_charges_calculation", + "address_and_contact_tab", + "section_addresses", + "supplier_address", + "address_display", + "col_break_address", + "contact_person", + "contact_display", + "contact_mobile", + "contact_email", + "company_shipping_address_section", + "shipping_address", + "column_break_99", + "shipping_address_display", + "company_billing_address_section", + "billing_address", + "column_break_103", + "billing_address_display", + "drop_ship", + "customer", + "customer_name", + "column_break_19", + "customer_contact_person", + "customer_contact_display", + "customer_contact_mobile", + "customer_contact_email", + "terms_tab", "payment_schedule_section", "payment_terms_template", "payment_schedule", + "terms_section_break", + "tc_name", + "terms", + "more_info_tab", "tracking_section", "status", "column_break_75", "per_billed", "per_received", - "terms_section_break", - "tc_name", - "terms", "column_break5", "letter_head", - "select_print_heading", - "column_break_86", - "language", "group_same_items", + "column_break_86", + "select_print_heading", + "language", "subscription_section", "from_date", "to_date", "column_break_97", "auto_repeat", "update_auto_repeat_reference", - "more_info", + "additional_info_section", + "is_internal_supplier", + "represents_company", "ref_sq", "column_break_74", "party_account_currency", - "is_internal_supplier", - "represents_company", "inter_company_order_reference", - "is_old_subcontracting_flow" + "is_old_subcontracting_flow", + "dashboard" ], "fields": [ { @@ -267,8 +277,9 @@ "read_only": 1 }, { + "depends_on": "eval:doc.customer", "fieldname": "drop_ship", - "fieldtype": "Section Break", + "fieldtype": "Tab Break", "label": "Drop Ship" }, { @@ -299,7 +310,6 @@ { "fieldname": "customer_contact_display", "fieldtype": "Small Text", - "hidden": 1, "label": "Customer Contact", "print_hide": 1 }, @@ -319,10 +329,9 @@ "print_hide": 1 }, { - "collapsible": 1, "fieldname": "section_addresses", "fieldtype": "Section Break", - "label": "Address and Contact" + "label": "Supplier Address" }, { "fieldname": "supplier_address", @@ -445,7 +454,6 @@ "print_hide": 1 }, { - "description": "Sets 'Warehouse' in each row of the Items table.", "fieldname": "set_warehouse", "fieldtype": "Link", "label": "Set Target Warehouse", @@ -483,7 +491,6 @@ "allow_bulk_edit": 1, "fieldname": "items", "fieldtype": "Table", - "label": "Items", "oldfieldname": "po_details", "oldfieldtype": "Table", "options": "Purchase Order Item", @@ -570,6 +577,7 @@ "read_only": 1 }, { + "depends_on": "total_net_weight", "fieldname": "total_net_weight", "fieldtype": "Float", "label": "Total Net Weight", @@ -579,6 +587,8 @@ { "fieldname": "taxes_section", "fieldtype": "Section Break", + "hide_border": 1, + "label": "Taxes and Charges", "oldfieldtype": "Section Break", "options": "fa fa-money" }, @@ -633,7 +643,6 @@ { "fieldname": "totals", "fieldtype": "Section Break", - "label": "Taxes and Charges", "oldfieldtype": "Section Break", "options": "fa fa-money" }, @@ -698,7 +707,6 @@ "read_only": 1 }, { - "depends_on": "total_taxes_and_charges", "fieldname": "total_taxes_and_charges", "fieldtype": "Currency", "label": "Total Taxes and Charges", @@ -708,7 +716,6 @@ }, { "collapsible": 1, - "collapsible_depends_on": "apply_discount_on", "fieldname": "discount_section", "fieldtype": "Section Break", "label": "Additional Discount" @@ -773,7 +780,6 @@ "read_only": 1 }, { - "description": "In Words will be visible once you save the Purchase Order.", "fieldname": "base_in_words", "fieldtype": "Data", "label": "In Words (Company Currency)", @@ -851,7 +857,6 @@ "read_only": 1 }, { - "collapsible": 1, "fieldname": "payment_schedule_section", "fieldtype": "Section Break", "label": "Payment Terms" @@ -871,11 +876,10 @@ "print_hide": 1 }, { - "collapsible": 1, "collapsible_depends_on": "terms", "fieldname": "terms_section_break", "fieldtype": "Section Break", - "label": "Terms and Conditions", + "label": "Terms & Conditions", "oldfieldtype": "Section Break", "options": "fa fa-legal" }, @@ -895,13 +899,6 @@ "oldfieldname": "terms", "oldfieldtype": "Text Editor" }, - { - "collapsible": 1, - "fieldname": "more_info", - "fieldtype": "Section Break", - "label": "More Information", - "oldfieldtype": "Section Break" - }, { "default": "Draft", "fieldname": "status", @@ -1023,7 +1020,7 @@ "collapsible": 1, "fieldname": "subscription_section", "fieldtype": "Section Break", - "label": "Subscription Section" + "label": "Auto Repeat" }, { "allow_on_submit": 1, @@ -1098,7 +1095,9 @@ }, { "fieldname": "before_items_section", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "hide_border": 1, + "label": "Items" }, { "fieldname": "items_col_break", @@ -1133,10 +1132,6 @@ "label": "Tax Withholding Category", "options": "Tax Withholding Category" }, - { - "fieldname": "section_break_45", - "fieldtype": "Section Break" - }, { "collapsible": 1, "fieldname": "accounting_dimensions_section", @@ -1173,13 +1168,71 @@ "fieldtype": "Link", "label": "Set From Warehouse", "options": "Warehouse" + }, + { + "fieldname": "terms_tab", + "fieldtype": "Tab Break", + "label": "Terms" + }, + { + "fieldname": "more_info_tab", + "fieldtype": "Tab Break", + "label": "More Info" + }, + { + "fieldname": "dashboard", + "fieldtype": "Tab Break", + "label": "Dashboard", + "show_dashboard": 1 + }, + { + "fieldname": "column_break_7", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_40", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_53", + "fieldtype": "Column Break" + }, + { + "fieldname": "address_and_contact_tab", + "fieldtype": "Tab Break", + "label": "Address & Contact" + }, + { + "fieldname": "company_shipping_address_section", + "fieldtype": "Section Break", + "label": "Company Shipping Address" + }, + { + "fieldname": "company_billing_address_section", + "fieldtype": "Section Break", + "label": "Company Billing Address" + }, + { + "collapsible": 1, + "fieldname": "additional_info_section", + "fieldtype": "Section Break", + "label": "Additional Info", + "oldfieldtype": "Section Break" + }, + { + "fieldname": "column_break_99", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_103", + "fieldtype": "Column Break" } ], "icon": "fa fa-file-text", "idx": 105, "is_submittable": 1, "links": [], - "modified": "2022-09-16 17:45:04.954055", + "modified": "2022-10-11 13:01:41.674352", "modified_by": "Administrator", "module": "Buying", "name": "Purchase Order", diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py index bcedd4d0a1..c224b611e5 100644 --- a/erpnext/buying/doctype/purchase_order/purchase_order.py +++ b/erpnext/buying/doctype/purchase_order/purchase_order.py @@ -18,7 +18,7 @@ from erpnext.accounts.doctype.sales_invoice.sales_invoice import ( from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import ( get_party_tax_withholding_details, ) -from erpnext.accounts.party import get_party_account_currency +from erpnext.accounts.party import get_party_account, get_party_account_currency from erpnext.buying.utils import check_on_hold_or_closed_status, validate_for_items from erpnext.controllers.buying_controller import BuyingController from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults @@ -558,6 +558,7 @@ def get_mapped_purchase_invoice(source_name, target_doc=None, ignore_permissions target.set_advances() target.set_payment_schedule() + target.credit_to = get_party_account("Supplier", source.supplier, source.company) def update_item(obj, target, source_parent): target.amount = flt(obj.amount) - flt(obj.billed_amt) diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json index 8d1939a101..16365618ca 100644 --- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json +++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json @@ -13,19 +13,13 @@ "naming_series", "supplier", "supplier_name", - "column_break1", "company", + "column_break1", + "status", "transaction_date", "valid_till", "quotation_number", "amended_from", - "address_section", - "supplier_address", - "contact_person", - "address_display", - "contact_display", - "contact_mobile", - "contact_email", "currency_and_price_list", "currency", "conversion_rate", @@ -36,25 +30,23 @@ "ignore_pricing_rule", "items_section", "items", - "pricing_rule_details", - "pricing_rules", "section_break_22", "total_qty", + "total_net_weight", + "column_break_26", "base_total", "base_net_total", "column_break_24", "total", "net_total", - "total_net_weight", "taxes_section", + "taxes_and_charges", + "column_break_34", "tax_category", "column_break_36", "shipping_rule", "section_break_38", - "taxes_and_charges", "taxes", - "tax_breakup", - "other_charges_calculation", "totals", "base_taxes_and_charges_added", "base_taxes_and_charges_deducted", @@ -80,24 +72,36 @@ "rounded_total", "in_words", "disable_rounded_total", - "terms_section_break", + "tax_breakup", + "other_charges_calculation", + "pricing_rule_details", + "pricing_rules", + "address_and_contact_tab", + "supplier_address", + "address_display", + "column_break_72", + "contact_person", + "contact_display", + "contact_mobile", + "contact_email", + "terms_tab", "tc_name", "terms", + "more_info_tab", "printing_settings", - "select_print_heading", - "group_same_items", - "column_break_72", "letter_head", + "group_same_items", + "column_break_85", + "select_print_heading", "language", "subscription_section", "auto_repeat", "update_auto_repeat_reference", "more_info", - "status", - "column_break_57", "is_subcontracted", - "reference", - "opportunity" + "column_break_57", + "opportunity", + "connections_tab" ], "fields": [ { @@ -146,7 +150,7 @@ "fieldname": "supplier_name", "fieldtype": "Data", "in_global_search": 1, - "label": "Name", + "label": "Supplier Name", "read_only": 1 }, { @@ -193,12 +197,6 @@ "reqd": 1, "search_index": 1 }, - { - "collapsible": 1, - "fieldname": "address_section", - "fieldtype": "Section Break", - "label": "Address and Contact" - }, { "fieldname": "supplier_address", "fieldtype": "Link", @@ -309,6 +307,8 @@ { "fieldname": "items_section", "fieldtype": "Section Break", + "hide_border": 1, + "label": "Items", "oldfieldtype": "Section Break", "options": "fa fa-shopping-cart" }, @@ -316,7 +316,6 @@ "allow_bulk_edit": 1, "fieldname": "items", "fieldtype": "Table", - "label": "Items", "oldfieldname": "po_details", "oldfieldtype": "Table", "options": "Supplier Quotation Item", @@ -394,6 +393,7 @@ { "fieldname": "taxes_section", "fieldtype": "Section Break", + "hide_border": 1, "label": "Taxes and Charges", "oldfieldtype": "Section Break", "options": "fa fa-money" @@ -417,7 +417,8 @@ }, { "fieldname": "section_break_38", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "hide_border": 1 }, { "fieldname": "taxes_and_charges", @@ -523,7 +524,6 @@ }, { "collapsible": 1, - "collapsible_depends_on": "discount_amount", "fieldname": "section_break_41", "fieldtype": "Section Break", "label": "Additional Discount" @@ -563,7 +563,8 @@ }, { "fieldname": "section_break_46", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "label": "Totals" }, { "fieldname": "base_grand_total", @@ -654,19 +655,10 @@ "fieldtype": "Check", "label": "Disable Rounded Total" }, - { - "collapsible": 1, - "collapsible_depends_on": "terms", - "fieldname": "terms_section_break", - "fieldtype": "Section Break", - "label": "Terms and Conditions", - "oldfieldtype": "Section Break", - "options": "fa fa-legal" - }, { "fieldname": "tc_name", "fieldtype": "Link", - "label": "Terms", + "label": "Terms Template", "oldfieldname": "tc_name", "oldfieldtype": "Link", "options": "Terms and Conditions", @@ -729,7 +721,7 @@ { "fieldname": "subscription_section", "fieldtype": "Section Break", - "label": "Auto Repeat Section" + "label": "Auto Repeat" }, { "fieldname": "auto_repeat", @@ -751,7 +743,7 @@ "collapsible": 1, "fieldname": "more_info", "fieldtype": "Section Break", - "label": "More Information", + "label": "Additional Info", "oldfieldtype": "Section Break", "options": "fa fa-file-text" }, @@ -779,11 +771,6 @@ "label": "Is Subcontracted", "print_hide": 1 }, - { - "fieldname": "reference", - "fieldtype": "Section Break", - "label": "Reference" - }, { "fieldname": "opportunity", "fieldtype": "Link", @@ -803,6 +790,39 @@ "fieldname": "quotation_number", "fieldtype": "Data", "label": "Quotation Number" + }, + { + "fieldname": "address_and_contact_tab", + "fieldtype": "Tab Break", + "label": "Address & Contact" + }, + { + "fieldname": "terms_tab", + "fieldtype": "Tab Break", + "label": "Terms" + }, + { + "fieldname": "more_info_tab", + "fieldtype": "Tab Break", + "label": "More Info" + }, + { + "fieldname": "connections_tab", + "fieldtype": "Tab Break", + "label": "Connections", + "show_dashboard": 1 + }, + { + "fieldname": "column_break_26", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_34", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_85", + "fieldtype": "Column Break" } ], "icon": "fa fa-shopping-cart", @@ -810,7 +830,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2022-03-14 16:13:20.284572", + "modified": "2022-09-27 18:20:09.462037", "modified_by": "Administrator", "module": "Buying", "name": "Supplier Quotation", diff --git a/erpnext/buying/report/procurement_tracker/procurement_tracker.py b/erpnext/buying/report/procurement_tracker/procurement_tracker.py index d70ac46ce3..71019e8037 100644 --- a/erpnext/buying/report/procurement_tracker/procurement_tracker.py +++ b/erpnext/buying/report/procurement_tracker/procurement_tracker.py @@ -127,32 +127,27 @@ def get_columns(filters): return columns -def get_conditions(filters): - conditions = "" - +def apply_filters_on_query(filters, parent, child, query): if filters.get("company"): - conditions += " AND parent.company=%s" % frappe.db.escape(filters.get("company")) + query = query.where(parent.company == filters.get("company")) if filters.get("cost_center") or filters.get("project"): - conditions += """ - AND (child.`cost_center`=%s OR child.`project`=%s) - """ % ( - frappe.db.escape(filters.get("cost_center")), - frappe.db.escape(filters.get("project")), + query = query.where( + (child.cost_center == filters.get("cost_center")) | (child.project == filters.get("project")) ) if filters.get("from_date"): - conditions += " AND parent.transaction_date>='%s'" % filters.get("from_date") + query = query.where(parent.transaction_date >= filters.get("from_date")) if filters.get("to_date"): - conditions += " AND parent.transaction_date<='%s'" % filters.get("to_date") - return conditions + query = query.where(parent.transaction_date <= filters.get("to_date")) + + return query def get_data(filters): - conditions = get_conditions(filters) - purchase_order_entry = get_po_entries(conditions) - mr_records, procurement_record_against_mr = get_mapped_mr_details(conditions) + purchase_order_entry = get_po_entries(filters) + mr_records, procurement_record_against_mr = get_mapped_mr_details(filters) pr_records = get_mapped_pr_records() pi_records = get_mapped_pi_records() @@ -187,11 +182,15 @@ def get_data(filters): return procurement_record -def get_mapped_mr_details(conditions): +def get_mapped_mr_details(filters): mr_records = {} - mr_details = frappe.db.sql( - """ - SELECT + parent = frappe.qb.DocType("Material Request") + child = frappe.qb.DocType("Material Request Item") + + query = ( + frappe.qb.from_(parent) + .from_(child) + .select( parent.transaction_date, parent.per_ordered, parent.owner, @@ -203,18 +202,13 @@ def get_mapped_mr_details(conditions): child.uom, parent.status, child.project, - child.cost_center - FROM `tabMaterial Request` parent, `tabMaterial Request Item` child - WHERE - parent.per_ordered>=0 - AND parent.name=child.parent - AND parent.docstatus=1 - {conditions} - """.format( - conditions=conditions - ), - as_dict=1, - ) # nosec + child.cost_center, + ) + .where((parent.per_ordered >= 0) & (parent.name == child.parent) & (parent.docstatus == 1)) + ) + query = apply_filters_on_query(filters, parent, child, query) + + mr_details = query.run(as_dict=True) procurement_record_against_mr = [] for record in mr_details: @@ -241,46 +235,49 @@ def get_mapped_mr_details(conditions): def get_mapped_pi_records(): - return frappe._dict( - frappe.db.sql( - """ - SELECT - pi_item.po_detail, - pi_item.base_amount - FROM `tabPurchase Invoice Item` as pi_item - INNER JOIN `tabPurchase Order` as po - ON pi_item.`purchase_order` = po.`name` - WHERE - pi_item.docstatus = 1 - AND po.status not in ('Closed','Completed','Cancelled') - AND pi_item.po_detail IS NOT NULL - """ + po = frappe.qb.DocType("Purchase Order") + pi_item = frappe.qb.DocType("Purchase Invoice Item") + pi_records = ( + frappe.qb.from_(pi_item) + .inner_join(po) + .on(pi_item.purchase_order == po.name) + .select(pi_item.po_detail, pi_item.base_amount) + .where( + (pi_item.docstatus == 1) + & (po.status.notin(("Closed", "Completed", "Cancelled"))) + & (pi_item.po_detail.isnotnull()) ) - ) + ).run() + + return frappe._dict(pi_records) def get_mapped_pr_records(): - return frappe._dict( - frappe.db.sql( - """ - SELECT - pr_item.purchase_order_item, - pr.posting_date - FROM `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pr_item - WHERE - pr.docstatus=1 - AND pr.name=pr_item.parent - AND pr_item.purchase_order_item IS NOT NULL - AND pr.status not in ('Closed','Completed','Cancelled') - """ + pr = frappe.qb.DocType("Purchase Receipt") + pr_item = frappe.qb.DocType("Purchase Receipt Item") + pr_records = ( + frappe.qb.from_(pr) + .from_(pr_item) + .select(pr_item.purchase_order_item, pr.posting_date) + .where( + (pr.docstatus == 1) + & (pr.name == pr_item.parent) + & (pr_item.purchase_order_item.isnotnull()) + & (pr.status.notin(("Closed", "Completed", "Cancelled"))) ) - ) + ).run() + + return frappe._dict(pr_records) -def get_po_entries(conditions): - return frappe.db.sql( - """ - SELECT +def get_po_entries(filters): + parent = frappe.qb.DocType("Purchase Order") + child = frappe.qb.DocType("Purchase Order Item") + + query = ( + frappe.qb.from_(parent) + .from_(child) + .select( child.name, child.parent, child.cost_center, @@ -297,17 +294,15 @@ def get_po_entries(conditions): parent.transaction_date, parent.supplier, parent.status, - parent.owner - FROM `tabPurchase Order` parent, `tabPurchase Order Item` child - WHERE - parent.docstatus = 1 - AND parent.name = child.parent - AND parent.status not in ('Closed','Completed','Cancelled') - {conditions} - GROUP BY - parent.name, child.item_code - """.format( - conditions=conditions - ), - as_dict=1, - ) # nosec + parent.owner, + ) + .where( + (parent.docstatus == 1) + & (parent.name == child.parent) + & (parent.status.notin(("Closed", "Completed", "Cancelled"))) + ) + .groupby(parent.name, child.item_code) + ) + query = apply_filters_on_query(filters, parent, child, query) + + return query.run(as_dict=True) diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py index a5c464910d..e10c0e2fcc 100644 --- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py +++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.py @@ -6,6 +6,7 @@ import copy import frappe from frappe import _ +from frappe.query_builder.functions import IfNull from frappe.utils import date_diff, flt, getdate @@ -16,9 +17,7 @@ def execute(filters=None): validate_filters(filters) columns = get_columns(filters) - conditions = get_conditions(filters) - - data = get_data(conditions, filters) + data = get_data(filters) if not data: return [], [], None, [] @@ -37,60 +36,61 @@ def validate_filters(filters): frappe.throw(_("To Date cannot be before From Date.")) -def get_conditions(filters): - conditions = "" - if filters.get("from_date") and filters.get("to_date"): - conditions += " and po.transaction_date between %(from_date)s and %(to_date)s" +def get_data(filters): + po = frappe.qb.DocType("Purchase Order") + po_item = frappe.qb.DocType("Purchase Order Item") + pi_item = frappe.qb.DocType("Purchase Invoice Item") - for field in ["company", "name"]: + query = ( + frappe.qb.from_(po) + .from_(po_item) + .left_join(pi_item) + .on(pi_item.po_detail == po_item.name) + .select( + po.transaction_date.as_("date"), + po_item.schedule_date.as_("required_date"), + po_item.project, + po.name.as_("purchase_order"), + po.status, + po.supplier, + po_item.item_code, + po_item.qty, + po_item.received_qty, + (po_item.qty - po_item.received_qty).as_("pending_qty"), + IfNull(pi_item.qty, 0).as_("billed_qty"), + po_item.base_amount.as_("amount"), + (po_item.received_qty * po_item.base_rate).as_("received_qty_amount"), + (po_item.billed_amt * IfNull(po.conversion_rate, 1)).as_("billed_amount"), + (po_item.base_amount - (po_item.billed_amt * IfNull(po.conversion_rate, 1))).as_( + "pending_amount" + ), + po.set_warehouse.as_("warehouse"), + po.company, + po_item.name, + ) + .where( + (po_item.parent == po.name) & (po.status.notin(("Stopped", "Closed"))) & (po.docstatus == 1) + ) + .groupby(po_item.name) + .orderby(po.transaction_date) + ) + + for field in ("company", "name"): if filters.get(field): - conditions += f" and po.{field} = %({field})s" + query = query.where(po[field] == filters.get(field)) + + if filters.get("from_date") and filters.get("to_date"): + query = query.where( + po.transaction_date.between(filters.get("from_date"), filters.get("to_date")) + ) if filters.get("status"): - conditions += " and po.status in %(status)s" + query = query.where(po.status.isin(filters.get("status"))) if filters.get("project"): - conditions += " and poi.project = %(project)s" + query = query.where(po_item.project == filters.get("project")) - return conditions - - -def get_data(conditions, filters): - data = frappe.db.sql( - """ - SELECT - po.transaction_date as date, - poi.schedule_date as required_date, - poi.project, - po.name as purchase_order, - po.status, po.supplier, poi.item_code, - poi.qty, poi.received_qty, - (poi.qty - poi.received_qty) AS pending_qty, - IFNULL(pii.qty, 0) as billed_qty, - poi.base_amount as amount, - (poi.received_qty * poi.base_rate) as received_qty_amount, - (poi.billed_amt * IFNULL(po.conversion_rate, 1)) as billed_amount, - (poi.base_amount - (poi.billed_amt * IFNULL(po.conversion_rate, 1))) as pending_amount, - po.set_warehouse as warehouse, - po.company, poi.name - FROM - `tabPurchase Order` po, - `tabPurchase Order Item` poi - LEFT JOIN `tabPurchase Invoice Item` pii - ON pii.po_detail = poi.name - WHERE - poi.parent = po.name - and po.status not in ('Stopped', 'Closed') - and po.docstatus = 1 - {0} - GROUP BY poi.name - ORDER BY po.transaction_date ASC - """.format( - conditions - ), - filters, - as_dict=1, - ) + data = query.run(as_dict=True) return data diff --git a/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py index 3013b6d160..a728290961 100644 --- a/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py +++ b/erpnext/buying/report/supplier_quotation_comparison/supplier_quotation_comparison.py @@ -16,8 +16,7 @@ def execute(filters=None): return [], [] columns = get_columns(filters) - conditions = get_conditions(filters) - supplier_quotation_data = get_data(filters, conditions) + supplier_quotation_data = get_data(filters) data, chart_data = prepare_data(supplier_quotation_data, filters) message = get_message() @@ -25,50 +24,51 @@ def execute(filters=None): return columns, data, message, chart_data -def get_conditions(filters): - conditions = "" +def get_data(filters): + sq = frappe.qb.DocType("Supplier Quotation") + sq_item = frappe.qb.DocType("Supplier Quotation Item") + + query = ( + frappe.qb.from_(sq_item) + .from_(sq) + .select( + sq_item.parent, + sq_item.item_code, + sq_item.qty, + sq_item.stock_qty, + sq_item.amount, + sq_item.uom, + sq_item.stock_uom, + sq_item.request_for_quotation, + sq_item.lead_time_days, + sq.supplier.as_("supplier_name"), + sq.valid_till, + ) + .where( + (sq_item.parent == sq.name) + & (sq_item.docstatus < 2) + & (sq.company == filters.get("company")) + & (sq.transaction_date.between(filters.get("from_date"), filters.get("to_date"))) + ) + .orderby(sq.transaction_date, sq_item.item_code) + ) + if filters.get("item_code"): - conditions += " AND sqi.item_code = %(item_code)s" + query = query.where(sq_item.item_code == filters.get("item_code")) if filters.get("supplier_quotation"): - conditions += " AND sqi.parent in %(supplier_quotation)s" + query = query.where(sq_item.parent.isin(filters.get("supplier_quotation"))) if filters.get("request_for_quotation"): - conditions += " AND sqi.request_for_quotation = %(request_for_quotation)s" + query = query.where(sq_item.request_for_quotation == filters.get("request_for_quotation")) if filters.get("supplier"): - conditions += " AND sq.supplier in %(supplier)s" + query = query.where(sq.supplier.isin(filters.get("supplier"))) if not filters.get("include_expired"): - conditions += " AND sq.status != 'Expired'" + query = query.where(sq.status != "Expired") - return conditions - - -def get_data(filters, conditions): - supplier_quotation_data = frappe.db.sql( - """ - SELECT - sqi.parent, sqi.item_code, - sqi.qty, sqi.stock_qty, sqi.amount, - sqi.uom, sqi.stock_uom, - sqi.request_for_quotation, - sqi.lead_time_days, sq.supplier as supplier_name, sq.valid_till - FROM - `tabSupplier Quotation Item` sqi, - `tabSupplier Quotation` sq - WHERE - sqi.parent = sq.name - AND sqi.docstatus < 2 - AND sq.company = %(company)s - AND sq.transaction_date between %(from_date)s and %(to_date)s - {0} - order by sq.transaction_date, sqi.item_code""".format( - conditions - ), - filters, - as_dict=1, - ) + supplier_quotation_data = query.run(as_dict=True) return supplier_quotation_data diff --git a/erpnext/controllers/selling_controller.py b/erpnext/controllers/selling_controller.py index 5e9c069b1d..e8e9076975 100644 --- a/erpnext/controllers/selling_controller.py +++ b/erpnext/controllers/selling_controller.py @@ -442,11 +442,17 @@ class SellingController(StockController): # For internal transfers use incoming rate as the valuation rate if self.is_internal_transfer(): if d.doctype == "Packed Item": - incoming_rate = flt(d.incoming_rate * d.conversion_factor, d.precision("incoming_rate")) + incoming_rate = flt( + flt(d.incoming_rate, d.precision("incoming_rate")) * d.conversion_factor, + d.precision("incoming_rate"), + ) if d.incoming_rate != incoming_rate: d.incoming_rate = incoming_rate else: - rate = flt(d.incoming_rate * d.conversion_factor, d.precision("rate")) + rate = flt( + flt(d.incoming_rate, d.precision("incoming_rate")) * d.conversion_factor, + d.precision("rate"), + ) if d.rate != rate: d.rate = rate frappe.msgprint( diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 9149b4d857..98dc58677b 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -142,13 +142,15 @@ class StockController(AccountsController): warehouse_with_no_account = [] precision = self.get_debit_field_precision() for item_row in voucher_details: - sle_list = sle_map.get(item_row.name) + sle_rounding_diff = 0.0 if sle_list: for sle in sle_list: if warehouse_account.get(sle.warehouse): # from warehouse account + sle_rounding_diff += flt(sle.stock_value_difference) + self.check_expense_account(item_row) # expense account/ target_warehouse / source_warehouse @@ -191,6 +193,46 @@ class StockController(AccountsController): elif sle.warehouse not in warehouse_with_no_account: warehouse_with_no_account.append(sle.warehouse) + if abs(sle_rounding_diff) > (1.0 / (10**precision)) and self.is_internal_transfer(): + warehouse_asset_account = "" + if self.get("is_internal_customer"): + warehouse_asset_account = warehouse_account[item_row.get("target_warehouse")]["account"] + elif self.get("is_internal_supplier"): + warehouse_asset_account = warehouse_account[item_row.get("warehouse")]["account"] + + expense_account = frappe.db.get_value("Company", self.company, "default_expense_account") + + gl_list.append( + self.get_gl_dict( + { + "account": expense_account, + "against": warehouse_asset_account, + "cost_center": item_row.cost_center, + "project": item_row.project or self.get("project"), + "remarks": _("Rounding gain/loss Entry for Stock Transfer"), + "debit": sle_rounding_diff, + "is_opening": item_row.get("is_opening") or self.get("is_opening") or "No", + }, + warehouse_account[sle.warehouse]["account_currency"], + item=item_row, + ) + ) + + gl_list.append( + self.get_gl_dict( + { + "account": warehouse_asset_account, + "against": expense_account, + "cost_center": item_row.cost_center, + "remarks": _("Rounding gain/loss Entry for Stock Transfer"), + "credit": sle_rounding_diff, + "project": item_row.get("project") or self.get("project"), + "is_opening": item_row.get("is_opening") or self.get("is_opening") or "No", + }, + item=item_row, + ) + ) + if warehouse_with_no_account: for wh in warehouse_with_no_account: if frappe.db.get_value("Warehouse", wh, "company"): diff --git a/erpnext/crm/doctype/lead/lead.json b/erpnext/crm/doctype/lead/lead.json index 99c00ad6e6..8f8a086d99 100644 --- a/erpnext/crm/doctype/lead/lead.json +++ b/erpnext/crm/doctype/lead/lead.json @@ -375,7 +375,7 @@ "depends_on": "eval:!doc.__islocal", "fieldname": "notes_tab", "fieldtype": "Tab Break", - "label": "Notes" + "label": "Comments" }, { "collapsible": 1, @@ -506,7 +506,7 @@ { "fieldname": "dashboard_tab", "fieldtype": "Tab Break", - "label": "Dashboard", + "label": "Connections", "show_dashboard": 1 } ], @@ -514,7 +514,7 @@ "idx": 5, "image_field": "image", "links": [], - "modified": "2022-08-09 18:26:17.101521", + "modified": "2022-10-13 12:42:04.277879", "modified_by": "Administrator", "module": "CRM", "name": "Lead", diff --git a/erpnext/crm/doctype/opportunity/opportunity.json b/erpnext/crm/doctype/opportunity/opportunity.json index fed0c7c79a..07641d20c3 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.json +++ b/erpnext/crm/doctype/opportunity/opportunity.json @@ -544,14 +544,14 @@ "depends_on": "eval:!doc.__islocal", "fieldname": "dashboard_tab", "fieldtype": "Tab Break", - "label": "Dashboard", + "label": "Connections", "show_dashboard": 1 }, { "depends_on": "eval:!doc.__islocal", "fieldname": "notes_tab", "fieldtype": "Tab Break", - "label": "Notes" + "label": "Comments" }, { "fieldname": "notes_html", @@ -622,7 +622,7 @@ "icon": "fa fa-info-sign", "idx": 195, "links": [], - "modified": "2022-08-09 18:26:37.235964", + "modified": "2022-10-13 12:42:21.545636", "modified_by": "Administrator", "module": "CRM", "name": "Opportunity", diff --git a/erpnext/crm/doctype/prospect/prospect.json b/erpnext/crm/doctype/prospect/prospect.json index 820a6c72ea..d32311bc4e 100644 --- a/erpnext/crm/doctype/prospect/prospect.json +++ b/erpnext/crm/doctype/prospect/prospect.json @@ -128,7 +128,7 @@ "depends_on": "eval:!doc.__islocal", "fieldname": "notes_section", "fieldtype": "Tab Break", - "label": "Notes" + "label": "Comments" }, { "depends_on": "eval: !doc.__islocal", @@ -218,7 +218,7 @@ ], "index_web_pages_for_search": 1, "links": [], - "modified": "2022-08-09 18:26:56.950185", + "modified": "2022-10-13 12:29:33.674561", "modified_by": "Administrator", "module": "CRM", "name": "Prospect", diff --git a/erpnext/hooks.py b/erpnext/hooks.py index b8f51f839c..6bc17a3675 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -274,8 +274,6 @@ has_website_permission = { "Timesheet": "erpnext.controllers.website_list_for_contact.has_website_permission", } -dump_report_map = "erpnext.startup.report_data_map.data_map" - before_tests = "erpnext.setup.utils.before_tests" standard_queries = { diff --git a/erpnext/manufacturing/doctype/job_card/job_card.py b/erpnext/manufacturing/doctype/job_card/job_card.py index ed45106634..fb94e8aa99 100644 --- a/erpnext/manufacturing/doctype/job_card/job_card.py +++ b/erpnext/manufacturing/doctype/job_card/job_card.py @@ -133,7 +133,7 @@ class JobCard(Document): (%(from_time)s <= jctl.from_time and %(to_time)s >= jctl.to_time) {0} ) and jctl.name != %(name)s and jc.name != %(parent)s and jc.docstatus < 2 {1} - order by jctl.to_time desc limit 1""".format( + order by jctl.to_time desc""".format( extra_cond, validate_overlap_for ), { diff --git a/erpnext/manufacturing/doctype/job_card/test_job_card.py b/erpnext/manufacturing/doctype/job_card/test_job_card.py index ac7114138c..4d2dab73e3 100644 --- a/erpnext/manufacturing/doctype/job_card/test_job_card.py +++ b/erpnext/manufacturing/doctype/job_card/test_job_card.py @@ -136,6 +136,45 @@ class TestJobCard(FrappeTestCase): ) self.assertRaises(OverlapError, jc2.save) + def test_job_card_overlap_with_capacity(self): + wo2 = make_wo_order_test_record(item="_Test FG Item 2", qty=2) + + workstation = make_workstation(workstation_name=random_string(5)).name + frappe.db.set_value("Workstation", workstation, "production_capacity", 1) + + jc1 = frappe.get_last_doc("Job Card", {"work_order": self.work_order.name}) + jc2 = frappe.get_last_doc("Job Card", {"work_order": wo2.name}) + + jc1.workstation = workstation + jc1.append( + "time_logs", + {"from_time": "2021-01-01 00:00:00", "to_time": "2021-01-01 08:00:00", "completed_qty": 1}, + ) + jc1.save() + + jc2.workstation = workstation + + # add a new entry in same time slice + jc2.append( + "time_logs", + {"from_time": "2021-01-01 00:01:00", "to_time": "2021-01-01 06:00:00", "completed_qty": 1}, + ) + self.assertRaises(OverlapError, jc2.save) + + frappe.db.set_value("Workstation", workstation, "production_capacity", 2) + jc2.load_from_db() + + jc2.workstation = workstation + + # add a new entry in same time slice + jc2.append( + "time_logs", + {"from_time": "2021-01-01 00:01:00", "to_time": "2021-01-01 06:00:00", "completed_qty": 1}, + ) + + jc2.save() + self.assertTrue(jc2.name) + def test_job_card_multiple_materials_transfer(self): "Test transferring RMs separately against Job Card with multiple RMs." self.transfer_material_against = "Job Card" diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 4bb4dcc648..000ee07f2c 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -27,6 +27,7 @@ from erpnext.manufacturing.doctype.bom.bom import get_children as get_bom_childr from erpnext.manufacturing.doctype.bom.bom import validate_bom_no from erpnext.manufacturing.doctype.work_order.work_order import get_item_details from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults +from erpnext.stock.get_item_details import get_conversion_factor from erpnext.utilities.transaction_base import validate_uom_is_integer @@ -648,13 +649,23 @@ class ProductionPlan(Document): else: material_request = material_request_map[key] + conversion_factor = 1.0 + if ( + material_request_type == "Purchase" + and item_doc.purchase_uom + and item_doc.purchase_uom != item_doc.stock_uom + ): + conversion_factor = ( + get_conversion_factor(item_doc.name, item_doc.purchase_uom).get("conversion_factor") or 1.0 + ) + # add item material_request.append( "items", { "item_code": item.item_code, "from_warehouse": item.from_warehouse, - "qty": item.quantity, + "qty": item.quantity / conversion_factor, "schedule_date": schedule_date, "warehouse": item.warehouse, "sales_order": item.sales_order, diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py index 60e6398072..c4ab0f886f 100644 --- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py @@ -806,6 +806,35 @@ class TestProductionPlan(FrappeTestCase): self.assertEqual(pln.status, "Completed") self.assertEqual(pln.po_items[0].produced_qty, 5) + def test_material_request_item_for_purchase_uom(self): + from erpnext.stock.doctype.item.test_item import make_item + + fg_item = make_item(properties={"is_stock_item": 1, "stock_uom": "_Test UOM 1"}).name + bom_item = make_item( + properties={"is_stock_item": 1, "stock_uom": "_Test UOM 1", "purchase_uom": "Nos"} + ).name + + if not frappe.db.exists("UOM Conversion Detail", {"parent": bom_item, "uom": "Nos"}): + doc = frappe.get_doc("Item", bom_item) + doc.append("uoms", {"uom": "Nos", "conversion_factor": 10}) + doc.save() + + make_bom(item=fg_item, raw_materials=[bom_item], source_warehouse="_Test Warehouse - _TC") + + pln = create_production_plan( + item_code=fg_item, planned_qty=10, ignore_existing_ordered_qty=1, stock_uom="_Test UOM 1" + ) + + pln.make_material_request() + for row in frappe.get_all( + "Material Request Item", + filters={"production_plan": pln.name}, + fields=["item_code", "uom", "qty"], + ): + self.assertEqual(row.item_code, bom_item) + self.assertEqual(row.uom, "Nos") + self.assertEqual(row.qty, 1) + def create_production_plan(**args): """ diff --git a/erpnext/projects/workspace/projects/projects.json b/erpnext/projects/workspace/projects/projects.json index 1253649e49..4bdb1db387 100644 --- a/erpnext/projects/workspace/projects/projects.json +++ b/erpnext/projects/workspace/projects/projects.json @@ -5,7 +5,7 @@ "label": "Open Projects" } ], - "content": "[{\"type\":\"chart\",\"data\":{\"chart_name\":\"Open Projects\",\"col\":12}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Task\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Project\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Timesheet\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Project Billing Summary\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Projects\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Time Tracking\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}}]", + "content": "[{\"type\":\"chart\",\"data\":{\"chart_name\":\"Open Projects\",\"col\":12}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Task\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Project\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Timesheet\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Project Billing Summary\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Projects\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Time Tracking\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}}]", "creation": "2020-03-02 15:46:04.874669", "docstatus": 0, "doctype": "Workspace", @@ -170,9 +170,27 @@ "link_type": "Report", "onboard": 0, "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Settings", + "link_count": 1, + "onboard": 0, + "type": "Card Break" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Projects Settings", + "link_count": 0, + "link_to": "Projects Settings", + "link_type": "DocType", + "onboard": 0, + "type": "Link" } ], - "modified": "2022-06-28 12:31:30.167740", + "modified": "2022-10-11 22:39:10.436311", "modified_by": "Administrator", "module": "Projects", "name": "Projects", diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index c17610b58a..7fecb18fad 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -426,6 +426,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe if(!this.validate_company_and_party()) { this.frm.fields_dict["items"].grid.grid_rows[item.idx - 1].remove(); } else { + item.pricing_rules = '' return this.frm.call({ method: "erpnext.stock.get_item_details.get_item_details", child: item, @@ -1045,6 +1046,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe uom(doc, cdt, cdn) { var me = this; var item = frappe.get_doc(cdt, cdn); + item.pricing_rules = '' if(item.item_code && item.uom) { return this.frm.call({ method: "erpnext.stock.get_item_details.get_conversion_factor", @@ -1121,6 +1123,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe qty(doc, cdt, cdn) { let item = frappe.get_doc(cdt, cdn); + item.pricing_rules = '' this.conversion_factor(doc, cdt, cdn, true); this.calculate_stock_uom_rate(doc, cdt, cdn); this.apply_pricing_rule(item, true); diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index 1a309ba015..b0082bdb28 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -28,7 +28,7 @@ erpnext.financial_statements = { }, "open_general_ledger": function(data) { if (!data.account) return; - var project = $.grep(frappe.query_report.filters, function(e){ return e.df.fieldname == 'project'; }) + let project = $.grep(frappe.query_report.filters, function(e){ return e.df.fieldname == 'project'; }); frappe.route_options = { "account": data.account, @@ -37,7 +37,16 @@ erpnext.financial_statements = { "to_date": data.to_date || data.year_end_date, "project": (project && project.length > 0) ? project[0].$input.val() : "" }; - frappe.set_route("query-report", "General Ledger"); + + let report = "General Ledger"; + + if (["Payable", "Receivable"].includes(data.account_type)) { + report = data.account_type == "Payable" ? "Accounts Payable" : "Accounts Receivable"; + frappe.route_options["party_account"] = data.account; + frappe.route_options["report_date"] = data.year_end_date; + } + + frappe.set_route("query-report", report); }, "tree": true, "name_field": "account", diff --git a/erpnext/regional/united_arab_emirates/setup.py b/erpnext/regional/united_arab_emirates/setup.py index be621bcdd1..36a079546e 100644 --- a/erpnext/regional/united_arab_emirates/setup.py +++ b/erpnext/regional/united_arab_emirates/setup.py @@ -37,7 +37,7 @@ def make_custom_fields(): fieldname="vat_section", label="VAT Details", fieldtype="Section Break", - insert_after="group_same_items", + insert_after="language", print_hide=1, collapsible=1, ), diff --git a/erpnext/selling/doctype/quotation/quotation.js b/erpnext/selling/doctype/quotation/quotation.js index 70ae085051..6b42e4daea 100644 --- a/erpnext/selling/doctype/quotation/quotation.js +++ b/erpnext/selling/doctype/quotation/quotation.js @@ -84,11 +84,12 @@ erpnext.selling.QuotationController = class QuotationController extends erpnext. } } - if(doc.docstatus == 1 && !(['Lost', 'Ordered']).includes(doc.status)) { - if(!doc.valid_till || frappe.datetime.get_diff(doc.valid_till, frappe.datetime.get_today()) >= 0) { - cur_frm.add_custom_button(__('Sales Order'), - cur_frm.cscript['Make Sales Order'], __('Create')); - } + if (doc.docstatus == 1 && !["Lost", "Ordered"].includes(doc.status)) { + this.frm.add_custom_button( + __("Sales Order"), + this.frm.cscript["Make Sales Order"], + __("Create") + ); if(doc.status!=="Ordered") { this.frm.add_custom_button(__('Set as Lost'), () => { diff --git a/erpnext/selling/doctype/quotation/quotation.json b/erpnext/selling/doctype/quotation/quotation.json index c58a46ba51..fa64b1625b 100644 --- a/erpnext/selling/doctype/quotation/quotation.json +++ b/erpnext/selling/doctype/quotation/quotation.json @@ -15,26 +15,13 @@ "quotation_to", "party_name", "customer_name", - "column_break1", - "amended_from", - "company", + "column_break_7", "transaction_date", "valid_till", + "column_break1", "order_type", - "contact_section", - "customer_address", - "address_display", - "contact_person", - "contact_display", - "contact_mobile", - "contact_email", - "col_break98", - "shipping_address_name", - "shipping_address", - "company_address", - "company_address_display", - "customer_group", - "territory", + "company", + "amended_from", "currency_and_price_list", "currency", "conversion_rate", @@ -43,79 +30,105 @@ "price_list_currency", "plc_conversion_rate", "ignore_pricing_rule", - "section_break_33", - "scan_barcode", "items_section", + "scan_barcode", "items", - "bundle_items_section", - "packed_items", - "pricing_rule_details", - "pricing_rules", "sec_break23", "total_qty", + "total_net_weight", + "column_break_28", "base_total", "base_net_total", - "column_break_28", + "column_break_31", "total", "net_total", - "total_net_weight", "taxes_section", + "taxes_and_charges", + "column_break_36", "tax_category", "column_break_34", "shipping_rule", "section_break_36", - "taxes_and_charges", "taxes", - "sec_tax_breakup", - "other_charges_calculation", "section_break_39", "base_total_taxes_and_charges", "column_break_42", "total_taxes_and_charges", - "section_break_44", - "coupon_code", - "referral_sales_partner", - "apply_discount_on", - "base_discount_amount", - "column_break_46", - "additional_discount_percentage", - "discount_amount", "totals", "base_grand_total", "base_rounding_adjustment", - "base_in_words", "base_rounded_total", + "base_in_words", "column_break3", "grand_total", "rounding_adjustment", "rounded_total", "in_words", + "section_break_44", + "apply_discount_on", + "base_discount_amount", + "coupon_code", + "column_break_46", + "additional_discount_percentage", + "discount_amount", + "referral_sales_partner", + "sec_tax_breakup", + "other_charges_calculation", + "bundle_items_section", + "packed_items", + "pricing_rule_details", + "pricing_rules", + "address_and_contact_tab", + "billing_address_section", + "customer_address", + "address_display", + "col_break98", + "contact_person", + "contact_display", + "contact_mobile", + "contact_email", + "shipping_address_section", + "shipping_address_name", + "column_break_81", + "shipping_address", + "company_address_section", + "company_address", + "column_break_87", + "company_address_display", + "terms_tab", "payment_schedule_section", "payment_terms_template", "payment_schedule", "terms_section_break", "tc_name", "terms", + "more_info_tab", + "subscription_section", + "auto_repeat", + "update_auto_repeat_reference", "print_settings", "letter_head", "group_same_items", "column_break_73", "select_print_heading", "language", - "subscription_section", - "auto_repeat", - "update_auto_repeat_reference", - "more_info", + "lost_reasons_section", + "lost_reasons", + "competitors", + "column_break_117", + "order_lost_reason", + "additional_info_section", + "status", + "customer_group", + "territory", + "column_break_108", "campaign", "source", - "order_lost_reason", "column_break4", - "status", - "enq_det", - "supplier_quotation", "opportunity", - "lost_reasons", - "competitors" + "supplier_quotation", + "enq_det", + "connections_tab" ], "fields": [ { @@ -241,14 +254,6 @@ "print_hide": 1, "reqd": 1 }, - { - "collapsible": 1, - "depends_on": "party_name", - "fieldname": "contact_section", - "fieldtype": "Section Break", - "label": "Address and Contact", - "options": "fa fa-bullhorn" - }, { "fieldname": "customer_address", "fieldtype": "Link", @@ -296,7 +301,7 @@ "read_only": 1 }, { - "depends_on": "eval:doc.quotation_to=='Customer' && doc.party_name", + "depends_on": "eval:(doc.quotation_to=='Customer' && doc.party_name)", "fieldname": "col_break98", "fieldtype": "Column Break", "width": "50%" @@ -316,7 +321,7 @@ "read_only": 1 }, { - "depends_on": "eval:doc.quotation_to=='Customer' && doc.party_name", + "depends_on": "eval:(doc.quotation_to=='Customer' && doc.party_name)", "fieldname": "customer_group", "fieldtype": "Link", "hidden": 1, @@ -408,6 +413,8 @@ { "fieldname": "items_section", "fieldtype": "Section Break", + "hide_border": 1, + "label": "Items", "oldfieldtype": "Section Break", "options": "fa fa-shopping-cart" }, @@ -415,7 +422,6 @@ "allow_bulk_edit": 1, "fieldname": "items", "fieldtype": "Table", - "label": "Items", "oldfieldname": "quotation_details", "oldfieldtype": "Table", "options": "Quotation Item", @@ -423,6 +429,7 @@ "width": "40px" }, { + "collapsible": 1, "fieldname": "pricing_rule_details", "fieldtype": "Section Break", "label": "Pricing Rules" @@ -483,6 +490,7 @@ "read_only": 1 }, { + "depends_on": "total_net_weight", "fieldname": "total_net_weight", "fieldtype": "Float", "label": "Total Net Weight", @@ -492,6 +500,7 @@ { "fieldname": "taxes_section", "fieldtype": "Section Break", + "hide_border": 1, "label": "Taxes and Charges", "oldfieldtype": "Section Break", "options": "fa fa-money" @@ -517,7 +526,8 @@ }, { "fieldname": "section_break_36", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "hide_border": 1 }, { "fieldname": "taxes_and_charges", @@ -579,10 +589,9 @@ }, { "collapsible": 1, - "collapsible_depends_on": "discount_amount", "fieldname": "section_break_44", "fieldtype": "Section Break", - "label": "Additional Discount and Coupon Code" + "label": "Additional Discount" }, { "fieldname": "coupon_code", @@ -632,6 +641,7 @@ { "fieldname": "totals", "fieldtype": "Section Break", + "label": "Totals", "oldfieldtype": "Section Break", "options": "fa fa-money", "print_hide": 1 @@ -657,7 +667,6 @@ "read_only": 1 }, { - "description": "In Words will be visible once you save the Quotation.", "fieldname": "base_in_words", "fieldtype": "Data", "label": "In Words (Company Currency)", @@ -665,8 +674,7 @@ "oldfieldname": "in_words", "oldfieldtype": "Data", "print_hide": 1, - "read_only": 1, - "width": "200px" + "read_only": 1 }, { "fieldname": "base_rounded_total", @@ -749,7 +757,6 @@ "print_hide": 1 }, { - "collapsible": 1, "collapsible_depends_on": "terms", "fieldname": "terms_section_break", "fieldtype": "Section Break", @@ -824,7 +831,7 @@ { "fieldname": "subscription_section", "fieldtype": "Section Break", - "label": "Auto Repeat Section" + "label": "Auto Repeat" }, { "fieldname": "auto_repeat", @@ -837,20 +844,11 @@ }, { "allow_on_submit": 1, - "depends_on": "eval: doc.auto_repeat", + "depends_on": "eval:doc.auto_repeat", "fieldname": "update_auto_repeat_reference", "fieldtype": "Button", "label": "Update Auto Repeat Reference" }, - { - "collapsible": 1, - "fieldname": "more_info", - "fieldtype": "Section Break", - "label": "More Information", - "oldfieldtype": "Section Break", - "options": "fa fa-file-text", - "print_hide": 1 - }, { "fieldname": "campaign", "fieldtype": "Link", @@ -871,7 +869,7 @@ }, { "allow_on_submit": 1, - "depends_on": "eval:doc.status===\"Lost\"", + "depends_on": "eval:doc.status=='Lost'", "fieldname": "order_lost_reason", "fieldtype": "Small Text", "label": "Detailed Reason", @@ -934,7 +932,6 @@ "read_only": 1 }, { - "depends_on": "packed_items", "fieldname": "packed_items", "fieldtype": "Table", "label": "Bundle Items", @@ -943,7 +940,6 @@ }, { "collapsible": 1, - "collapsible_depends_on": "packed_items", "depends_on": "packed_items", "fieldname": "bundle_items_section", "fieldtype": "Section Break", @@ -970,22 +966,99 @@ "label": "Company Address", "read_only": 1 }, - { - "fieldname": "section_break_33", - "fieldtype": "Section Break" - }, { "fieldname": "scan_barcode", "fieldtype": "Data", "label": "Scan Barcode", "options": "Barcode" + }, + { + "fieldname": "address_and_contact_tab", + "fieldtype": "Tab Break", + "label": "Address & Contact" + }, + { + "fieldname": "terms_tab", + "fieldtype": "Tab Break", + "label": "Terms" + }, + { + "fieldname": "more_info_tab", + "fieldtype": "Tab Break", + "label": "More Info" + }, + { + "fieldname": "connections_tab", + "fieldtype": "Tab Break", + "label": "Connections", + "show_dashboard": 1 + }, + { + "fieldname": "column_break_7", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_31", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_36", + "fieldtype": "Column Break" + }, + { + "fieldname": "billing_address_section", + "fieldtype": "Section Break", + "label": "Billing Address", + "options": "fa fa-bullhorn" + }, + { + "fieldname": "shipping_address_section", + "fieldtype": "Section Break", + "label": "Shipping Address" + }, + { + "fieldname": "column_break_81", + "fieldtype": "Column Break" + }, + { + "fieldname": "company_address_section", + "fieldtype": "Section Break", + "label": "Company Address" + }, + { + "fieldname": "column_break_87", + "fieldtype": "Column Break" + }, + { + "collapsible": 1, + "depends_on": "eval:(doc.lost_reasons || doc.order_lost_reason)", + "fieldname": "lost_reasons_section", + "fieldtype": "Section Break", + "label": "Lost Reasons" + }, + { + "fieldname": "column_break_117", + "fieldtype": "Column Break" + }, + { + "collapsible": 1, + "fieldname": "additional_info_section", + "fieldtype": "Section Break", + "label": "Additional Info", + "oldfieldtype": "Section Break", + "options": "fa fa-file-text", + "print_hide": 1 + }, + { + "fieldname": "column_break_108", + "fieldtype": "Column Break" } ], "icon": "fa fa-shopping-cart", "idx": 82, "is_submittable": 1, "links": [], - "modified": "2022-09-16 17:44:43.221804", + "modified": "2022-10-11 13:06:33.479650", "modified_by": "Administrator", "module": "Selling", "name": "Quotation", @@ -1083,4 +1156,4 @@ "states": [], "timeline_field": "party_name", "title_field": "title" -} +} \ No newline at end of file diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json index ff269d0e68..e6ff39d8d4 100644 --- a/erpnext/selling/doctype/sales_order/sales_order.json +++ b/erpnext/selling/doctype/sales_order/sales_order.json @@ -15,37 +15,25 @@ "naming_series", "customer", "customer_name", + "tax_id", "order_type", - "skip_delivery_note", - "column_break1", - "amended_from", - "company", + "column_break_7", "transaction_date", "delivery_date", + "column_break1", "po_no", "po_date", - "tax_id", + "company", + "skip_delivery_note", + "amended_from", "accounting_dimensions_section", "cost_center", "dimension_col_break", "project", - "contact_info", - "customer_address", - "address_display", - "contact_person", - "contact_display", - "contact_phone", - "contact_mobile", - "contact_email", - "company_address", - "company_address_display", - "col_break46", - "shipping_address_name", - "shipping_address", - "dispatch_address_name", - "dispatch_address", - "customer_group", - "territory", + "column_break_77", + "source", + "campaign", + "custom_dimensions_section", "currency_and_price_list", "currency", "conversion_rate", @@ -55,46 +43,32 @@ "plc_conversion_rate", "ignore_pricing_rule", "sec_warehouse", + "scan_barcode", + "column_break_28", "set_warehouse", "items_section", - "scan_barcode", "items", - "packing_list", - "packed_items", - "pricing_rule_details", - "pricing_rules", "section_break_31", - "column_break_33a", "total_qty", + "total_net_weight", + "column_break_33", "base_total", "base_net_total", - "column_break_33", - "total_net_weight", + "column_break_33a", "total", "net_total", "taxes_section", - "tax_category", + "taxes_and_charges", "column_break_38", + "tax_category", + "column_break_49", "shipping_rule", "section_break_40", - "taxes_and_charges", "taxes", - "sec_tax_breakup", - "other_charges_calculation", "section_break_43", "base_total_taxes_and_charges", "column_break_46", "total_taxes_and_charges", - "loyalty_points_redemption", - "loyalty_points", - "loyalty_amount", - "section_break_48", - "coupon_code", - "apply_discount_on", - "base_discount_amount", - "column_break_50", - "additional_discount_percentage", - "discount_amount", "totals", "base_grand_total", "base_rounding_adjustment", @@ -107,26 +81,49 @@ "in_words", "advance_paid", "disable_rounded_total", + "section_break_48", + "apply_discount_on", + "base_discount_amount", + "coupon_code", + "column_break_50", + "additional_discount_percentage", + "discount_amount", + "sec_tax_breakup", + "other_charges_calculation", + "packing_list", + "packed_items", + "pricing_rule_details", + "pricing_rules", + "contact_info", + "billing_address_column", + "customer_address", + "address_display", + "customer_group", + "territory", + "column_break_84", + "contact_person", + "contact_display", + "contact_phone", + "contact_mobile", + "contact_email", + "shipping_address_column", + "shipping_address_name", + "shipping_address", + "column_break_93", + "dispatch_address_name", + "dispatch_address", + "col_break46", + "company_address", + "column_break_92", + "company_address_display", "payment_schedule_section", + "payment_terms_section", "payment_terms_template", "payment_schedule", "terms_section_break", "tc_name", "terms", "more_info", - "is_internal_customer", - "represents_company", - "inter_company_order_reference", - "party_account_currency", - "column_break_77", - "source", - "campaign", - "printing_details", - "language", - "letter_head", - "column_break4", - "select_print_heading", - "group_same_items", "section_break_78", "status", "delivery_status", @@ -143,12 +140,29 @@ "total_commission", "section_break1", "sales_team", + "loyalty_points_redemption", + "loyalty_points", + "column_break_116", + "loyalty_amount", "subscription_section", "from_date", "to_date", "column_break_108", "auto_repeat", - "update_auto_repeat_reference" + "update_auto_repeat_reference", + "printing_details", + "letter_head", + "group_same_items", + "column_break4", + "select_print_heading", + "language", + "additional_info_section", + "is_internal_customer", + "represents_company", + "column_break_152", + "inter_company_order_reference", + "party_account_currency", + "connections_tab" ], "fields": [ { @@ -334,10 +348,10 @@ "collapsible": 1, "depends_on": "customer", "fieldname": "contact_info", - "fieldtype": "Section Break", + "fieldtype": "Tab Break", "hide_days": 1, "hide_seconds": 1, - "label": "Address and Contact", + "label": "Address & Contact", "options": "fa fa-bullhorn" }, { @@ -414,9 +428,10 @@ }, { "fieldname": "col_break46", - "fieldtype": "Column Break", + "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, + "label": "Company Address", "width": "50%" }, { @@ -550,8 +565,10 @@ { "fieldname": "sec_warehouse", "fieldtype": "Section Break", + "hide_border": 1, "hide_days": 1, - "hide_seconds": 1 + "hide_seconds": 1, + "label": "Items" }, { "fieldname": "set_warehouse", @@ -565,6 +582,7 @@ { "fieldname": "items_section", "fieldtype": "Section Break", + "hide_border": 1, "hide_days": 1, "hide_seconds": 1, "oldfieldtype": "Section Break", @@ -675,6 +693,7 @@ "read_only": 1 }, { + "depends_on": "total_net_weight", "fieldname": "total_net_weight", "fieldtype": "Float", "hide_days": 1, @@ -688,7 +707,7 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Taxes and Charges", + "label": "Taxes", "oldfieldtype": "Section Break", "options": "fa fa-money" }, @@ -720,6 +739,7 @@ { "fieldname": "section_break_40", "fieldtype": "Section Break", + "hide_border": 1, "hide_days": 1, "hide_seconds": 1 }, @@ -804,7 +824,7 @@ "hidden": 1, "hide_days": 1, "hide_seconds": 1, - "label": "Loyalty Points Redemption", + "label": "Loyalty Points", "print_hide": 1 }, { @@ -828,12 +848,11 @@ }, { "collapsible": 1, - "collapsible_depends_on": "discount_amount", "fieldname": "section_break_48", "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Additional Discount and Coupon Code" + "label": "Additional Discount" }, { "fieldname": "coupon_code", @@ -891,6 +910,7 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, + "label": "Totals", "oldfieldtype": "Section Break", "options": "fa fa-money", "print_hide": 1 @@ -1045,10 +1065,10 @@ }, { "fieldname": "payment_schedule_section", - "fieldtype": "Section Break", + "fieldtype": "Tab Break", "hide_days": 1, "hide_seconds": 1, - "label": "Payment Terms" + "label": "Terms" }, { "fieldname": "payment_terms_template", @@ -1070,13 +1090,12 @@ "print_hide": 1 }, { - "collapsible": 1, "collapsible_depends_on": "terms", "fieldname": "terms_section_break", "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Terms and Conditions", + "label": "Terms & Conditions", "oldfieldtype": "Section Break", "options": "fa fa-legal" }, @@ -1104,10 +1123,10 @@ "collapsible": 1, "collapsible_depends_on": "project", "fieldname": "more_info", - "fieldtype": "Section Break", + "fieldtype": "Tab Break", "hide_days": 1, "hide_seconds": 1, - "label": "More Information", + "label": "More Info", "oldfieldtype": "Section Break", "options": "fa fa-file-text", "print_hide": 1 @@ -1122,7 +1141,6 @@ "read_only": 1 }, { - "description": "Track this Sales Order against any Project", "fieldname": "project", "fieldtype": "Link", "hide_days": 1, @@ -1240,7 +1258,7 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Billing and Delivery Status", + "label": "Status", "oldfieldtype": "Column Break", "print_hide": 1, "width": "50%" @@ -1410,7 +1428,7 @@ "fieldtype": "Section Break", "hide_days": 1, "hide_seconds": 1, - "label": "Auto Repeat Section", + "label": "Auto Repeat", "no_copy": 1, "print_hide": 1, "read_only": 1 @@ -1542,13 +1560,76 @@ { "fieldname": "dimension_col_break", "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_7", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_28", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_49", + "fieldtype": "Column Break" + }, + { + "fieldname": "connections_tab", + "fieldtype": "Tab Break", + "label": "Connections", + "show_dashboard": 1 + }, + { + "fieldname": "payment_terms_section", + "fieldtype": "Section Break", + "label": "Payment Terms" + }, + { + "fieldname": "column_break_116", + "fieldtype": "Column Break" + }, + { + "fieldname": "billing_address_column", + "fieldtype": "Section Break", + "label": "Billing Address" + }, + { + "fieldname": "shipping_address_column", + "fieldtype": "Section Break", + "label": "Shipping Address" + }, + { + "fieldname": "column_break_93", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_84", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_92", + "fieldtype": "Column Break" + }, + { + "fieldname": "custom_dimensions_section", + "fieldtype": "Section Break" + }, + { + "collapsible": 1, + "fieldname": "additional_info_section", + "fieldtype": "Section Break", + "label": "Additional Info" + }, + { + "fieldname": "column_break_152", + "fieldtype": "Column Break" } ], "icon": "fa fa-file-text", "idx": 105, "is_submittable": 1, "links": [], - "modified": "2022-09-16 17:43:57.007441", + "modified": "2022-10-11 13:06:10.469796", "modified_by": "Administrator", "module": "Selling", "name": "Sales Order", diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py index 25806d6ed8..f0e9e4b7d9 100755 --- a/erpnext/selling/doctype/sales_order/sales_order.py +++ b/erpnext/selling/doctype/sales_order/sales_order.py @@ -18,6 +18,7 @@ from erpnext.accounts.doctype.sales_invoice.sales_invoice import ( update_linked_doc, validate_inter_company_party, ) +from erpnext.accounts.party import get_party_account from erpnext.controllers.selling_controller import SellingController from erpnext.manufacturing.doctype.production_plan.production_plan import ( get_items_for_material_requests, @@ -727,6 +728,8 @@ def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False): if source.loyalty_points and source.order_type == "Shopping Cart": target.redeem_loyalty_points = 1 + target.debit_to = get_party_account("Customer", source.customer, source.company) + def update_item(source, target, source_parent): target.amount = flt(source.amount) - flt(source.billed_amt) target.base_amount = target.amount * flt(source_parent.conversion_rate) diff --git a/erpnext/setup/doctype/brand/brand.js b/erpnext/setup/doctype/brand/brand.js index 3680906057..0abb71a362 100644 --- a/erpnext/setup/doctype/brand/brand.js +++ b/erpnext/setup/doctype/brand/brand.js @@ -1,13 +1,71 @@ // Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors // License: GNU General Public License v3. See license.txt +frappe.ui.form.on('Brand', { + setup: (frm) => { + frm.fields_dict["brand_defaults"].grid.get_field("default_warehouse").get_query = function(doc, cdt, cdn) { + const row = locals[cdt][cdn]; + return { + filters: { company: row.company } + } + } + frm.fields_dict["brand_defaults"].grid.get_field("default_discount_account").get_query = function(doc, cdt, cdn) { + const row = locals[cdt][cdn]; + return { + filters: { + 'report_type': 'Profit and Loss', + 'company': row.company, + "is_group": 0 + } + }; + } -//--------- ONLOAD ------------- -cur_frm.cscript.onload = function(doc, cdt, cdn) { + frm.fields_dict["brand_defaults"].grid.get_field("buying_cost_center").get_query = function(doc, cdt, cdn) { + const row = locals[cdt][cdn]; + return { + filters: { + "is_group": 0, + "company": row.company + } + } + } -} + frm.fields_dict["brand_defaults"].grid.get_field("expense_account").get_query = function(doc, cdt, cdn) { + const row = locals[cdt][cdn]; + return { + query: "erpnext.controllers.queries.get_expense_account", + filters: { company: row.company } + } + } -cur_frm.cscript.refresh = function(doc, cdt, cdn) { + frm.fields_dict["brand_defaults"].grid.get_field("default_provisional_account").get_query = function(doc, cdt, cdn) { + const row = locals[cdt][cdn]; + return { + filters: { + "company": row.company, + "root_type": ["in", ["Liability", "Asset"]], + "is_group": 0 + } + }; + } -} + frm.fields_dict["brand_defaults"].grid.get_field("selling_cost_center").get_query = function(doc, cdt, cdn) { + const row = locals[cdt][cdn]; + return { + filters: { + "is_group": 0, + "company": row.company + } + } + } + + frm.fields_dict["brand_defaults"].grid.get_field("income_account").get_query = function(doc, cdt, cdn) { + const row = locals[cdt][cdn]; + return { + query: "erpnext.controllers.queries.get_income_account", + filters: { company: row.company } + } + } + } +}); \ No newline at end of file diff --git a/erpnext/startup/report_data_map.py b/erpnext/startup/report_data_map.py deleted file mode 100644 index f8c1b6cca0..0000000000 --- a/erpnext/startup/report_data_map.py +++ /dev/null @@ -1,327 +0,0 @@ -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# License: GNU General Public License v3. See license.txt - - -# mappings for table dumps -# "remember to add indexes!" - -data_map = { - "Company": {"columns": ["name"], "conditions": ["docstatus < 2"]}, - "Fiscal Year": { - "columns": ["name", "year_start_date", "year_end_date"], - "conditions": ["docstatus < 2"], - }, - # Accounts - "Account": { - "columns": ["name", "parent_account", "lft", "rgt", "report_type", "company", "is_group"], - "conditions": ["docstatus < 2"], - "order_by": "lft", - "links": { - "company": ["Company", "name"], - }, - }, - "Cost Center": { - "columns": ["name", "lft", "rgt"], - "conditions": ["docstatus < 2"], - "order_by": "lft", - }, - "GL Entry": { - "columns": [ - "name", - "account", - "posting_date", - "cost_center", - "debit", - "credit", - "is_opening", - "company", - "voucher_type", - "voucher_no", - "remarks", - ], - "order_by": "posting_date, account", - "links": { - "account": ["Account", "name"], - "company": ["Company", "name"], - "cost_center": ["Cost Center", "name"], - }, - }, - # Stock - "Item": { - "columns": [ - "name", - "if(item_name=name, '', item_name) as item_name", - "description", - "item_group as parent_item_group", - "stock_uom", - "brand", - "valuation_method", - ], - # "conditions": ["docstatus < 2"], - "order_by": "name", - "links": {"parent_item_group": ["Item Group", "name"], "brand": ["Brand", "name"]}, - }, - "Item Group": { - "columns": ["name", "parent_item_group"], - # "conditions": ["docstatus < 2"], - "order_by": "lft", - }, - "Brand": {"columns": ["name"], "conditions": ["docstatus < 2"], "order_by": "name"}, - "Project": {"columns": ["name"], "conditions": ["docstatus < 2"], "order_by": "name"}, - "Warehouse": {"columns": ["name"], "conditions": ["docstatus < 2"], "order_by": "name"}, - "Stock Ledger Entry": { - "columns": [ - "name", - "posting_date", - "posting_time", - "item_code", - "warehouse", - "actual_qty as qty", - "voucher_type", - "voucher_no", - "project", - "incoming_rate as incoming_rate", - "stock_uom", - "serial_no", - "qty_after_transaction", - "valuation_rate", - ], - "order_by": "posting_date, posting_time, creation", - "links": { - "item_code": ["Item", "name"], - "warehouse": ["Warehouse", "name"], - "project": ["Project", "name"], - }, - "force_index": "posting_sort_index", - }, - "Serial No": { - "columns": ["name", "purchase_rate as incoming_rate"], - "conditions": ["docstatus < 2"], - "order_by": "name", - }, - "Stock Entry": { - "columns": ["name", "purpose"], - "conditions": ["docstatus=1"], - "order_by": "posting_date, posting_time, name", - }, - "Material Request Item": { - "columns": ["item.name as name", "item_code", "warehouse", "(qty - ordered_qty) as qty"], - "from": "`tabMaterial Request Item` item, `tabMaterial Request` main", - "conditions": [ - "item.parent = main.name", - "main.docstatus=1", - "main.status != 'Stopped'", - "ifnull(warehouse, '')!=''", - "qty > ordered_qty", - ], - "links": {"item_code": ["Item", "name"], "warehouse": ["Warehouse", "name"]}, - }, - "Purchase Order Item": { - "columns": [ - "item.name as name", - "item_code", - "warehouse", - "(qty - received_qty)*conversion_factor as qty", - ], - "from": "`tabPurchase Order Item` item, `tabPurchase Order` main", - "conditions": [ - "item.parent = main.name", - "main.docstatus=1", - "main.status != 'Stopped'", - "ifnull(warehouse, '')!=''", - "qty > received_qty", - ], - "links": {"item_code": ["Item", "name"], "warehouse": ["Warehouse", "name"]}, - }, - "Sales Order Item": { - "columns": [ - "item.name as name", - "item_code", - "(qty - delivered_qty)*conversion_factor as qty", - "warehouse", - ], - "from": "`tabSales Order Item` item, `tabSales Order` main", - "conditions": [ - "item.parent = main.name", - "main.docstatus=1", - "main.status != 'Stopped'", - "ifnull(warehouse, '')!=''", - "qty > delivered_qty", - ], - "links": {"item_code": ["Item", "name"], "warehouse": ["Warehouse", "name"]}, - }, - # Sales - "Customer": { - "columns": [ - "name", - "if(customer_name=name, '', customer_name) as customer_name", - "customer_group as parent_customer_group", - "territory as parent_territory", - ], - "conditions": ["docstatus < 2"], - "order_by": "name", - "links": { - "parent_customer_group": ["Customer Group", "name"], - "parent_territory": ["Territory", "name"], - }, - }, - "Customer Group": { - "columns": ["name", "parent_customer_group"], - "conditions": ["docstatus < 2"], - "order_by": "lft", - }, - "Territory": { - "columns": ["name", "parent_territory"], - "conditions": ["docstatus < 2"], - "order_by": "lft", - }, - "Sales Invoice": { - "columns": ["name", "customer", "posting_date", "company"], - "conditions": ["docstatus=1"], - "order_by": "posting_date", - "links": {"customer": ["Customer", "name"], "company": ["Company", "name"]}, - }, - "Sales Invoice Item": { - "columns": ["name", "parent", "item_code", "stock_qty as qty", "base_net_amount"], - "conditions": ["docstatus=1", "ifnull(parent, '')!=''"], - "order_by": "parent", - "links": {"parent": ["Sales Invoice", "name"], "item_code": ["Item", "name"]}, - }, - "Sales Order": { - "columns": ["name", "customer", "transaction_date as posting_date", "company"], - "conditions": ["docstatus=1"], - "order_by": "transaction_date", - "links": {"customer": ["Customer", "name"], "company": ["Company", "name"]}, - }, - "Sales Order Item[Sales Analytics]": { - "columns": ["name", "parent", "item_code", "stock_qty as qty", "base_net_amount"], - "conditions": ["docstatus=1", "ifnull(parent, '')!=''"], - "order_by": "parent", - "links": {"parent": ["Sales Order", "name"], "item_code": ["Item", "name"]}, - }, - "Delivery Note": { - "columns": ["name", "customer", "posting_date", "company"], - "conditions": ["docstatus=1"], - "order_by": "posting_date", - "links": {"customer": ["Customer", "name"], "company": ["Company", "name"]}, - }, - "Delivery Note Item[Sales Analytics]": { - "columns": ["name", "parent", "item_code", "stock_qty as qty", "base_net_amount"], - "conditions": ["docstatus=1", "ifnull(parent, '')!=''"], - "order_by": "parent", - "links": {"parent": ["Delivery Note", "name"], "item_code": ["Item", "name"]}, - }, - "Supplier": { - "columns": [ - "name", - "if(supplier_name=name, '', supplier_name) as supplier_name", - "supplier_group as parent_supplier_group", - ], - "conditions": ["docstatus < 2"], - "order_by": "name", - "links": { - "parent_supplier_group": ["Supplier Group", "name"], - }, - }, - "Supplier Group": { - "columns": ["name", "parent_supplier_group"], - "conditions": ["docstatus < 2"], - "order_by": "name", - }, - "Purchase Invoice": { - "columns": ["name", "supplier", "posting_date", "company"], - "conditions": ["docstatus=1"], - "order_by": "posting_date", - "links": {"supplier": ["Supplier", "name"], "company": ["Company", "name"]}, - }, - "Purchase Invoice Item": { - "columns": ["name", "parent", "item_code", "stock_qty as qty", "base_net_amount"], - "conditions": ["docstatus=1", "ifnull(parent, '')!=''"], - "order_by": "parent", - "links": {"parent": ["Purchase Invoice", "name"], "item_code": ["Item", "name"]}, - }, - "Purchase Order": { - "columns": ["name", "supplier", "transaction_date as posting_date", "company"], - "conditions": ["docstatus=1"], - "order_by": "posting_date", - "links": {"supplier": ["Supplier", "name"], "company": ["Company", "name"]}, - }, - "Purchase Order Item[Purchase Analytics]": { - "columns": ["name", "parent", "item_code", "stock_qty as qty", "base_net_amount"], - "conditions": ["docstatus=1", "ifnull(parent, '')!=''"], - "order_by": "parent", - "links": {"parent": ["Purchase Order", "name"], "item_code": ["Item", "name"]}, - }, - "Purchase Receipt": { - "columns": ["name", "supplier", "posting_date", "company"], - "conditions": ["docstatus=1"], - "order_by": "posting_date", - "links": {"supplier": ["Supplier", "name"], "company": ["Company", "name"]}, - }, - "Purchase Receipt Item[Purchase Analytics]": { - "columns": ["name", "parent", "item_code", "stock_qty as qty", "base_net_amount"], - "conditions": ["docstatus=1", "ifnull(parent, '')!=''"], - "order_by": "parent", - "links": {"parent": ["Purchase Receipt", "name"], "item_code": ["Item", "name"]}, - }, - # Support - "Issue": { - "columns": ["name", "status", "creation", "resolution_date", "first_responded_on"], - "conditions": ["docstatus < 2"], - "order_by": "creation", - }, - # Manufacturing - "Work Order": { - "columns": [ - "name", - "status", - "creation", - "planned_start_date", - "planned_end_date", - "status", - "actual_start_date", - "actual_end_date", - "modified", - ], - "conditions": ["docstatus = 1"], - "order_by": "creation", - }, - # Medical - "Patient": { - "columns": [ - "name", - "creation", - "owner", - "if(patient_name=name, '', patient_name) as patient_name", - ], - "conditions": ["docstatus < 2"], - "order_by": "name", - "links": {"owner": ["User", "name"]}, - }, - "Patient Appointment": { - "columns": [ - "name", - "appointment_type", - "patient", - "practitioner", - "appointment_date", - "department", - "status", - "company", - ], - "order_by": "name", - "links": { - "practitioner": ["Healthcare Practitioner", "name"], - "appointment_type": ["Appointment Type", "name"], - }, - }, - "Healthcare Practitioner": { - "columns": ["name", "department"], - "order_by": "name", - "links": { - "department": ["Department", "name"], - }, - }, - "Appointment Type": {"columns": ["name"], "order_by": "name"}, - "Medical Department": {"columns": ["name"], "order_by": "name"}, -} diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json index a8f907ed71..0ca3e69f76 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.json +++ b/erpnext/stock/doctype/delivery_note/delivery_note.json @@ -8,46 +8,29 @@ "document_type": "Document", "engine": "InnoDB", "field_order": [ - "delivery_to_section", - "column_break0", "title", "naming_series", "customer", + "tax_id", "customer_name", "column_break1", - "amended_from", - "company", "posting_date", "posting_time", "set_posting_time", + "column_break_10", + "company", + "amended_from", "is_return", "issue_credit_note", "return_against", "accounting_dimensions_section", "cost_center", - "dimension_col_break", + "column_break_18", "project", - "customer_po_details", - "po_no", - "column_break_17", - "po_date", - "section_break_18", - "pick_list", - "contact_info", - "shipping_address_name", - "shipping_address", - "dispatch_address_name", - "dispatch_address", - "contact_person", - "contact_display", - "contact_mobile", - "contact_email", - "col_break21", - "customer_address", - "tax_id", - "address_display", - "company_address", - "company_address_display", + "dimension_col_break", + "campaign", + "source", + "custom_dimensions_section", "currency_and_price_list", "currency", "conversion_rate", @@ -56,45 +39,35 @@ "price_list_currency", "plc_conversion_rate", "ignore_pricing_rule", - "sec_warehouse", - "set_warehouse", - "col_break_warehouse", - "set_target_warehouse", "items_section", "scan_barcode", + "pick_list", + "col_break_warehouse", + "set_warehouse", + "set_target_warehouse", + "section_break_30", "items", - "pricing_rule_details", - "pricing_rules", - "packing_list", - "packed_items", - "product_bundle_help", "section_break_31", "total_qty", + "total_net_weight", + "column_break_35", "base_total", "base_net_total", "column_break_33", - "total_net_weight", "total", "net_total", "taxes_section", + "taxes_and_charges", + "column_break_43", "tax_category", "column_break_39", "shipping_rule", "section_break_41", - "taxes_and_charges", "taxes", - "sec_tax_breakup", - "other_charges_calculation", "section_break_44", "base_total_taxes_and_charges", "column_break_47", "total_taxes_and_charges", - "section_break_49", - "apply_discount_on", - "base_discount_amount", - "column_break_51", - "additional_discount_percentage", - "discount_amount", "totals", "base_grand_total", "base_rounding_adjustment", @@ -106,9 +79,50 @@ "rounded_total", "in_words", "disable_rounded_total", - "terms_section_break", + "section_break_49", + "apply_discount_on", + "base_discount_amount", + "column_break_51", + "additional_discount_percentage", + "discount_amount", + "sec_tax_breakup", + "other_charges_calculation", + "packing_list", + "packed_items", + "product_bundle_help", + "pricing_rule_details", + "pricing_rules", + "address_and_contact_tab", + "contact_info", + "customer_address", + "address_display", + "col_break21", + "contact_person", + "contact_display", + "contact_mobile", + "contact_email", + "shipping_address_section", + "shipping_address_name", + "shipping_address", + "column_break_95", + "dispatch_address_name", + "dispatch_address", + "company_address_section", + "company_address", + "column_break_101", + "company_address_display", + "terms_tab", "tc_name", "terms", + "more_info_tab", + "section_break_83", + "per_billed", + "status", + "column_break_112", + "per_installed", + "installation_status", + "column_break_89", + "per_returned", "transporter_info", "transporter", "driver", @@ -118,56 +132,40 @@ "transporter_name", "driver_name", "lr_date", - "more_info", - "campaign", - "source", - "column_break5", - "is_internal_customer", - "represents_company", - "inter_company_reference", - "per_billed", - "customer_group", - "territory", - "printing_details", - "letter_head", - "select_print_heading", - "language", - "column_break_88", - "print_without_amount", - "group_same_items", - "section_break_83", - "status", - "per_installed", - "installation_status", - "column_break_89", - "per_returned", - "excise_page", - "instructions", - "subscription_section", - "auto_repeat", + "customer_po_details", + "po_no", + "column_break_17", + "po_date", "sales_team_section_break", "sales_partner", - "column_break7", "amount_eligible_for_commission", + "column_break7", "commission_rate", "total_commission", "section_break1", - "sales_team" + "sales_team", + "subscription_section", + "auto_repeat", + "printing_details", + "letter_head", + "print_without_amount", + "group_same_items", + "column_break_88", + "select_print_heading", + "language", + "more_info", + "is_internal_customer", + "represents_company", + "inter_company_reference", + "customer_group", + "territory", + "column_break5", + "excise_page", + "instructions", + "connections_tab", + "column_break_25" ], "fields": [ - { - "fieldname": "delivery_to_section", - "fieldtype": "Section Break", - "label": "Delivery To", - "options": "fa fa-user" - }, - { - "fieldname": "column_break0", - "fieldtype": "Column Break", - "oldfieldtype": "Column Break", - "print_width": "50%", - "width": "50%" - }, { "allow_on_submit": 1, "default": "{customer_name}", @@ -337,11 +335,10 @@ "width": "100px" }, { - "collapsible": 1, "depends_on": "customer", "fieldname": "contact_info", "fieldtype": "Section Break", - "label": "Address and Contact", + "label": "Billing Address", "options": "fa fa-bullhorn" }, { @@ -493,10 +490,6 @@ "permlevel": 1, "print_hide": 1 }, - { - "fieldname": "sec_warehouse", - "fieldtype": "Section Break" - }, { "fieldname": "set_warehouse", "fieldtype": "Link", @@ -511,6 +504,8 @@ { "fieldname": "items_section", "fieldtype": "Section Break", + "hide_border": 1, + "label": "Items", "oldfieldtype": "Section Break", "options": "fa fa-shopping-cart" }, @@ -524,7 +519,6 @@ "allow_bulk_edit": 1, "fieldname": "items", "fieldtype": "Table", - "label": "Items", "oldfieldname": "delivery_note_details", "oldfieldtype": "Table", "options": "Delivery Note Item", @@ -619,6 +613,7 @@ "read_only": 1 }, { + "depends_on": "total_net_weight", "fieldname": "total_net_weight", "fieldtype": "Float", "label": "Total Net Weight", @@ -628,6 +623,7 @@ { "fieldname": "taxes_section", "fieldtype": "Section Break", + "hide_border": 1, "label": "Taxes and Charges", "oldfieldtype": "Section Break", "options": "fa fa-money" @@ -653,10 +649,10 @@ }, { "fieldname": "section_break_41", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "hide_border": 1 }, { - "description": "If you have created a standard template in Sales Taxes and Charges Template, select one and click on the button below.", "fieldname": "taxes_and_charges", "fieldtype": "Link", "label": "Sales Taxes and Charges Template", @@ -668,7 +664,6 @@ { "fieldname": "taxes", "fieldtype": "Table", - "label": "Sales Taxes and Charges", "oldfieldname": "other_charges", "oldfieldtype": "Table", "options": "Sales Taxes and Charges" @@ -718,7 +713,6 @@ }, { "collapsible": 1, - "collapsible_depends_on": "discount_amount", "fieldname": "section_break_49", "fieldtype": "Section Break", "label": "Additional Discount" @@ -759,6 +753,7 @@ { "fieldname": "totals", "fieldtype": "Section Break", + "label": "Totals", "oldfieldtype": "Section Break", "options": "fa fa-money" }, @@ -863,15 +858,6 @@ "read_only": 1, "width": "150px" }, - { - "collapsible": 1, - "collapsible_depends_on": "terms", - "fieldname": "terms_section_break", - "fieldtype": "Section Break", - "label": "Terms and Conditions", - "oldfieldtype": "Section Break", - "options": "fa fa-legal" - }, { "fieldname": "tc_name", "fieldtype": "Link", @@ -965,13 +951,12 @@ "collapsible": 1, "fieldname": "more_info", "fieldtype": "Section Break", - "label": "More Information", + "label": "Additional Info", "oldfieldtype": "Section Break", "options": "fa fa-file-text", "print_hide": 1 }, { - "description": "Track this Delivery Note against any Project", "fieldname": "project", "fieldtype": "Link", "label": "Project", @@ -1110,7 +1095,6 @@ }, { "depends_on": "eval:!doc.__islocal", - "description": "% of materials delivered against this Delivery Note", "fieldname": "per_installed", "fieldtype": "Percent", "in_list_view": 1, @@ -1237,10 +1221,6 @@ "options": "Pick List", "read_only": 1 }, - { - "fieldname": "section_break_18", - "fieldtype": "Section Break" - }, { "default": "0", "fetch_from": "customer.is_internal_customer", @@ -1329,13 +1309,85 @@ { "fieldname": "dimension_col_break", "fieldtype": "Column Break" + }, + { + "fieldname": "address_and_contact_tab", + "fieldtype": "Tab Break", + "label": "Address & Contact" + }, + { + "fieldname": "terms_tab", + "fieldtype": "Tab Break", + "label": "Terms" + }, + { + "fieldname": "more_info_tab", + "fieldtype": "Tab Break", + "label": "More Info" + }, + { + "fieldname": "connections_tab", + "fieldtype": "Tab Break", + "label": "Connections", + "show_dashboard": 1 + }, + { + "fieldname": "column_break_10", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_25", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_30", + "fieldtype": "Section Break", + "hide_border": 1 + }, + { + "fieldname": "column_break_35", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_43", + "fieldtype": "Column Break" + }, + { + "fieldname": "custom_dimensions_section", + "fieldtype": "Section Break" + }, + { + "fieldname": "shipping_address_section", + "fieldtype": "Section Break", + "label": "Shipping Address" + }, + { + "fieldname": "column_break_95", + "fieldtype": "Column Break" + }, + { + "fieldname": "company_address_section", + "fieldtype": "Section Break", + "label": "Company Address" + }, + { + "fieldname": "column_break_101", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_112", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_18", + "fieldtype": "Column Break" } ], "icon": "fa fa-truck", "idx": 146, "is_submittable": 1, "links": [], - "modified": "2022-09-16 17:46:17.701904", + "modified": "2022-10-11 13:06:58.655635", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note", diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py index 36d5a6ce0e..9dd28dc60b 100644 --- a/erpnext/stock/doctype/delivery_note/delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/delivery_note.py @@ -842,6 +842,9 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None): update_address( target_doc, "shipping_address", "shipping_address_display", source_doc.customer_address ) + update_address( + target_doc, "billing_address", "billing_address_display", source_doc.customer_address + ) update_taxes( target_doc, diff --git a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json index 0911cdb476..0a5cbabab0 100644 --- a/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json +++ b/erpnext/stock/doctype/delivery_note_item/delivery_note_item.json @@ -753,6 +753,7 @@ "fieldtype": "Currency", "label": "Incoming Rate", "no_copy": 1, + "precision": "6", "print_hide": 1, "read_only": 1 }, @@ -813,7 +814,7 @@ "index_web_pages_for_search": 1, "istable": 1, "links": [], - "modified": "2022-09-06 14:19:42.876357", + "modified": "2022-10-12 03:36:05.344847", "modified_by": "Administrator", "module": "Stock", "name": "Delivery Note Item", diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index c8bb1b960e..20bc9d9b2c 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -232,10 +232,10 @@ class Item(Document): def clear_retain_sample(self): if not self.has_batch_no: - self.retain_sample = None + self.retain_sample = False if not self.retain_sample: - self.sample_quantity = None + self.sample_quantity = 0 def add_default_uom_in_conversion_factor_table(self): if not self.is_new() and self.has_value_changed("stock_uom"): diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py index e35c8bf335..e1ee9389de 100644 --- a/erpnext/stock/doctype/item/test_item.py +++ b/erpnext/stock/doctype/item/test_item.py @@ -32,7 +32,7 @@ test_ignore = ["BOM"] test_dependencies = ["Warehouse", "Item Group", "Item Tax Template", "Brand", "Item Attribute"] -def make_item(item_code=None, properties=None): +def make_item(item_code=None, properties=None, uoms=None): if not item_code: item_code = frappe.generate_hash(length=16) @@ -56,6 +56,11 @@ def make_item(item_code=None, properties=None): for item_default in [doc for doc in item.get("item_defaults") if not doc.default_warehouse]: item_default.default_warehouse = "_Test Warehouse - _TC" item_default.company = "_Test Company" + + if uoms: + for uom in uoms: + item.append("uoms", uom) + item.insert() return item @@ -717,8 +722,8 @@ class TestItem(FrappeTestCase): item.has_batch_no = None item.save() - self.assertEqual(item.retain_sample, None) - self.assertEqual(item.sample_quantity, None) + self.assertEqual(item.retain_sample, False) + self.assertEqual(item.sample_quantity, 0) item.delete() def consume_item_code_with_differet_stock_transactions( diff --git a/erpnext/stock/doctype/material_request/material_request.json b/erpnext/stock/doctype/material_request/material_request.json index 35931307af..413c373e0e 100644 --- a/erpnext/stock/doctype/material_request/material_request.json +++ b/erpnext/stock/doctype/material_request/material_request.json @@ -11,34 +11,40 @@ "naming_series", "title", "material_request_type", - "transfer_status", "customer", - "status", + "company", "column_break_2", "transaction_date", "schedule_date", - "company", "amended_from", "warehouse_section", + "scan_barcode", + "column_break_13", "set_from_warehouse", "column_break5", "set_warehouse", "items_section", - "scan_barcode", "items", - "more_info", - "per_ordered", - "column_break2", - "per_received", - "printing_details", - "letter_head", - "select_print_heading", + "terms_tab", "terms_section_break", "tc_name", "terms", + "more_info_tab", + "status_section", + "status", + "per_ordered", + "column_break2", + "transfer_status", + "per_received", + "printing_details", + "letter_head", + "column_break_31", + "select_print_heading", "reference", "job_card", - "work_order" + "column_break_35", + "work_order", + "connections_tab" ], "fields": [ { @@ -147,14 +153,6 @@ "options": "Material Request Item", "reqd": 1 }, - { - "collapsible": 1, - "fieldname": "more_info", - "fieldtype": "Section Break", - "label": "More Information", - "oldfieldtype": "Section Break", - "options": "fa fa-file-text" - }, { "default": "Today", "fieldname": "transaction_date", @@ -239,7 +237,6 @@ "collapsible_depends_on": "terms", "fieldname": "terms_section_break", "fieldtype": "Section Break", - "label": "Terms and Conditions", "oldfieldtype": "Section Break", "options": "fa fa-legal" }, @@ -277,10 +274,10 @@ { "fieldname": "warehouse_section", "fieldtype": "Section Break", - "label": "Warehouse" + "hide_border": 1, + "label": "Items" }, { - "description": "Sets 'Target Warehouse' in each row of the Items table.", "fieldname": "set_warehouse", "fieldtype": "Link", "in_list_view": 1, @@ -296,7 +293,6 @@ }, { "depends_on": "eval:doc.material_request_type == 'Material Transfer'", - "description": "Sets 'Source Warehouse' in each row of the Items table.", "fieldname": "set_from_warehouse", "fieldtype": "Link", "label": "Set Source Warehouse", @@ -317,13 +313,49 @@ "label": "Work Order", "options": "Work Order", "read_only": 1 + }, + { + "fieldname": "terms_tab", + "fieldtype": "Tab Break", + "label": "Terms" + }, + { + "fieldname": "more_info_tab", + "fieldtype": "Tab Break", + "label": "More Info" + }, + { + "fieldname": "connections_tab", + "fieldtype": "Tab Break", + "label": "Connections", + "show_dashboard": 1 + }, + { + "collapsible": 1, + "fieldname": "status_section", + "fieldtype": "Section Break", + "label": "Status", + "oldfieldtype": "Section Break", + "options": "fa fa-file-text" + }, + { + "fieldname": "column_break_31", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_35", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_13", + "fieldtype": "Column Break" } ], "icon": "fa fa-ticket", "idx": 70, "is_submittable": 1, "links": [], - "modified": "2022-08-25 11:49:28.155048", + "modified": "2022-09-27 17:58:26.366469", "modified_by": "Administrator", "module": "Stock", "name": "Material Request", diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index acaac920c9..3141212ee5 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -17,10 +17,11 @@ "supplier_name", "supplier_delivery_note", "column_break1", - "company", "posting_date", "posting_time", "set_posting_time", + "column_break_12", + "company", "apply_putaway_rule", "is_return", "return_against", @@ -28,18 +29,6 @@ "cost_center", "dimension_col_break", "project", - "section_addresses", - "supplier_address", - "contact_person", - "address_display", - "contact_display", - "contact_mobile", - "contact_email", - "col_break_address", - "shipping_address", - "shipping_address_display", - "billing_address", - "billing_address_display", "currency_and_price_list", "currency", "conversion_rate", @@ -49,37 +38,33 @@ "plc_conversion_rate", "ignore_pricing_rule", "sec_warehouse", + "scan_barcode", + "column_break_31", "set_warehouse", - "rejected_warehouse", - "col_break_warehouse", "set_from_warehouse", + "col_break_warehouse", + "rejected_warehouse", "is_subcontracted", "supplier_warehouse", "items_section", - "scan_barcode", "items", "section_break0", "total_qty", + "total_net_weight", + "column_break_43", "base_total", "base_net_total", "column_break_27", - "total_net_weight", "total", "net_total", - "pricing_rule_details", - "pricing_rules", - "raw_material_details", - "get_current_stock", - "supplied_items", "taxes_charges_section", - "tax_category", + "taxes_and_charges", "shipping_col", + "tax_category", + "column_break_53", "shipping_rule", "taxes_section", - "taxes_and_charges", "taxes", - "sec_tax_breakup", - "other_charges_calculation", "totals", "base_taxes_and_charges_added", "base_taxes_and_charges_deducted", @@ -88,53 +73,81 @@ "taxes_and_charges_added", "taxes_and_charges_deducted", "total_taxes_and_charges", - "section_break_42", - "apply_discount_on", - "base_discount_amount", - "column_break_44", - "additional_discount_percentage", - "discount_amount", "section_break_46", "base_grand_total", "base_rounding_adjustment", - "base_in_words", "base_rounded_total", + "base_in_words", "column_break_50", "grand_total", "rounding_adjustment", "rounded_total", "in_words", "disable_rounded_total", - "terms_section_break", + "section_break_42", + "apply_discount_on", + "base_discount_amount", + "column_break_44", + "additional_discount_percentage", + "discount_amount", + "sec_tax_breakup", + "other_charges_calculation", + "pricing_rule_details", + "pricing_rules", + "raw_material_details", + "get_current_stock", + "supplied_items", + "address_and_contact_tab", + "section_addresses", + "supplier_address", + "address_display", + "col_break_address", + "contact_person", + "contact_display", + "contact_mobile", + "contact_email", + "section_break_98", + "shipping_address", + "column_break_100", + "shipping_address_display", + "billing_address_section", + "billing_address", + "column_break_104", + "billing_address_display", + "terms_tab", "tc_name", "terms", - "more_info", + "more_info_tab", + "status_section", "status", - "amended_from", - "range", "column_break4", "per_billed", "per_returned", - "is_internal_supplier", - "inter_company_reference", - "represents_company", "subscription_detail", "auto_repeat", "printing_settings", "letter_head", - "language", - "instructions", + "group_same_items", "column_break_97", "select_print_heading", - "other_details", - "remarks", - "group_same_items", + "language", "transporter_info", "transporter_name", "column_break5", "lr_no", "lr_date", - "is_old_subcontracting_flow" + "additional_info_section", + "instructions", + "is_internal_supplier", + "represents_company", + "inter_company_reference", + "column_break_131", + "remarks", + "range", + "amended_from", + "is_old_subcontracting_flow", + "other_details", + "connections_tab" ], "fields": [ { @@ -223,7 +236,6 @@ "width": "100px" }, { - "description": "Time at which materials were received", "fieldname": "posting_time", "fieldtype": "Time", "label": "Posting Time", @@ -277,15 +289,14 @@ "read_only": 1 }, { - "collapsible": 1, "fieldname": "section_addresses", "fieldtype": "Section Break", - "label": "Address and Contact" + "label": "Supplier Address" }, { "fieldname": "supplier_address", "fieldtype": "Link", - "label": "Select Supplier Address", + "label": "Supplier Address", "options": "Address", "print_hide": 1 }, @@ -330,7 +341,7 @@ { "fieldname": "shipping_address", "fieldtype": "Link", - "label": "Select Shipping Address", + "label": "Shipping Address Template", "options": "Address", "print_hide": 1 }, @@ -410,10 +421,11 @@ }, { "fieldname": "sec_warehouse", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "hide_border": 1, + "label": "Items" }, { - "description": "Sets 'Accepted Warehouse' in each row of the items table.", "fieldname": "set_warehouse", "fieldtype": "Link", "label": "Accepted Warehouse", @@ -421,7 +433,6 @@ "print_hide": 1 }, { - "description": "Sets 'Rejected Warehouse' in each row of the items table.", "fieldname": "rejected_warehouse", "fieldtype": "Link", "label": "Rejected Warehouse", @@ -461,6 +472,7 @@ { "fieldname": "items_section", "fieldtype": "Section Break", + "hide_border": 1, "oldfieldtype": "Section Break", "options": "fa fa-shopping-cart" }, @@ -571,6 +583,7 @@ "read_only": 1 }, { + "depends_on": "total_net_weight", "fieldname": "total_net_weight", "fieldtype": "Float", "label": "Total Net Weight", @@ -578,11 +591,11 @@ "read_only": 1 }, { - "description": "Add / Edit Taxes and Charges", "fieldname": "taxes_charges_section", "fieldtype": "Section Break", - "oldfieldtype": "Section Break", - "options": "fa fa-money" + "hide_border": 1, + "label": "Taxes and Charges", + "oldfieldtype": "Section Break" }, { "fieldname": "tax_category", @@ -603,7 +616,8 @@ }, { "fieldname": "taxes_section", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "hide_border": 1 }, { "fieldname": "taxes_and_charges", @@ -709,7 +723,6 @@ }, { "collapsible": 1, - "collapsible_depends_on": "discount_amount", "fieldname": "section_break_42", "fieldtype": "Section Break", "label": "Additional Discount" @@ -749,7 +762,8 @@ }, { "fieldname": "section_break_46", - "fieldtype": "Section Break" + "fieldtype": "Section Break", + "label": "Totals" }, { "fieldname": "base_grand_total", @@ -841,15 +855,6 @@ "fieldtype": "Check", "label": "Disable Rounded Total" }, - { - "collapsible": 1, - "collapsible_depends_on": "terms", - "fieldname": "terms_section_break", - "fieldtype": "Section Break", - "label": "Terms and Conditions", - "oldfieldtype": "Section Break", - "options": "fa fa-legal" - }, { "fieldname": "tc_name", "fieldtype": "Link", @@ -866,14 +871,6 @@ "oldfieldname": "terms", "oldfieldtype": "Text Editor" }, - { - "collapsible": 1, - "fieldname": "more_info", - "fieldtype": "Section Break", - "label": "More Information", - "oldfieldtype": "Section Break", - "options": "fa fa-file-text" - }, { "default": "Draft", "fieldname": "status", @@ -924,7 +921,6 @@ "width": "50%" }, { - "description": "Track this Purchase Receipt against any Project", "fieldname": "project", "fieldtype": "Link", "label": "Project", @@ -941,7 +937,7 @@ { "fieldname": "subscription_detail", "fieldtype": "Section Break", - "label": "Auto Repeat Detail" + "label": "Auto Repeat" }, { "fieldname": "auto_repeat", @@ -1025,7 +1021,7 @@ "collapsible_depends_on": "transporter_name", "fieldname": "transporter_info", "fieldtype": "Section Break", - "label": "Transporter Details", + "label": "Transporter", "options": "fa fa-truck" }, { @@ -1087,7 +1083,7 @@ { "fieldname": "billing_address", "fieldtype": "Link", - "label": "Select Billing Address", + "label": "Billing Address", "options": "Address" }, { @@ -1113,7 +1109,6 @@ }, { "depends_on": "eval: doc.is_internal_supplier", - "description": "Sets 'From Warehouse' in each row of the items table.", "fieldname": "set_from_warehouse", "fieldtype": "Link", "label": "Set From Warehouse", @@ -1151,13 +1146,85 @@ "hidden": 1, "label": "Is Old Subcontracting Flow", "read_only": 1 + }, + { + "fieldname": "address_and_contact_tab", + "fieldtype": "Tab Break", + "label": "Address & Contact" + }, + { + "fieldname": "terms_tab", + "fieldtype": "Tab Break", + "label": "Terms" + }, + { + "fieldname": "more_info_tab", + "fieldtype": "Tab Break", + "label": "More Info" + }, + { + "fieldname": "connections_tab", + "fieldtype": "Tab Break", + "label": "Connections", + "show_dashboard": 1 + }, + { + "fieldname": "column_break_12", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_31", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_43", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_53", + "fieldtype": "Column Break" + }, + { + "fieldname": "section_break_98", + "fieldtype": "Section Break", + "label": "Company Shipping Address" + }, + { + "fieldname": "billing_address_section", + "fieldtype": "Section Break", + "label": "Company Billing Address" + }, + { + "collapsible": 1, + "fieldname": "status_section", + "fieldtype": "Section Break", + "label": "Status", + "oldfieldtype": "Section Break", + "options": "fa fa-file-text" + }, + { + "fieldname": "additional_info_section", + "fieldtype": "Section Break", + "label": "Additional Info" + }, + { + "fieldname": "column_break_131", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_100", + "fieldtype": "Column Break" + }, + { + "fieldname": "column_break_104", + "fieldtype": "Column Break" } ], "icon": "fa fa-truck", "idx": 261, "is_submittable": 1, "links": [], - "modified": "2022-09-16 17:45:58.430132", + "modified": "2022-10-11 13:02:31.776256", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt", diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 62697244ba..dc9f2b2117 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -1,7 +1,6 @@ # Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors # License: GNU General Public License v3. See license.txt - import frappe from frappe.tests.utils import FrappeTestCase, change_settings from frappe.utils import add_days, cint, cstr, flt, today @@ -1199,6 +1198,8 @@ class TestPurchaseReceipt(FrappeTestCase): self.assertEqual(pr1.items[0].rate, 100) pr1.submit() + self.assertEqual(pr1.is_internal_supplier, 1) + # Backdated purchase receipt entry, the valuation rate should be updated for DN1 and PR1 make_purchase_receipt( item_code=item_doc.name, @@ -1241,6 +1242,234 @@ class TestPurchaseReceipt(FrappeTestCase): self.assertEqual(query[0].value, 0) + def test_backdated_transaction_for_internal_transfer_in_trasit_warehouse_for_purchase_receipt( + self, + ): + from erpnext.stock.doctype.delivery_note.delivery_note import make_inter_company_purchase_receipt + from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note + + prepare_data_for_internal_transfer() + customer = "_Test Internal Customer 2" + company = "_Test Company with perpetual inventory" + + from_warehouse = create_warehouse("_Test Internal From Warehouse New", company=company) + to_warehouse = create_warehouse("_Test Internal To Warehouse New", company=company) + item_doc = create_item("Test Internal Transfer Item") + + target_warehouse = create_warehouse("_Test Internal GIT Warehouse New", company=company) + + make_purchase_receipt( + item_code=item_doc.name, + company=company, + posting_date=add_days(today(), -1), + warehouse=from_warehouse, + qty=1, + rate=100, + ) + + # Keep stock in advance and make sure that systen won't pick this stock while reposting backdated transaction + for i in range(1, 4): + make_purchase_receipt( + item_code=item_doc.name, + company=company, + posting_date=add_days(today(), -1 * i), + warehouse=target_warehouse, + qty=1, + rate=320 * i, + ) + + dn1 = create_delivery_note( + item_code=item_doc.name, + company=company, + customer=customer, + cost_center="Main - TCP1", + expense_account="Cost of Goods Sold - TCP1", + qty=1, + rate=500, + warehouse=from_warehouse, + target_warehouse=target_warehouse, + ) + + self.assertEqual(dn1.items[0].rate, 100) + + pr1 = make_inter_company_purchase_receipt(dn1.name) + pr1.items[0].warehouse = to_warehouse + self.assertEqual(pr1.items[0].rate, 100) + pr1.submit() + + stk_ledger = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_type": "Purchase Receipt", "voucher_no": pr1.name, "warehouse": target_warehouse}, + ["stock_value_difference", "outgoing_rate"], + as_dict=True, + ) + + self.assertEqual(abs(stk_ledger.stock_value_difference), 100) + self.assertEqual(stk_ledger.outgoing_rate, 100) + + # Backdated purchase receipt entry, the valuation rate should be updated for DN1 and PR1 + make_purchase_receipt( + item_code=item_doc.name, + company=company, + posting_date=add_days(today(), -2), + warehouse=from_warehouse, + qty=1, + rate=200, + ) + + dn_value = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_type": "Delivery Note", "voucher_no": dn1.name, "warehouse": target_warehouse}, + "stock_value_difference", + ) + + self.assertEqual(abs(dn_value), 200.00) + + pr_value = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_type": "Purchase Receipt", "voucher_no": pr1.name, "warehouse": to_warehouse}, + "stock_value_difference", + ) + + self.assertEqual(abs(pr_value), 200.00) + pr1.load_from_db() + + self.assertEqual(pr1.items[0].valuation_rate, 200) + self.assertEqual(pr1.items[0].rate, 100) + + Gl = frappe.qb.DocType("GL Entry") + + query = ( + frappe.qb.from_(Gl) + .select( + (fn.Sum(Gl.debit) - fn.Sum(Gl.credit)).as_("value"), + ) + .where((Gl.voucher_type == pr1.doctype) & (Gl.voucher_no == pr1.name)) + ).run(as_dict=True) + + self.assertEqual(query[0].value, 0) + + def test_backdated_transaction_for_internal_transfer_in_trasit_warehouse_for_purchase_invoice( + self, + ): + from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import ( + make_purchase_invoice as make_purchase_invoice_for_si, + ) + from erpnext.accounts.doctype.sales_invoice.sales_invoice import ( + make_inter_company_purchase_invoice, + ) + from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice + + prepare_data_for_internal_transfer() + customer = "_Test Internal Customer 2" + company = "_Test Company with perpetual inventory" + + from_warehouse = create_warehouse("_Test Internal From Warehouse New", company=company) + to_warehouse = create_warehouse("_Test Internal To Warehouse New", company=company) + item_doc = create_item("Test Internal Transfer Item") + + target_warehouse = create_warehouse("_Test Internal GIT Warehouse New", company=company) + + make_purchase_invoice_for_si( + item_code=item_doc.name, + company=company, + posting_date=add_days(today(), -1), + warehouse=from_warehouse, + qty=1, + update_stock=1, + expense_account="Cost of Goods Sold - TCP1", + cost_center="Main - TCP1", + rate=100, + ) + + # Keep stock in advance and make sure that systen won't pick this stock while reposting backdated transaction + for i in range(1, 4): + make_purchase_invoice_for_si( + item_code=item_doc.name, + company=company, + posting_date=add_days(today(), -1 * i), + warehouse=target_warehouse, + update_stock=1, + qty=1, + expense_account="Cost of Goods Sold - TCP1", + cost_center="Main - TCP1", + rate=320 * i, + ) + + si1 = create_sales_invoice( + item_code=item_doc.name, + company=company, + customer=customer, + cost_center="Main - TCP1", + income_account="Sales - TCP1", + qty=1, + rate=500, + update_stock=1, + warehouse=from_warehouse, + target_warehouse=target_warehouse, + ) + + self.assertEqual(si1.items[0].rate, 100) + + pi1 = make_inter_company_purchase_invoice(si1.name) + pi1.items[0].warehouse = to_warehouse + self.assertEqual(pi1.items[0].rate, 100) + pi1.update_stock = 1 + pi1.save() + pi1.submit() + + stk_ledger = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_type": pi1.doctype, "voucher_no": pi1.name, "warehouse": target_warehouse}, + ["stock_value_difference", "outgoing_rate"], + as_dict=True, + ) + + self.assertEqual(abs(stk_ledger.stock_value_difference), 100) + self.assertEqual(stk_ledger.outgoing_rate, 100) + + # Backdated purchase receipt entry, the valuation rate should be updated for si1 and pi1 + make_purchase_receipt( + item_code=item_doc.name, + company=company, + posting_date=add_days(today(), -2), + warehouse=from_warehouse, + qty=1, + rate=200, + ) + + si_value = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_type": si1.doctype, "voucher_no": si1.name, "warehouse": target_warehouse}, + "stock_value_difference", + ) + + self.assertEqual(abs(si_value), 200.00) + + pi_value = frappe.db.get_value( + "Stock Ledger Entry", + {"voucher_type": pi1.doctype, "voucher_no": pi1.name, "warehouse": to_warehouse}, + "stock_value_difference", + ) + + self.assertEqual(abs(pi_value), 200.00) + pi1.load_from_db() + + self.assertEqual(pi1.items[0].valuation_rate, 200) + self.assertEqual(pi1.items[0].rate, 100) + + Gl = frappe.qb.DocType("GL Entry") + + query = ( + frappe.qb.from_(Gl) + .select( + (fn.Sum(Gl.debit) - fn.Sum(Gl.credit)).as_("value"), + ) + .where((Gl.voucher_type == pi1.doctype) & (Gl.voucher_no == pi1.name)) + ).run(as_dict=True) + + 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 diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json index 39833b5e91..772736e0ad 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -744,6 +744,7 @@ "oldfieldname": "valuation_rate", "oldfieldtype": "Currency", "options": "Company:company:default_currency", + "precision": "6", "print_hide": 1, "print_width": "80px", "read_only": 1, @@ -999,7 +1000,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2022-07-28 19:27:54.880781", + "modified": "2022-10-12 03:37:59.516609", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", diff --git a/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py index edd2553d5d..e0f2479710 100644 --- a/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py +++ b/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py @@ -9,6 +9,7 @@ from frappe.tests.utils import FrappeTestCase from frappe.utils import nowdate from frappe.utils.data import add_to_date, today +from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice from erpnext.accounts.utils import repost_gle_for_stock_vouchers from erpnext.controllers.stock_controller import create_item_wise_repost_entries from erpnext.stock.doctype.item.test_item import make_item @@ -272,3 +273,57 @@ class TestRepostItemValuation(FrappeTestCase, StockTestMixin): [{"credit": 50, "debit": 0}], gle_filters={"account": "Stock In Hand - TCP1"}, ) + + def test_duplicate_ple_on_repost(self): + from erpnext.accounts import utils + + # lower numbers to simplify test + orig_chunk_size = utils.GL_REPOSTING_CHUNK + utils.GL_REPOSTING_CHUNK = 2 + self.addCleanup(setattr, utils, "GL_REPOSTING_CHUNK", orig_chunk_size) + + rate = 100 + item = self.make_item() + item.valuation_rate = 90 + item.allow_negative_stock = 1 + item.save() + + company = "_Test Company with perpetual inventory" + + # consume non-existing stock + sinv = create_sales_invoice( + company=company, + posting_date=today(), + debit_to="Debtors - TCP1", + income_account="Sales - TCP1", + expense_account="Cost of Goods Sold - TCP1", + warehouse="Stores - TCP1", + update_stock=1, + currency="INR", + item_code=item.name, + cost_center="Main - TCP1", + qty=1, + rate=rate, + ) + + # backdated receipt triggers repost + make_stock_entry( + item=item.name, + company=company, + qty=5, + rate=rate, + target="Stores - TCP1", + posting_date=add_to_date(today(), days=-1), + ) + + ple_entries = frappe.db.get_list( + "Payment Ledger Entry", + filters={"voucher_type": sinv.doctype, "voucher_no": sinv.name, "delinked": 0}, + ) + + # assert successful deduplication on PLE + self.assertEqual(len(ple_entries), 1) + + # outstanding should not be affected + sinv.reload() + self.assertEqual(sinv.outstanding_amount, 100) diff --git a/erpnext/stock/doctype/serial_no/serial_no.py b/erpnext/stock/doctype/serial_no/serial_no.py index 6042ed4ac5..a2748d0d09 100644 --- a/erpnext/stock/doctype/serial_no/serial_no.py +++ b/erpnext/stock/doctype/serial_no/serial_no.py @@ -846,16 +846,15 @@ def get_pos_reserved_serial_nos(filters): pos_transacted_sr_nos = query.run(as_dict=True) - reserved_sr_nos = [] - returned_sr_nos = [] + reserved_sr_nos = set() + returned_sr_nos = set() for d in pos_transacted_sr_nos: if d.is_return == 0: - reserved_sr_nos += get_serial_nos(d.serial_no) + [reserved_sr_nos.add(x) for x in get_serial_nos(d.serial_no)] elif d.is_return == 1: - returned_sr_nos += get_serial_nos(d.serial_no) + [returned_sr_nos.add(x) for x in get_serial_nos(d.serial_no)] - for sr_no in returned_sr_nos: - reserved_sr_nos.remove(sr_no) + reserved_sr_nos = list(reserved_sr_nos - returned_sr_nos) return reserved_sr_nos diff --git a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py index 1410da56a3..6c341d9e9e 100644 --- a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py +++ b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py @@ -1323,13 +1323,15 @@ def create_product_bundle_item(new_item_code, packed_items): item.save() -def create_items(): - items = [ - "_Test Item for Reposting", - "_Test Finished Item for Reposting", - "_Test Subcontracted Item for Reposting", - "_Test Bundled Item for Reposting", - ] +def create_items(items=None, uoms=None): + if not items: + items = [ + "_Test Item for Reposting", + "_Test Finished Item for Reposting", + "_Test Subcontracted Item for Reposting", + "_Test Bundled Item for Reposting", + ] + for d in items: properties = {"valuation_method": "FIFO"} if d == "_Test Bundled Item for Reposting": @@ -1337,7 +1339,7 @@ def create_items(): elif d == "_Test Subcontracted Item for Reposting": properties.update({"is_sub_contracted_item": 1}) - make_item(d, properties=properties) + make_item(d, properties=properties, uoms=uoms) return items diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py index 23e0f1efaf..d92d0f1686 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py @@ -132,7 +132,9 @@ class StockReconciliation(StockController): key.append(row.get(field)) if key in item_warehouse_combinations: - self.validation_messages.append(_get_msg(row_num, _("Duplicate entry"))) + self.validation_messages.append( + _get_msg(row_num, _("Same item and warehouse combination already entered.")) + ) else: item_warehouse_combinations.append(key) diff --git a/erpnext/stock/doctype/warehouse/warehouse.py b/erpnext/stock/doctype/warehouse/warehouse.py index ab784ca107..6e06d23617 100644 --- a/erpnext/stock/doctype/warehouse/warehouse.py +++ b/erpnext/stock/doctype/warehouse/warehouse.py @@ -9,6 +9,7 @@ from frappe import _, throw from frappe.contacts.address_and_contact import load_address_and_contact from frappe.utils import cint, flt from frappe.utils.nestedset import NestedSet +from pypika.terms import ExistsCriterion from erpnext.stock import get_warehouse_account @@ -266,3 +267,23 @@ def get_warehouses_based_on_account(account, company=None): frappe.throw(_("Warehouse not found against the account {0}").format(account)) return warehouses + + +# Will be use for frappe.qb +def apply_warehouse_filter(query, sle, filters): + if warehouse := filters.get("warehouse"): + warehouse_table = frappe.qb.DocType("Warehouse") + + lft, rgt = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt"]) + chilren_subquery = ( + frappe.qb.from_(warehouse_table) + .select(warehouse_table.name) + .where( + (warehouse_table.lft >= lft) + & (warehouse_table.rgt <= rgt) + & (warehouse_table.name == sle.warehouse) + ) + ) + query = query.where(ExistsCriterion(chilren_subquery)) + + return query diff --git a/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py b/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py index 3d9b046197..ef7d6e6816 100644 --- a/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py +++ b/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py @@ -4,6 +4,7 @@ import frappe from frappe import _ +from frappe.query_builder.functions import IfNull from frappe.utils import cint, getdate @@ -54,31 +55,28 @@ def get_columns(filters): return columns -def get_conditions(filters): - conditions = "" +def get_stock_ledger_entries(filters): if not filters.get("from_date"): frappe.throw(_("'From Date' is required")) - if filters.get("to_date"): - conditions += " and posting_date <= '%s'" % filters["to_date"] - else: + if not filters.get("to_date"): frappe.throw(_("'To Date' is required")) - return conditions - - -def get_stock_ledger_entries(filters): - conditions = get_conditions(filters) - return frappe.db.sql( - """select item_code, batch_no, warehouse, - posting_date, actual_qty - from `tabStock Ledger Entry` - where is_cancelled = 0 - and docstatus < 2 and ifnull(batch_no, '') != '' %s order by item_code, warehouse""" - % conditions, - as_dict=1, + sle = frappe.qb.DocType("Stock Ledger Entry") + query = ( + frappe.qb.from_(sle) + .select(sle.item_code, sle.batch_no, sle.warehouse, sle.posting_date, sle.actual_qty) + .where( + (sle.is_cancelled == 0) + & (sle.docstatus < 2) + & (IfNull(sle.batch_no, "") != "") + & (sle.posting_date <= filters["to_date"]) + ) + .orderby(sle.item_code, sle.warehouse) ) + return query.run(as_dict=True) + def get_item_warehouse_batch_map(filters, float_precision): sle = get_stock_ledger_entries(filters) @@ -112,7 +110,7 @@ def get_item_warehouse_batch_map(filters, float_precision): def get_item_details(filters): item_map = {} - for d in frappe.db.sql("select name, item_name, description from tabItem", as_dict=1): + for d in (frappe.qb.from_("Item").select("name", "item_name", "description")).run(as_dict=True): item_map.setdefault(d.name, d) return item_map diff --git a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py index 8a13300dc8..0d57938e31 100644 --- a/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py +++ b/erpnext/stock/report/batch_wise_balance_history/batch_wise_balance_history.py @@ -5,6 +5,9 @@ import frappe from frappe import _ from frappe.utils import cint, flt, getdate +from pypika import functions as fn + +from erpnext.stock.doctype.warehouse.warehouse import apply_warehouse_filter def execute(filters=None): @@ -64,37 +67,40 @@ def get_columns(filters): return columns -def get_conditions(filters): - conditions = "" - if not filters.get("from_date"): - frappe.throw(_("'From Date' is required")) - - if filters.get("to_date"): - conditions += " and posting_date <= '%s'" % filters["to_date"] - else: - frappe.throw(_("'To Date' is required")) - - for field in ["item_code", "warehouse", "batch_no", "company"]: - if filters.get(field): - conditions += " and {0} = {1}".format(field, frappe.db.escape(filters.get(field))) - - return conditions - - # get all details def get_stock_ledger_entries(filters): - conditions = get_conditions(filters) - return frappe.db.sql( - """ - select item_code, batch_no, warehouse, posting_date, sum(actual_qty) as actual_qty - from `tabStock Ledger Entry` - where is_cancelled = 0 and docstatus < 2 and ifnull(batch_no, '') != '' %s - group by voucher_no, batch_no, item_code, warehouse - order by item_code, warehouse""" - % conditions, - as_dict=1, + if not filters.get("from_date"): + frappe.throw(_("'From Date' is required")) + if not filters.get("to_date"): + frappe.throw(_("'To Date' is required")) + + sle = frappe.qb.DocType("Stock Ledger Entry") + query = ( + frappe.qb.from_(sle) + .select( + sle.item_code, + sle.warehouse, + sle.batch_no, + sle.posting_date, + fn.Sum(sle.actual_qty).as_("actual_qty"), + ) + .where( + (sle.docstatus < 2) + & (sle.is_cancelled == 0) + & (fn.IfNull(sle.batch_no, "") != "") + & (sle.posting_date <= filters["to_date"]) + ) + .groupby(sle.voucher_no, sle.batch_no, sle.item_code, sle.warehouse) + .orderby(sle.item_code, sle.warehouse) ) + query = apply_warehouse_filter(query, sle, filters) + for field in ["item_code", "batch_no", "company"]: + if filters.get(field): + query = query.where(sle[field] == filters.get(field)) + + return query.run(as_dict=True) + def get_item_warehouse_batch_map(filters, float_precision): sle = get_stock_ledger_entries(filters) @@ -127,7 +133,9 @@ def get_item_warehouse_batch_map(filters, float_precision): def get_item_details(filters): item_map = {} - for d in frappe.db.sql("select name, item_name, description, stock_uom from tabItem", as_dict=1): + for d in (frappe.qb.from_("Item").select("name", "item_name", "description", "stock_uom")).run( + as_dict=1 + ): item_map.setdefault(d.name, d) return item_map diff --git a/erpnext/stock/report/delayed_item_report/delayed_item_report.py b/erpnext/stock/report/delayed_item_report/delayed_item_report.py index 9df24d6559..546a834da8 100644 --- a/erpnext/stock/report/delayed_item_report/delayed_item_report.py +++ b/erpnext/stock/report/delayed_item_report/delayed_item_report.py @@ -21,56 +21,54 @@ class DelayedItemReport(object): return self.get_columns(), self.get_data() or [] def get_data(self, consolidated=False): - conditions = "" - doctype = self.filters.get("based_on") - child_doc = "%s Item" % doctype + sales_order_field = "sales_order" if doctype == "Sales Invoice" else "against_sales_order" + + parent = frappe.qb.DocType(doctype) + child = frappe.qb.DocType(f"{doctype} Item") + + query = ( + frappe.qb.from_(child) + .from_(parent) + .select( + child.item_code, + child.item_name, + child.item_group, + child.qty, + child.rate, + child.amount, + child.so_detail, + child[sales_order_field].as_("sales_order"), + parent.shipping_address_name, + parent.po_no, + parent.customer, + parent.posting_date, + parent.name, + parent.grand_total, + ) + .where( + (child.parent == parent.name) + & (parent.docstatus == 1) + & (parent.posting_date.between(self.filters.get("from_date"), self.filters.get("to_date"))) + & (child[sales_order_field].notnull()) + & (child[sales_order_field] != "") + ) + ) if doctype == "Sales Invoice": - conditions = " and `tabSales Invoice`.update_stock = 1 and `tabSales Invoice`.is_pos = 0" + query = query.where((parent.update_stock == 1) & (parent.is_pos == 0)) if self.filters.get("item_group"): - conditions += " and `tab%s`.item_group = %s" % ( - child_doc, - frappe.db.escape(self.filters.get("item_group")), - ) - - for field in ["customer", "customer_group", "company"]: - if self.filters.get(field): - conditions += " and `tab%s`.%s = %s" % ( - doctype, - field, - frappe.db.escape(self.filters.get(field)), - ) - - sales_order_field = "against_sales_order" - if doctype == "Sales Invoice": - sales_order_field = "sales_order" + query = query.where(child.item_group == self.filters.get("item_group")) if self.filters.get("sales_order"): - conditions = " and `tab%s`.%s = '%s'" % ( - child_doc, - sales_order_field, - self.filters.get("sales_order"), - ) + query = query.where(child[sales_order_field] == self.filters.get("sales_order")) - self.transactions = frappe.db.sql( - """ SELECT `tab{child_doc}`.item_code, `tab{child_doc}`.item_name, - `tab{child_doc}`.item_group, `tab{child_doc}`.qty, `tab{child_doc}`.rate, `tab{child_doc}`.amount, - `tab{child_doc}`.so_detail, `tab{child_doc}`.{so_field} as sales_order, - `tab{doctype}`.shipping_address_name, `tab{doctype}`.po_no, `tab{doctype}`.customer, - `tab{doctype}`.posting_date, `tab{doctype}`.name, `tab{doctype}`.grand_total - FROM `tab{child_doc}`, `tab{doctype}` - WHERE - `tab{child_doc}`.parent = `tab{doctype}`.name and `tab{doctype}`.docstatus = 1 and - `tab{doctype}`.posting_date between %(from_date)s and %(to_date)s and - `tab{child_doc}`.{so_field} is not null and `tab{child_doc}`.{so_field} != '' {cond} - """.format( - cond=conditions, doctype=doctype, child_doc=child_doc, so_field=sales_order_field - ), - {"from_date": self.filters.get("from_date"), "to_date": self.filters.get("to_date")}, - as_dict=1, - ) + for field in ("customer", "customer_group", "company"): + if self.filters.get(field): + query = query.where(parent[field] == self.filters.get(field)) + + self.transactions = query.run(as_dict=True) if self.transactions: self.filter_transactions_data(consolidated) diff --git a/erpnext/stock/report/item_prices/item_prices.py b/erpnext/stock/report/item_prices/item_prices.py index 87f1a42e2b..ab47b4a8b9 100644 --- a/erpnext/stock/report/item_prices/item_prices.py +++ b/erpnext/stock/report/item_prices/item_prices.py @@ -4,6 +4,7 @@ import frappe from frappe import _ +from frappe.query_builder.functions import IfNull, Sum from frappe.utils import flt @@ -12,8 +13,7 @@ def execute(filters=None): filters = {} columns = get_columns(filters) - conditions = get_condition(filters) - item_map = get_item_details(conditions) + item_map = get_item_details(filters) pl = get_price_list() last_purchase_rate = get_last_purchase_rate() bom_rate = get_item_bom_rate() @@ -63,18 +63,24 @@ def get_columns(filters): return columns -def get_item_details(conditions): +def get_item_details(filters): """returns all items details""" item_map = {} - for i in frappe.db.sql( - """select name, item_group, item_name, description, - brand, stock_uom from tabItem %s - order by item_code, item_group""" - % (conditions), - as_dict=1, - ): + item = frappe.qb.DocType("Item") + query = ( + frappe.qb.from_(item) + .select(item.name, item.item_group, item.item_name, item.description, item.brand, item.stock_uom) + .orderby(item.item_code, item.item_group) + ) + + if filters.get("items") == "Enabled Items only": + query = query.where(item.disabled == 0) + elif filters.get("items") == "Disabled Items only": + query = query.where(item.disabled == 1) + + for i in query.run(as_dict=True): item_map.setdefault(i.name, i) return item_map @@ -85,19 +91,38 @@ def get_price_list(): rate = {} - price_list = frappe.db.sql( - """select ip.item_code, ip.buying, ip.selling, - concat(ifnull(cu.symbol,ip.currency), " ", round(ip.price_list_rate,2), " - ", ip.price_list) as price - from `tabItem Price` ip, `tabPrice List` pl, `tabCurrency` cu - where ip.price_list=pl.name and pl.currency=cu.name and pl.enabled=1""", - as_dict=1, - ) + ip = frappe.qb.DocType("Item Price") + pl = frappe.qb.DocType("Price List") + cu = frappe.qb.DocType("Currency") - for j in price_list: - if j.price: - rate.setdefault(j.item_code, {}).setdefault("Buying" if j.buying else "Selling", []).append( - j.price + price_list = ( + frappe.qb.from_(ip) + .from_(pl) + .from_(cu) + .select( + ip.item_code, + ip.buying, + ip.selling, + (IfNull(cu.symbol, ip.currency)).as_("currency"), + ip.price_list_rate, + ip.price_list, + ) + .where((ip.price_list == pl.name) & (pl.currency == cu.name) & (pl.enabled == 1)) + ).run(as_dict=True) + + for d in price_list: + d.update( + {"price": "{0} {1} - {2}".format(d.currency, round(d.price_list_rate, 2), d.price_list)} + ) + d.pop("currency") + d.pop("price_list_rate") + d.pop("price_list") + + if d.price: + rate.setdefault(d.item_code, {}).setdefault("Buying" if d.buying else "Selling", []).append( + d.price ) + item_rate_map = {} for item in rate: @@ -112,30 +137,39 @@ def get_price_list(): def get_last_purchase_rate(): item_last_purchase_rate_map = {} - query = """select * from ( - (select - po_item.item_code, - po.transaction_date as posting_date, - po_item.base_rate - from `tabPurchase Order` po, `tabPurchase Order Item` po_item - where po.name = po_item.parent and po.docstatus = 1) - union - (select - pr_item.item_code, - pr.posting_date, - pr_item.base_rate - from `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pr_item - where pr.name = pr_item.parent and pr.docstatus = 1) - union - (select - pi_item.item_code, - pi.posting_date, - pi_item.base_rate - from `tabPurchase Invoice` pi, `tabPurchase Invoice Item` pi_item - where pi.name = pi_item.parent and pi.docstatus = 1 and pi.update_stock = 1) - ) result order by result.item_code asc, result.posting_date asc""" + po = frappe.qb.DocType("Purchase Order") + pr = frappe.qb.DocType("Purchase Receipt") + pi = frappe.qb.DocType("Purchase Invoice") + po_item = frappe.qb.DocType("Purchase Order Item") + pr_item = frappe.qb.DocType("Purchase Receipt Item") + pi_item = frappe.qb.DocType("Purchase Invoice Item") - for d in frappe.db.sql(query, as_dict=1): + query = ( + frappe.qb.from_( + ( + frappe.qb.from_(po) + .from_(po_item) + .select(po_item.item_code, po.transaction_date.as_("posting_date"), po_item.base_rate) + .where((po.name == po_item.parent) & (po.docstatus == 1)) + ) + + ( + frappe.qb.from_(pr) + .from_(pr_item) + .select(pr_item.item_code, pr.posting_date, pr_item.base_rate) + .where((pr.name == pr_item.parent) & (pr.docstatus == 1)) + ) + + ( + frappe.qb.from_(pi) + .from_(pi_item) + .select(pi_item.item_code, pi.posting_date, pi_item.base_rate) + .where((pi.name == pi_item.parent) & (pi.docstatus == 1) & (pi.update_stock == 1)) + ) + ) + .select("*") + .orderby("item_code", "posting_date") + ) + + for d in query.run(as_dict=True): item_last_purchase_rate_map[d.item_code] = d.base_rate return item_last_purchase_rate_map @@ -146,12 +180,15 @@ def get_item_bom_rate(): item_bom_map = {} - for b in frappe.db.sql( - """select item, (total_cost/quantity) as bom_rate - from `tabBOM` where is_active=1 and is_default=1""", - as_dict=1, - ): - item_bom_map.setdefault(b.item, flt(b.bom_rate)) + bom = frappe.qb.DocType("BOM") + bom_data = ( + frappe.qb.from_(bom) + .select(bom.item, (bom.total_cost / bom.quantity).as_("bom_rate")) + .where((bom.is_active == 1) & (bom.is_default == 1)) + ).run(as_dict=True) + + for d in bom_data: + item_bom_map.setdefault(d.item, flt(d.bom_rate)) return item_bom_map @@ -161,25 +198,17 @@ def get_valuation_rate(): item_val_rate_map = {} - for d in frappe.db.sql( - """select item_code, - sum(actual_qty*valuation_rate)/sum(actual_qty) as val_rate - from tabBin where actual_qty > 0 group by item_code""", - as_dict=1, - ): + bin = frappe.qb.DocType("Bin") + bin_data = ( + frappe.qb.from_(bin) + .select( + bin.item_code, Sum(bin.actual_qty * bin.valuation_rate) / Sum(bin.actual_qty).as_("val_rate") + ) + .where(bin.actual_qty > 0) + .groupby(bin.item_code) + ).run(as_dict=True) + + for d in bin_data: item_val_rate_map.setdefault(d.item_code, d.val_rate) return item_val_rate_map - - -def get_condition(filters): - """Get Filter Items""" - - if filters.get("items") == "Enabled Items only": - conditions = " where disabled=0 " - elif filters.get("items") == "Disabled Items only": - conditions = " where disabled=1 " - else: - conditions = "" - - return conditions diff --git a/erpnext/stock/report/stock_balance/stock_balance.py b/erpnext/stock/report/stock_balance/stock_balance.py index 679d234c9f..0fc642ef20 100644 --- a/erpnext/stock/report/stock_balance/stock_balance.py +++ b/erpnext/stock/report/stock_balance/stock_balance.py @@ -10,10 +10,10 @@ from frappe import _ from frappe.query_builder.functions import CombineDatetime from frappe.utils import cint, date_diff, flt, getdate from frappe.utils.nestedset import get_descendants_of -from pypika.terms import ExistsCriterion import erpnext from erpnext.stock.doctype.inventory_dimension.inventory_dimension import get_inventory_dimensions +from erpnext.stock.doctype.warehouse.warehouse import apply_warehouse_filter from erpnext.stock.report.stock_ageing.stock_ageing import FIFOSlots, get_average_age from erpnext.stock.utils import add_additional_uom_columns, is_reposting_item_valuation_in_progress @@ -270,18 +270,8 @@ def apply_conditions(query, filters): if company := filters.get("company"): query = query.where(sle.company == company) - if warehouse := filters.get("warehouse"): - lft, rgt = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt"]) - chilren_subquery = ( - frappe.qb.from_(warehouse_table) - .select(warehouse_table.name) - .where( - (warehouse_table.lft >= lft) - & (warehouse_table.rgt <= rgt) - & (warehouse_table.name == sle.warehouse) - ) - ) - query = query.where(ExistsCriterion(chilren_subquery)) + if filters.get("warehouse"): + query = apply_warehouse_filter(query, sle, filters) elif warehouse_type := filters.get("warehouse_type"): query = ( query.join(warehouse_table) diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py index e18d4c7522..a95119736a 100644 --- a/erpnext/stock/report/stock_ledger/stock_ledger.py +++ b/erpnext/stock/report/stock_ledger/stock_ledger.py @@ -6,11 +6,11 @@ import frappe from frappe import _ from frappe.query_builder.functions import CombineDatetime from frappe.utils import cint, flt -from pypika.terms import ExistsCriterion from erpnext.stock.doctype.inventory_dimension.inventory_dimension import get_inventory_dimensions from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos from erpnext.stock.doctype.stock_reconciliation.stock_reconciliation import get_stock_balance_for +from erpnext.stock.doctype.warehouse.warehouse import apply_warehouse_filter from erpnext.stock.utils import ( is_reposting_item_valuation_in_progress, update_included_uom_in_report, @@ -295,20 +295,7 @@ def get_stock_ledger_entries(filters, items): if filters.get(field): query = query.where(sle[field] == filters.get(field)) - if warehouse := filters.get("warehouse"): - lft, rgt = frappe.db.get_value("Warehouse", warehouse, ["lft", "rgt"]) - - warehouse_table = frappe.qb.DocType("Warehouse") - chilren_subquery = ( - frappe.qb.from_(warehouse_table) - .select(warehouse_table.name) - .where( - (warehouse_table.lft >= lft) - & (warehouse_table.rgt <= rgt) - & (warehouse_table.name == sle.warehouse) - ) - ) - query = query.where(ExistsCriterion(chilren_subquery)) + query = apply_warehouse_filter(query, sle, filters) return query.run(as_dict=True) diff --git a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py index 49e797d6a3..f477d8f08f 100644 --- a/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py +++ b/erpnext/stock/report/stock_projected_qty/stock_projected_qty.py @@ -5,6 +5,7 @@ import frappe from frappe import _ from frappe.utils import flt, today +from pypika.terms import ExistsCriterion from erpnext.accounts.doctype.pos_invoice.pos_invoice import get_pos_reserved_qty from erpnext.stock.utils import ( @@ -218,10 +219,26 @@ def get_columns(): def get_bin_list(filters): - conditions = [] + bin = frappe.qb.DocType("Bin") + query = ( + frappe.qb.from_(bin) + .select( + bin.item_code, + bin.warehouse, + bin.actual_qty, + bin.planned_qty, + bin.indented_qty, + bin.ordered_qty, + bin.reserved_qty, + bin.reserved_qty_for_production, + bin.reserved_qty_for_sub_contract, + bin.projected_qty, + ) + .orderby(bin.item_code, bin.warehouse) + ) if filters.item_code: - conditions.append("item_code = '%s' " % filters.item_code) + query = query.where(bin.item_code == filters.item_code) if filters.warehouse: warehouse_details = frappe.db.get_value( @@ -229,21 +246,20 @@ def get_bin_list(filters): ) if warehouse_details: - conditions.append( - " exists (select name from `tabWarehouse` wh \ - where wh.lft >= %s and wh.rgt <= %s and bin.warehouse = wh.name)" - % (warehouse_details.lft, warehouse_details.rgt) + wh = frappe.qb.DocType("Warehouse") + query = query.where( + ExistsCriterion( + frappe.qb.from_(wh) + .select(wh.name) + .where( + (wh.lft >= warehouse_details.lft) + & (wh.rgt <= warehouse_details.rgt) + & (bin.warehouse == wh.name) + ) + ) ) - bin_list = frappe.db.sql( - """select item_code, warehouse, actual_qty, planned_qty, indented_qty, - ordered_qty, reserved_qty, reserved_qty_for_production, reserved_qty_for_sub_contract, projected_qty - from tabBin bin {conditions} order by item_code, warehouse - """.format( - conditions=" where " + " and ".join(conditions) if conditions else "" - ), - as_dict=1, - ) + bin_list = query.run(as_dict=True) return bin_list @@ -251,45 +267,43 @@ def get_bin_list(filters): def get_item_map(item_code, include_uom): """Optimization: get only the item doc and re_order_levels table""" - condition = "" - if item_code: - condition = "and item_code = {0}".format(frappe.db.escape(item_code, percent=False)) + bin = frappe.qb.DocType("Bin") + item = frappe.qb.DocType("Item") - cf_field = cf_join = "" - if include_uom: - cf_field = ", ucd.conversion_factor" - cf_join = ( - "left join `tabUOM Conversion Detail` ucd on ucd.parent=item.name and ucd.uom=%(include_uom)s" + query = ( + frappe.qb.from_(item) + .select(item.name, item.item_name, item.description, item.item_group, item.brand, item.stock_uom) + .where( + (item.is_stock_item == 1) + & (item.disabled == 0) + & ( + (item.end_of_life > today()) | (item.end_of_life.isnull()) | (item.end_of_life == "0000-00-00") + ) + & (ExistsCriterion(frappe.qb.from_(bin).select(bin.name).where(bin.item_code == item.name))) ) - - items = frappe.db.sql( - """ - select item.name, item.item_name, item.description, item.item_group, item.brand, item.stock_uom{cf_field} - from `tabItem` item - {cf_join} - where item.is_stock_item = 1 - and item.disabled=0 - {condition} - and (item.end_of_life > %(today)s or item.end_of_life is null or item.end_of_life='0000-00-00') - and exists (select name from `tabBin` bin where bin.item_code=item.name)""".format( - cf_field=cf_field, cf_join=cf_join, condition=condition - ), - {"today": today(), "include_uom": include_uom}, - as_dict=True, ) - condition = "" if item_code: - condition = "where parent={0}".format(frappe.db.escape(item_code, percent=False)) + query = query.where(item.item_code == item_code) + + if include_uom: + ucd = frappe.qb.DocType("UOM Conversion Detail") + query = query.left_join(ucd).on((ucd.parent == item.name) & (ucd.uom == include_uom)) + + items = query.run(as_dict=True) + + ir = frappe.qb.DocType("Item Reorder") + query = frappe.qb.from_(ir).select("*") + + if item_code: + query = query.where(ir.parent == item_code) reorder_levels = frappe._dict() - for ir in frappe.db.sql( - """select * from `tabItem Reorder` {condition}""".format(condition=condition), as_dict=1 - ): - if ir.parent not in reorder_levels: - reorder_levels[ir.parent] = [] + for d in query.run(as_dict=True): + if d.parent not in reorder_levels: + reorder_levels[d.parent] = [] - reorder_levels[ir.parent].append(ir) + reorder_levels[d.parent].append(d) item_map = frappe._dict() for item in items: diff --git a/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.py b/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.py index 5430fe6969..8c76908cc3 100644 --- a/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.py +++ b/erpnext/stock/report/supplier_wise_sales_analytics/supplier_wise_sales_analytics.py @@ -4,6 +4,7 @@ import frappe from frappe import _ +from frappe.query_builder.functions import IfNull from frappe.utils import flt @@ -70,31 +71,33 @@ def get_columns(filters): return columns -def get_conditions(filters): - conditions = "" - values = [] +def get_consumed_details(filters): + item = frappe.qb.DocType("Item") + sle = frappe.qb.DocType("Stock Ledger Entry") + + query = ( + frappe.qb.from_(sle) + .from_(item) + .select( + sle.item_code, + item.item_name, + item.description, + item.stock_uom, + sle.actual_qty, + sle.stock_value_difference, + sle.voucher_no, + sle.voucher_type, + ) + .where((sle.is_cancelled == 0) & (sle.item_code == item.name) & (sle.actual_qty < 0)) + ) if filters.get("from_date") and filters.get("to_date"): - conditions = "and sle.posting_date>=%s and sle.posting_date<=%s" - values = [filters.get("from_date"), filters.get("to_date")] + query = query.where( + (sle.posting_date >= filters.get("from_date")) & (sle.posting_date <= filters.get("to_date")) + ) - return conditions, values - - -def get_consumed_details(filters): - conditions, values = get_conditions(filters) consumed_details = {} - - for d in frappe.db.sql( - """select sle.item_code, i.item_name, i.description, - i.stock_uom, sle.actual_qty, sle.stock_value_difference, - sle.voucher_no, sle.voucher_type - from `tabStock Ledger Entry` sle, `tabItem` i - where sle.is_cancelled = 0 and sle.item_code=i.name and sle.actual_qty < 0 %s""" - % conditions, - values, - as_dict=1, - ): + for d in query.run(as_dict=True): consumed_details.setdefault(d.item_code, []).append(d) return consumed_details @@ -104,24 +107,54 @@ def get_suppliers_details(filters): item_supplier_map = {} supplier = filters.get("supplier") - for d in frappe.db.sql( - """select pr.supplier, pri.item_code from - `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pri - where pr.name=pri.parent and pr.docstatus=1 and - pri.item_code=(select name from `tabItem` where - is_stock_item=1 and name=pri.item_code)""", - as_dict=1, - ): + item = frappe.qb.DocType("Item") + pr = frappe.qb.DocType("Purchase Receipt") + pr_item = frappe.qb.DocType("Purchase Receipt Item") + + query = ( + frappe.qb.from_(pr) + .from_(pr_item) + .select(pr.supplier, pr_item.item_code) + .where( + (pr.name == pr_item.parent) + & (pr.docstatus == 1) + & ( + pr_item.item_code + == ( + frappe.qb.from_(item) + .select(item.name) + .where((item.is_stock_item == 1) & (item.name == pr_item.item_code)) + ) + ) + ) + ) + + for d in query.run(as_dict=True): item_supplier_map.setdefault(d.item_code, []).append(d.supplier) - for d in frappe.db.sql( - """select pr.supplier, pri.item_code from - `tabPurchase Invoice` pr, `tabPurchase Invoice Item` pri - where pr.name=pri.parent and pr.docstatus=1 and - ifnull(pr.update_stock, 0) = 1 and pri.item_code=(select name from `tabItem` - where is_stock_item=1 and name=pri.item_code)""", - as_dict=1, - ): + pi = frappe.qb.DocType("Purchase Invoice") + pi_item = frappe.qb.DocType("Purchase Invoice Item") + + query = ( + frappe.qb.from_(pi) + .from_(pi_item) + .select(pi.supplier, pi_item.item_code) + .where( + (pi.name == pi_item.parent) + & (pi.docstatus == 1) + & (IfNull(pi.update_stock, 0) == 1) + & ( + pi_item.item_code + == ( + frappe.qb.from_(item) + .select(item.name) + .where((item.is_stock_item == 1) & (item.name == pi_item.item_code)) + ) + ) + ) + ) + + for d in query.run(as_dict=True): if d.item_code not in item_supplier_map: item_supplier_map.setdefault(d.item_code, []).append(d.supplier) @@ -138,7 +171,11 @@ def get_suppliers_details(filters): def get_material_transfer_vouchers(): - return frappe.db.sql_list( - """select name from `tabStock Entry` where - purpose='Material Transfer' and docstatus=1""" + se = frappe.qb.DocType("Stock Entry") + query = ( + frappe.qb.from_(se) + .select(se.name) + .where((se.purpose == "Material Transfer") & (se.docstatus == 1)) ) + + return [r[0] for r in query.run()] diff --git a/erpnext/stock/report/total_stock_summary/total_stock_summary.py b/erpnext/stock/report/total_stock_summary/total_stock_summary.py index 21529da2a1..c3155bd1a5 100644 --- a/erpnext/stock/report/total_stock_summary/total_stock_summary.py +++ b/erpnext/stock/report/total_stock_summary/total_stock_summary.py @@ -4,60 +4,58 @@ import frappe from frappe import _ +from frappe.query_builder.functions import Sum def execute(filters=None): if not filters: filters = {} - columns = get_columns() + columns = get_columns(filters) stock = get_total_stock(filters) return columns, stock -def get_columns(): +def get_columns(filters): columns = [ - _("Company") + ":Link/Company:250", - _("Warehouse") + ":Link/Warehouse:150", _("Item") + ":Link/Item:150", _("Description") + "::300", _("Current Qty") + ":Float:100", ] + if filters.get("group_by") == "Warehouse": + columns.insert(0, _("Warehouse") + ":Link/Warehouse:150") + else: + columns.insert(0, _("Company") + ":Link/Company:250") + return columns def get_total_stock(filters): - conditions = "" - columns = "" + bin = frappe.qb.DocType("Bin") + item = frappe.qb.DocType("Item") + wh = frappe.qb.DocType("Warehouse") + + query = ( + frappe.qb.from_(bin) + .inner_join(item) + .on(bin.item_code == item.item_code) + .inner_join(wh) + .on(wh.name == bin.warehouse) + .where(bin.actual_qty != 0) + ) if filters.get("group_by") == "Warehouse": if filters.get("company"): - conditions += " AND warehouse.company = %s" % frappe.db.escape( - filters.get("company"), percent=False - ) + query = query.where(wh.company == filters.get("company")) - conditions += " GROUP BY ledger.warehouse, item.item_code" - columns += "'' as company, ledger.warehouse" + query = query.select(bin.warehouse).groupby(bin.warehouse) else: - conditions += " GROUP BY warehouse.company, item.item_code" - columns += " warehouse.company, '' as warehouse" + query = query.select(wh.company).groupby(wh.company) - return frappe.db.sql( - """ - SELECT - %s, - item.item_code, - item.description, - sum(ledger.actual_qty) as actual_qty - FROM - `tabBin` AS ledger - INNER JOIN `tabItem` AS item - ON ledger.item_code = item.item_code - INNER JOIN `tabWarehouse` warehouse - ON warehouse.name = ledger.warehouse - WHERE - ledger.actual_qty != 0 %s""" - % (columns, conditions) - ) + query = query.select( + item.item_code, item.description, Sum(bin.actual_qty).as_("actual_qty") + ).groupby(item.item_code) + + return query.run() diff --git a/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py b/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py index a54373f364..eedf1a0460 100644 --- a/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py +++ b/erpnext/stock/report/warehouse_wise_item_balance_age_and_value/warehouse_wise_item_balance_age_and_value.py @@ -7,6 +7,7 @@ import frappe from frappe import _ +from frappe.query_builder.functions import Count from frappe.utils import flt from erpnext.stock.report.stock_ageing.stock_ageing import FIFOSlots, get_average_age @@ -98,7 +99,7 @@ def get_columns(filters): def validate_filters(filters): if not (filters.get("item_code") or filters.get("warehouse")): - sle_count = flt(frappe.db.sql("""select count(name) from `tabStock Ledger Entry`""")[0][0]) + sle_count = flt(frappe.qb.from_("Stock Ledger Entry").select(Count("name")).run()[0][0]) if sle_count > 500000: frappe.throw(_("Please set filter based on Item or Warehouse")) if not filters.get("company"): @@ -108,25 +109,16 @@ def validate_filters(filters): def get_warehouse_list(filters): from frappe.core.doctype.user_permission.user_permission import get_permitted_documents - condition = "" - user_permitted_warehouse = get_permitted_documents("Warehouse") - value = () - if user_permitted_warehouse: - condition = "and name in %s" - value = set(user_permitted_warehouse) - elif not user_permitted_warehouse and filters.get("warehouse"): - condition = "and name = %s" - value = filters.get("warehouse") + wh = frappe.qb.DocType("Warehouse") + query = frappe.qb.from_(wh).select(wh.name).where(wh.is_group == 0) - return frappe.db.sql( - """select name - from `tabWarehouse` where is_group = 0 - {condition}""".format( - condition=condition - ), - value, - as_dict=1, - ) + user_permitted_warehouse = get_permitted_documents("Warehouse") + if user_permitted_warehouse: + query = query.where(wh.name.isin(set(user_permitted_warehouse))) + elif filters.get("warehouse"): + query = query.where(wh.name == filters.get("warehouse")) + + return query.run(as_dict=True) def add_warehouse_column(columns, warehouse_list): diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 9ca40c3675..cdf6e89fcb 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -542,6 +542,14 @@ class update_entries_after(object): if not self.args.get("sle_id"): self.get_dynamic_incoming_outgoing_rate(sle) + if ( + sle.voucher_type in ["Purchase Receipt", "Purchase Invoice"] + and sle.voucher_detail_no + and sle.actual_qty < 0 + and frappe.get_cached_value(sle.voucher_type, sle.voucher_no, "is_internal_supplier") + ): + sle.outgoing_rate = get_incoming_rate_for_inter_company_transfer(sle) + if get_serial_nos(sle.serial_no): self.get_serialized_values(sle) self.wh_data.qty_after_transaction += flt(sle.actual_qty) @@ -589,6 +597,7 @@ class update_entries_after(object): sle.stock_queue = json.dumps(self.wh_data.stock_queue) sle.stock_value_difference = stock_value_difference sle.doctype = "Stock Ledger Entry" + frappe.get_doc(sle).db_update() if not self.args.get("sle_id"): @@ -652,22 +661,7 @@ class update_entries_after(object): and sle.voucher_detail_no and frappe.get_cached_value(sle.voucher_type, sle.voucher_no, "is_internal_supplier") ): - field = ( - "delivery_note_item" if sle.voucher_type == "Purchase Receipt" else "sales_invoice_item" - ) - doctype = ( - "Delivery Note Item" if sle.voucher_type == "Purchase Receipt" else "Sales Invoice Item" - ) - refernce_name = frappe.get_cached_value( - sle.voucher_type + " Item", sle.voucher_detail_no, field - ) - - if refernce_name: - rate = frappe.get_cached_value( - doctype, - refernce_name, - "incoming_rate", - ) + rate = get_incoming_rate_for_inter_company_transfer(sle) else: if sle.voucher_type in ("Purchase Receipt", "Purchase Invoice"): rate_field = "valuation_rate" @@ -748,14 +742,12 @@ class update_entries_after(object): def update_rate_on_purchase_receipt(self, sle, outgoing_rate): if frappe.db.exists(sle.voucher_type + " Item", sle.voucher_detail_no): - frappe.db.set_value( - sle.voucher_type + " Item", - sle.voucher_detail_no, - { - "base_net_rate": outgoing_rate, - "valuation_rate": outgoing_rate, - }, - ) + if sle.voucher_type in ["Purchase Receipt", "Purchase Invoice"] and frappe.get_cached_value( + sle.voucher_type, sle.voucher_no, "is_internal_supplier" + ): + frappe.db.set_value( + f"{sle.voucher_type} Item", sle.voucher_detail_no, "valuation_rate", sle.outgoing_rate + ) else: frappe.db.set_value( "Purchase Receipt Item Supplied", sle.voucher_detail_no, "rate", outgoing_rate @@ -1546,3 +1538,25 @@ def is_negative_stock_allowed(*, item_code: Optional[str] = None) -> bool: if item_code and cint(frappe.db.get_value("Item", item_code, "allow_negative_stock", cache=True)): return True return False + + +def get_incoming_rate_for_inter_company_transfer(sle) -> float: + """ + For inter company transfer, incoming rate is the average of the outgoing rate + """ + rate = 0.0 + + field = "delivery_note_item" if sle.voucher_type == "Purchase Receipt" else "sales_invoice_item" + + doctype = "Delivery Note Item" if sle.voucher_type == "Purchase Receipt" else "Sales Invoice Item" + + reference_name = frappe.get_cached_value(sle.voucher_type + " Item", sle.voucher_detail_no, field) + + if reference_name: + rate = frappe.get_cached_value( + doctype, + reference_name, + "incoming_rate", + ) + + return rate diff --git a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py index 4e00138906..472f6bc24e 100644 --- a/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/test_service_level_agreement.py @@ -15,8 +15,30 @@ from erpnext.support.doctype.service_level_agreement.service_level_agreement imp class TestServiceLevelAgreement(unittest.TestCase): def setUp(self): + self.create_company() frappe.db.set_value("Support Settings", None, "track_service_level_agreement", 1) - frappe.db.sql("delete from `tabLead`") + lead = frappe.qb.DocType("Lead") + frappe.qb.from_(lead).delete().where(lead.company == self.company).run() + + def create_company(self): + name = "_Test Support SLA" + company = None + if frappe.db.exists("Company", name): + company = frappe.get_doc("Company", name) + else: + company = frappe.get_doc( + { + "doctype": "Company", + "company_name": name, + "country": "India", + "default_currency": "INR", + "create_chart_of_accounts_based_on": "Standard Template", + "chart_of_accounts": "Standard", + } + ) + company = company.save() + + self.company = company.name def test_service_level_agreement(self): # Default Service Level Agreement @@ -205,7 +227,7 @@ class TestServiceLevelAgreement(unittest.TestCase): # make lead with default SLA creation = datetime.datetime(2019, 3, 4, 12, 0) - lead = make_lead(creation=creation, index=1) + lead = make_lead(creation=creation, index=1, company=self.company) self.assertEqual(lead.service_level_agreement, lead_sla.name) self.assertEqual(lead.response_by, datetime.datetime(2019, 3, 4, 16, 0)) @@ -233,7 +255,7 @@ class TestServiceLevelAgreement(unittest.TestCase): ) creation = datetime.datetime(2020, 3, 4, 4, 0) - lead = make_lead(creation, index=2) + lead = make_lead(creation, index=2, company=self.company) frappe.flags.current_time = datetime.datetime(2020, 3, 4, 4, 15) lead.reload() @@ -267,7 +289,7 @@ class TestServiceLevelAgreement(unittest.TestCase): ) creation = datetime.datetime(2019, 3, 4, 12, 0) - lead = make_lead(creation=creation, index=1) + lead = make_lead(creation=creation, index=1, company=self.company) self.assertEqual(lead.response_by, datetime.datetime(2019, 3, 4, 16, 0)) # failed with response time only @@ -294,7 +316,7 @@ class TestServiceLevelAgreement(unittest.TestCase): # fulfilled with response time only creation = datetime.datetime(2019, 3, 4, 12, 0) - lead = make_lead(creation=creation, index=2) + lead = make_lead(creation=creation, index=2, company=self.company) self.assertEqual(lead.service_level_agreement, lead_sla.name) self.assertEqual(lead.response_by, datetime.datetime(2019, 3, 4, 16, 0)) @@ -321,7 +343,7 @@ class TestServiceLevelAgreement(unittest.TestCase): apply_sla_for_resolution=0, ) creation = datetime.datetime(2019, 3, 4, 12, 0) - lead = make_lead(creation=creation, index=4) + lead = make_lead(creation=creation, index=4, company=self.company) applied_sla = frappe.db.get_value("Lead", lead.name, "service_level_agreement") self.assertFalse(applied_sla) @@ -611,7 +633,7 @@ def create_custom_doctype(): return frappe.get_doc("DocType", "Test SLA on Custom Dt") -def make_lead(creation=None, index=0): +def make_lead(creation=None, index=0, company=None): return frappe.get_doc( { "doctype": "Lead", @@ -621,5 +643,6 @@ def make_lead(creation=None, index=0): "creation": creation, "service_level_agreement_creation": creation, "priority": "Medium", + "company": company, } ).insert(ignore_permissions=True) diff --git a/erpnext/utilities/doctype/video/video.py b/erpnext/utilities/doctype/video/video.py index 15dbccde89..13b7877b21 100644 --- a/erpnext/utilities/doctype/video/video.py +++ b/erpnext/utilities/doctype/video/video.py @@ -59,7 +59,7 @@ def update_youtube_data(): "Video Settings", "Video Settings", ["enable_youtube_tracking", "frequency"] ) - if not enable_youtube_tracking: + if not cint(enable_youtube_tracking): return frequency = get_frequency(frequency)