Merge branch 'develop' of https://github.com/niralisatapara/erpnext into develop

This commit is contained in:
niralisatapara 2022-11-02 11:21:02 +05:30
commit 4aff2a32ad
156 changed files with 7397 additions and 4463 deletions

View File

@ -120,7 +120,7 @@ jobs:
FRAPPE_BRANCH: ${{ github.event.inputs.branch }} FRAPPE_BRANCH: ${{ github.event.inputs.branch }}
- name: Run Tests - 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: env:
TYPE: server TYPE: server
CI_BUILD_ID: ${{ github.run_id }} CI_BUILD_ID: ${{ github.run_id }}

View File

@ -2,397 +2,438 @@
"country_code": "at", "country_code": "at",
"name": "Austria - Chart of Accounts", "name": "Austria - Chart of Accounts",
"tree": { "tree": {
"Summe Abschreibungen und Aufwendungen": { "Klasse 0 Aktiva: Anlageverm\u00f6gen": {
"7010 bis 7080 Abschreibungen auf das Anlageverm\u00f6gen (ausgenommen Finanzanlagen)": {}, "0100 Konzessionen ": {"account_type": "Fixed Asset"},
"7100 bis 7190 Sonstige Steuern": { "0110 Patentrechte und Lizenzen ": {"account_type": "Fixed Asset"},
"account_type": "Tax" "0120 Datenverarbeitungsprogramme ": {"account_type": "Fixed Asset"},
}, "0130 Marken, Warenzeichen und Musterschutzrechte, sonstige Urheberrechte ": {"account_type": "Fixed Asset"},
"7200 bis 7290 Instandhaltung u. Reinigung durh Dritte, Entsorgung, Beleuchtung": {}, "0140 Pacht- und Mietrechte ": {"account_type": "Fixed Asset"},
"7300 bis 7310 Transporte durch Dritte": {}, "0150 Bezugs- und ähnliche Rechte ": {"account_type": "Fixed Asset"},
"7320 bis 7330 Kfz - Aufwand": {}, "0160 Geschäfts-/Firmenwert ": {"account_type": "Fixed Asset"},
"7340 bis 7350 Reise- und Fahraufwand": {}, "0170 Umgründungsmehrwert ": {"account_type": "Fixed Asset"},
"7360 bis 7370 Tag- und N\u00e4chtigungsgelder": {}, "0180 Geleistete Anzahlungen auf immaterielle Vermögensgegenstände": {"account_type": "Fixed Asset"},
"7380 bis 7390 Nachrichtenaufwand": {}, "0190 Kumulierte Abschreibungen zu immateriellen Vermögensgegenständen ": {"account_type": "Fixed Asset"},
"7400 bis 7430 Miet- und Pachtaufwand": {}, "0200 Unbebaute Grundstücke, soweit nicht landwirtschaftlich genutzt ": {"account_type": "Fixed Asset"},
"7440 bis 7470 Leasingaufwand": {}, "0210 Bebaute Grundstücke (Grundwert) ": {"account_type": "Fixed Asset"},
"7480 bis 7490 Lizenzaufwand": {}, "0220 Landwirtschaftlich genutzte Grundstücke ": {"account_type": "Fixed Asset"},
"7500 bis 7530 Aufwand f\u00fcr beigestelltes Personal": {}, "0230 Grundstücksgleiche Rechte ": {"account_type": "Fixed Asset"},
"7540 bis 7570 Provisionen an Dritte": {}, "0300 Betriebs- und Geschäftsgebäude auf eigenem Grund ": {"account_type": "Fixed Asset"},
"7580 bis 7590 Aufsichtsratsverg\u00fctungen": {}, "0310 Wohn- und Sozialgebäude auf eigenem Grund ": {"account_type": "Fixed Asset"},
"7610 bis 7620 Druckerzeugnisse und Vervielf\u00e4ltigungen": {}, "0320 Betriebs- und Geschäftsgebäude auf fremdem Grund ": {"account_type": "Fixed Asset"},
"7650 bis 7680 Werbung und Repr\u00e4sentationen": {}, "0330 Wohn- und Sozialgebäude auf fremdem Grund ": {"account_type": "Fixed Asset"},
"7700 bis 7740 Versicherungen": {}, "0340 Grundstückseinrichtungen auf eigenem Grund ": {"account_type": "Fixed Asset"},
"7750 bis 7760 Beratungs- und Pr\u00fcfungsaufwand": {}, "0350 Grundstückseinrichtungen auf fremdem Grund ": {"account_type": "Fixed Asset"},
"7800 bis 7810 Schadensf\u00e4lle": {}, "0360 Bauliche Investitionen in fremden (gepachteten) Betriebs- und Geschäftsgebäuden": {"account_type": "Fixed Asset"},
"7840 bis 7880 Verschiedene betriebliche Aufwendungen": {}, "0370 Bauliche Investitionen in fremden (gepachteten) Wohn- und Sozialgebäuden": {"account_type": "Fixed Asset"},
"7910 bis 7950 Aufwandsstellenrechung der Hersteller": {}, "0390 Kumulierte Abschreibungen zu Grundstücken ": {"account_type": "Fixed Asset"},
"Abschreibungen auf aktivierte Aufwendungen f\u00fcr das Ingangs. u. Erweitern des Betriebes": {}, "0400 Maschinen und Geräte ": {"account_type": "Fixed Asset"},
"Abschreibungen vom Umlaufverm\u00f6gen, soweit diese die im Unternehmen \u00fcblichen Abschreibungen \u00fcbersteigen": {}, "0500 Maschinenwerkzeuge ": {"account_type": "Fixed Asset"},
"Aufwandsstellenrechnung": {}, "0510 Allgemeine Werkzeuge und Handwerkzeuge ": {"account_type": "Fixed Asset"},
"Aus- und Fortbildung": {}, "0520 Prototypen, Formen, Modelle ": {"account_type": "Fixed Asset"},
"Buchwert abgegangener Anlagen, ausgenommen Finanzanlagen": {}, "0530 Andere Erzeugungshilfsmittel (auch Softwarewerkzeuge)": {"account_type": "Fixed Asset"},
"B\u00fcromaterial und Drucksorten": {}, "0540 Hebezeuge und Montageanlagen ": {"account_type": "Fixed Asset"},
"Fachliteratur und Zeitungen ": {}, "0550 Geringwertige Vermögensgegenstände, soweit im Erzeugungsprozess ": {"account_type": "Fixed Asset"},
"Herstellungskosten der zur Erzielung der Umsatzerl\u00f6se erbrachten Leistungen": {}, "0560 Festwerte technische Anlagen und Maschinen ": {"account_type": "Fixed Asset"},
"Mitgliedsbeitr\u00e4ge": {}, "0590 Kumulierte Abschreibungen zu technischen Anlagen und Maschinen ": {"account_type": "Fixed Asset"},
"Skontoertr\u00e4ge auf sonstige betriebliche Aufwendungen": {}, "0600 Betriebs- und Geschäftsausstattung, soweit nicht gesondert angeführt ": {"account_type": "Fixed Asset"},
"Sonstige betrieblichen Aufwendungen": {}, "0610 Andere Anlagen, soweit nicht gesondert angeführt ": {"account_type": "Fixed Asset"},
"Spenden und Trinkgelder": {}, "0620 Büromaschinen, EDV-Anlagen ": {"account_type": "Fixed Asset"},
"Spesen des Geldverkehrs": {}, "0630 PKW und Kombis ": {"account_type": "Fixed Asset"},
"Verluste aus dem Abgang vom Anlageverm\u00f6gen, ausgenommen Finanzanlagen": {}, "0640 LKW ": {"account_type": "Fixed Asset"},
"Vertriebskosten": {}, "0650 Andere Beförderungsmittel ": {"account_type": "Fixed Asset"},
"Verwaltungskosten": {}, "0660 Gebinde ": {"account_type": "Fixed Asset"},
"root_type": "Expense" "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"},
"Summe Betriebliche Ertr\u00e4ge": { "0690 Kumulierte Abschreibungen zu anderen Anlagen, Betriebs- und Geschäftsausstattung": {"account_type": "Fixed Asset"},
"4400 bis 4490 Erl\u00f6sschm\u00e4lerungen": {}, "0700 Geleistete Anzahlungen auf Sachanlagen ": {"account_type": "Fixed Asset"},
"4500 bis 4570 Ver\u00e4nderungen des Bestandes an fertigen und unfertigen Erzeugn. sowie an noch nicht abrechenbaren Leistungen": {}, "0710 Anlagen in Bau ": {"account_type": "Fixed Asset"},
"4580 bis 4590 andere aktivierte Eigenleistungen": {}, "0790 Kumulierte Abschreibungen zu geleisteten Anzahlungen auf Sachanlagen ": {"account_type": "Fixed Asset"},
"4600 bis 4620 Erl\u00f6se aus dem Abgang vom Anlageverm\u00f6gen, ausgen. Finanzanlagen": {}, "0800 Anteile an verbundenen Unternehmen ": {"account_type": "Fixed Asset"},
"4630 bis 4650 Ertr\u00e4ge aus dem Abgang vom Anlageverm\u00f6gen, ausgen. Finanzanlagen": {}, "0810 Beteiligungen an Gemeinschaftsunternehmen ": {"account_type": "Fixed Asset"},
"4660 bis 4670 Ertr\u00e4ge aus der Zuschreibung zum Anlageverm\u00f6gen, ausgen. Finanzanlagen": {}, "0820 Beteiligungen an angeschlossenen (assoziierten) Unternehmen ": {"account_type": "Fixed Asset"},
"4700 bis 4790 Ertr\u00e4ge aus der Aufl\u00f6sung von R\u00fcckstellungen": {}, "0830 Eigene Anteile, Anteile an herrschenden oder mit Mehrheit beteiligten ": {"account_type": "Fixed Asset"},
"4800 bis 4990 \u00dcbrige betriebliche Ertr\u00e4ge": {}, "0840 Sonstige Beteiligungen ": {"account_type": "Fixed Asset"},
"Erl\u00f6se 0 % Ausfuhrlieferungen/Drittl\u00e4nder": {}, "0850 Ausleihungen an verbundene Unternehmen ": {"account_type": "Fixed Asset"},
"Erl\u00f6se 10 %": {}, "0860 Ausleihungen an Unternehmen mit Beteiligungsverhältnis": {"account_type": "Fixed Asset"},
"Erl\u00f6se 20 %": {}, "0870 Ausleihungen an Gesellschafter ": {"account_type": "Fixed Asset"},
"Erl\u00f6se aus im Inland stpfl. EG Lieferungen 10 % USt": {}, "0880 Sonstige Ausleihungen ": {"account_type": "Fixed Asset"},
"Erl\u00f6se aus im Inland stpfl. EG Lieferungen 20 % USt": {}, "0890 Anteile an Kapitalgesellschaften ohne Beteiligungscharakter ": {"account_type": "Fixed Asset"},
"Erl\u00f6se i.g. Lieferungen (stfr)": {}, "0900 Anteile an Personengesellschaften ohne Beteiligungscharakter ": {"account_type": "Fixed Asset"},
"root_type": "Income" "0910 Genossenschaftsanteile ohne Beteiligungscharakter ": {"account_type": "Fixed Asset"},
}, "0920 Anteile an Investmentfonds ": {"account_type": "Fixed Asset"},
"Summe Eigenkapital R\u00fccklagen Abschlusskonten": { "0930 Festverzinsliche Wertpapiere des Anlagevermögens ": {"account_type": "Fixed Asset"},
"9000 bis 9180 Gezeichnetes bzw. gewidmetes Kapital": { "0980 Geleistete Anzahlungen auf Finanzanlagen ": {"account_type": "Fixed Asset"},
"account_type": "Equity" "0990 Kumulierte Abschreibungen zu Finanzanlagen ": {"account_type": "Fixed Asset"},
}, "root_type": "Asset"
"9200 bis 9290 Kapitalr\u00fccklagen": { },
"account_type": "Equity" "Klasse 1 Aktiva: Vorr\u00e4te": {
}, "1000 Bezugsverrechnung": {"account_type": "Stock"},
"9300 bis 9380 Gewinnr\u00fccklagen": { "1100 Rohstoffe": {"account_type": "Stock"},
"account_type": "Equity" "1200 Bezogene Teile": {"account_type": "Stock"},
}, "1300 Hilfsstoffe": {"account_type": "Stock"},
"9400 bis 9590 Bewertungsreserven uns sonst. unversteuerte R\u00fccklagen": { "1350 Betriebsstoffe": {"account_type": "Stock"},
"account_type": "Equity" "1360 Vorrat Energietraeger": {"account_type": "Stock"},
}, "1400 Unfertige Erzeugnisse": {"account_type": "Stock"},
"9600 bis 9690 Privat und Verrechnungskonten bei Einzelunternehmen und Personengesellschaften": {}, "1500 Fertige Erzeugnisse": {"account_type": "Stock"},
"9700 bis 9790 Einlagen stiller Gesellschafter ": {}, "1600 Handelswarenvorrat": {"account_type": "Stock Received But Not Billed"},
"9900 bis 9999 Evidenzkonten": {}, "1700 Noch nicht abrechenbare Leistungen": {"account_type": "Stock"},
"Bilanzgewinn (-verlust )": { "1900 Wertberichtigungen": {"account_type": "Stock"},
"account_type": "Equity" "1800 Geleistete Anzahlungen": {"account_type": "Stock"},
}, "1900 Wertberichtigungen": {"account_type": "Stock"},
"Er\u00f6ffnungsbilanz": {}, "root_type": "Asset"
"Gewinn- und Verlustrechnung": {}, },
"Schlussbilanz": {}, "Klasse 3 Passiva: Verbindlichkeiten": {
"nicht eingeforderte ausstehende Einlagen": { "3000 Allgemeine Verbindlichkeiten (Schuld)": {"account_type": "Payable"},
"account_type": "Equity" "3010 R\u00fcckstellungen f\u00fcr Pensionen": {"account_type": "Payable"},
}, "3020 Steuerr\u00fcckstellungen": {"account_type": "Tax"},
"root_type": "Equity" "3041 Sonstige R\u00fcckstellungen": {"account_type": "Payable"},
}, "3110 Verbindlichkeiten gegen\u00fcber Bank": {"account_type": "Payable"},
"Summe Finanzertr\u00e4ge und Aufwendungen": { "3150 Verbindlichkeiten Darlehen": {"account_type": "Payable"},
"8000 bis 8040 Ertr\u00e4ge aus Beteiligungen": {}, "3185 Verbindlichkeiten Kreditkarte": {"account_type": "Payable"},
"8050 bis 8090 Ertr\u00e4ge aus anderen Wertpapieren und Ausleihungen des Finanzanlageverm\u00f6gens": {}, "3380 Verbindlichkeiten aus der Annahme gezogener Wechsel u. d. Ausstellungen eigener Wechsel": {
"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": {
"account_type": "Payable" "account_type": "Payable"
}, },
"3400 bis 3470 Verbindlichkeiten gegen\u00fc. verb. Untern., Verbindl. gegen\u00fc. Untern., mit denen eine Beteiligungsverh\u00e4lnis besteht": {}, "3400 Verbindlichkeiten gegen\u00fc. verb. Untern., Verbindl. gegen\u00fc. Untern., mit denen eine Beteiligungsverh\u00e4lnis besteht": {},
"3600 bis 3690 Verbindlichkeiten im Rahmen der sozialen Sicherheit": {}, "3460 Verbindlichkeiten gegenueber Gesellschaftern": {"account_type": "Payable"},
"3700 bis 3890 \u00dcbrige sonstige Verbindlichkeiten": {}, "3470 Einlagen stiller Gesellschafter": {"account_type": "Payable"},
"3900 bis 3990 Passive Rechnungsabgrenzungsposten": {}, "3585 Verbindlichkeiten Lohnsteuer": {"account_type": "Tax"},
"Anleihen (einschlie\u00dflich konvertibler)": {}, "3590 Verbindlichkeiten Kommunalabgaben": {"account_type": "Tax"},
"Erhaltene Anzahlungenauf Bestellungen": {}, "3595 Verbindlichkeiten Dienstgeberbeitrag": {"account_type": "Tax"},
"R\u00fcckstellungen f\u00fcr Abfertigung": {}, "3600 Verbindlichkeiten Sozialversicherung": {"account_type": "Payable"},
"R\u00fcckstellungen f\u00fcr Pensionen": {}, "3640 Verbindlichkeiten Loehne und Gehaelter": {"account_type": "Payable"},
"USt. \u00a719 /art (reverse charge)": { "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" "account_type": "Tax"
}, },
"Umsatzsteuer": {}, "3500 Verbindlichkeiten aus Umsatzsteuer": {"account_type": "Tax"},
"Umsatzsteuer Zahllast": { "3580 Umsatzsteuer Zahllast": {
"account_type": "Tax" "account_type": "Tax"
}, },
"Umsatzsteuer aus i.g. Erwerb 10%": { "3510 Umsatzsteuer Inland 20%": {
"account_type": "Tax" "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" "account_type": "Tax"
}, },
"Umsatzsteuer aus i.g. Lieferungen 10%": { "3525 Umsatzsteuer aus i.g. Erwerb 10%": {
"account_type": "Tax" "account_type": "Tax"
}, },
"Umsatzsteuer aus i.g. Lieferungen 20%": { "3560 Umsatzsteuer-Evidenzkonto f\u00fcr erhaltene Anzahlungen auf Bestellungen": {},
"account_type": "Tax" "3360 Verbindlichkeiten aus Lieferungen u. Leistungen EU": {
},
"Umsatzsteuer-Evidenzkonto f\u00fcr erhaltene Anzahlungen auf Bestellungen": {},
"Verbindlichkeiten aus Lieferungen u. Leistungen EU": {
"account_type": "Payable" "account_type": "Payable"
}, },
"Verbindlichkeiten aus Lieferungen u. Leistungen Inland": { "3000 Verbindlichkeiten aus Lieferungen u. Leistungen Inland": {
"account_type": "Payable" "account_type": "Payable"
}, },
"Verbindlichkeiten aus Lieferungen u. Leistungen sonst. Ausland": { "3370 Verbindlichkeiten aus Lieferungen u. Leistungen sonst. Ausland": {
"account_type": "Payable" "account_type": "Payable"
}, },
"Verbindlichkeiten gegen\u00fcber Gesellschaften": {}, "3400 Verbindlichkeiten gegen\u00fcber verbundenen Unternehmen": {},
"Verrechnung Finanzamt": { "3570 Verrechnung Finanzamt": {
"account_type": "Tax" "account_type": "Tax"
}, },
"root_type": "Liability" "root_type": "Liability"
}, },
"Summe Kontoklasse 0 Anlageverm\u00f6gen": { "Klasse 2 Aktiva: Umlaufverm\u00f6gen, Rechnungsabgrenzungen": {
"44 bis 49 Sonstige Maschinen und maschinelle Anlagen": {}, "2030 Forderungen aus Lieferungen und Leistungen Inland (0% USt, umsatzsteuerfrei)": {
"920 bis 930 Festverzinsliche Wertpapiere des Anlageverm\u00f6gens": {}, "account_type": "Receivable"
"940 bis 970 Sonstige Finanzanlagen, Wertrechte": {}, },
"Allgemeine Werkzeuge und Handwerkzeuge": {}, "2010 Forderungen aus Lieferungen und Leistungen Inland (10% USt, umsatzsteuerfrei)": {
"Andere Bef\u00f6rderungsmittel": {}, "account_type": "Receivable"
"Andere Betriebs- und Gesch\u00e4ftsausstattung": {}, },
"Andere Erzeugungshilfsmittel": {}, "2000 Forderungen aus Lieferungen und Leistungen Inland (20% USt, umsatzsteuerfrei)": {
"Anlagen im Bau": {}, "account_type": "Receivable"
"Anteile an Investmentfonds": {}, },
"Anteile an Kapitalgesellschaften ohne Beteiligungscharakter": {}, "2040 Forderungen aus Lieferungen und Leistungen Inland (sonstiger USt-Satz)": {
"Anteile an Personengesellschaften ohne Beteiligungscharakter": {}, "account_type": "Receivable"
"Anteile an verbundenen Unternehmen": {}, },
"Antriebsmaschinen": {}, "2100 Forderungen aus Lieferungen und Leistungen EU": {
"Aufwendungen f\u00fcs das Ingangssetzen u. Erweitern eines Betriebes": {}, "account_type": "Receivable"
"Ausleihungen an verbundene Unternehmen": {}, },
"Ausleihungen an verbundene Unternehmen, mit denen ein Beteiligungsverh\u00e4lnis besteht": {}, "2150 Forderungen aus Lieferungen und Leistungen Ausland (Nicht-EU)": {
"Bauliche Investitionen in fremden (gepachteten) Betriebs- und Gesch\u00e4ftsgeb\u00e4uden": {}, "account_type": "Receivable"
"Bauliche Investitionen in fremden (gepachteten) Wohn- und Sozialgeb\u00e4uden": {}, },
"Bebaute Grundst\u00fccke (Grundwert)": {}, "2200 Forderungen gegen\u00fcber verbundenen Unternehmen": {
"Beheizungs- und Beleuchtungsanlagen": {}, "account_type": "Receivable"
"Beteiligungen an Gemeinschaftunternehmen": {}, },
"Beteiligungen an angeschlossenen (assoziierten) Unternehmen": {}, "2250 Forderungen gegen\u00fcber Unternehmen, mit denen ein Beteiligungsverh\u00e4ltnis besteht": {
"Betriebs- und Gesch\u00e4ftsgeb\u00e4ude auf eigenem Grund": {}, "account_type": "Receivable"
"Betriebs- und Gesch\u00e4ftsgeb\u00e4ude auf fremdem Grund": {}, },
"B\u00fcromaschinen, EDV - Anlagen": {}, "2300 Sonstige Forderungen und Verm\u00f6gensgegenst\u00e4nde": {
"Datenverarbeitungsprogramme": {}, "account_type": "Receivable"
"Energieversorgungsanlagen": {}, },
"Fertigungsmaschinen": {}, "2630 Sonstige Wertpapiere": {
"Gebinde": {}, "account_type": "Stock"
"Geleistete Anzahlungen": {}, },
"Genossenschaften ohne Beteiligungscharakter": {}, "2750 Kassenbest\u00e4nde in Fremdw\u00e4hrung": {
"Geringwertige Verm\u00f6gensgegenst\u00e4nde, soweit im Erzeugerprozess verwendet": {}, "account_type": "Cash"
"Geringwertige Verm\u00f6gensgegenst\u00e4nde, soweit nicht im Erzeugungsprozess verwendet": {}, },
"Gesch\u00e4fts(Firmen)wert": {}, "2900 Aktive Rechnungsabrenzungsposten": {
"Grundst\u00fcckseinrichtunten auf eigenem Grund": {}, "account_type": "Receivable"
"Grundst\u00fcckseinrichtunten auf fremdem Grund": {}, },
"Grundst\u00fccksgleiche Rechte": {}, "2600 Anteile an verbundenen Unternehmen": {
"Hebezeuge und Montageanlagen": {}, "account_type": "Equity"
"Konzessionen": {}, },
"Kumulierte Abschreibungen": {}, "2680 Besitzwechsel ohne Forderungen": {
"LKW": {}, "account_type": "Receivable"
"Marken, Warenzeichen und Musterschutzrechte": {}, },
"Maschinenwerkzeuge": {}, "2950 Aktiviertes Disagio": {
"Nachrichten- und Kontrollanlagen": {}, "account_type": "Receivable"
"PKW": {}, },
"Pacht- und Mietrechte": {}, "2610 Eigene Anteile und Wertpapiere an mit Mehrheit beteiligten Unternehmen": {
"Patentrechte und Lizenzen": {}, "account_type": "Receivable"
"Sonstige Ausleihungen": {}, },
"Sonstige Beteiligungen": {}, "2570 Einfuhrumsatzsteuer (bezahlt)": {"account_type": "Tax"},
"Transportanlagen": {},
"Unbebaute Grundst\u00fccke": {}, "2460 Eingeforderte aber noch nicht eingezahlte Einlagen": {
"Vorrichtungen, Formen und Modelle": {}, "account_type": "Receivable"
"Wohn- und Sozialgeb\u00e4ude auf eigenem Grund": {}, },
"Wohn- und Sozialgeb\u00e4ude auf fremdem Grund": {}, "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" "root_type": "Asset"
}, },
"Summe Personalaufwand": { "Klasse 4: Betriebliche Erträge": {
"6000 bis 6190 L\u00f6hne": {}, "4000 Erlöse 20 %": {"account_type": "Income Account"},
"6200 bis 6390 Geh\u00e4lter": {}, "4020 Erl\u00f6se 0 % steuerbefreit": {"account_type": "Income Account"},
"6400 bis 6440 Aufwendungen f\u00fcr Abfertigungen": {}, "4010 Erl\u00f6se 10 %": {"account_type": "Income Account"},
"6450 bis 6490 Aufwendungen f\u00fcr Altersversorgung": {}, "4030 Erl\u00f6se 13 %": {"account_type": "Income Account"},
"6500 bis 6550 Gesetzlicher Sozialaufwand Arbeiter": {}, "4040 Erl\u00f6se 0 % innergemeinschaftliche Lieferungen": {"account_type": "Income Account"},
"6560 bis 6590 Gesetzlicher Sozialaufwand Angestellte": {}, "4400 Erl\u00f6sreduktion 0 % steuerbefreit": {"account_type": "Expense Account"},
"6600 bis 6650 Lohnabh\u00e4ngige Abgaben und Pflichtbeitr\u00e4gte": {}, "4410 Erl\u00f6sreduktion 10 %": {"account_type": "Expense Account"},
"6660 bis 6690 Gehaltsabh\u00e4ngige Abgaben und Pflichtbeitr\u00e4gte": {}, "4420 Erl\u00f6sreduktion 20 %": {"account_type": "Expense Account"},
"6700 bis 6890 Sonstige Sozialaufwendungen": {}, "4430 Erl\u00f6sreduktion 13 %": {"account_type": "Expense Account"},
"Aufwandsstellenrechnung": {}, "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" "root_type": "Expense"
}, },
"Summe Umlaufverm\u00f6gen": { "Klasse 6: Personalaufwand": {
"2000 bis 2007 Forderungen aus Lief. und Leist. Inland": { "6000 L\u00f6hne": {"account_type": "Payable"},
"account_type": "Receivable" "6200 Geh\u00e4lter": {"account_type": "Payable"},
}, "6400 Aufwendungen f\u00fcr Abfertigungen": {"account_type": "Payable"},
"2100 bis 2120 Forderungen aus Lief. und Leist. EU": { "6450 Aufwendungen f\u00fcr Altersversorgung": {"account_type": "Payable"},
"account_type": "Receivable" "6500 Gesetzlicher Sozialaufwand Arbeiter": {"account_type": "Payable"},
}, "6560 Gesetzlicher Sozialaufwand Angestellte": {"account_type": "Payable"},
"2150 bis 2170 Forderungen aus Lief. und Leist. Ausland": { "6600 Lohnabh\u00e4ngige Abgaben und Pflichtbeitr\u00e4gte": {"account_type": "Payable"},
"account_type": "Receivable" "6660 Gehaltsabh\u00e4ngige Abgaben und Pflichtbeitr\u00e4gte": {"account_type": "Payable"},
}, "6700 Sonstige Sozialaufwendungen": {"account_type": "Payable"},
"2200 bis 2220 Forderungen gegen\u00fcber verbundenen Unternehmen": { "6900 Aufwandsstellenrechnung Personal": {"account_type": "Payable"},
"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": {},
"root_type": "Expense" "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"
} }
} }
} }

View File

@ -4,6 +4,23 @@
frappe.ui.form.on("Bank Clearance", { frappe.ui.form.on("Bank Clearance", {
setup: function(frm) { setup: function(frm) {
frm.add_fetch("account", "account_currency", "account_currency"); frm.add_fetch("account", "account_currency", "account_currency");
frm.set_query("account", function() {
return {
"filters": {
"account_type": ["in",["Bank","Cash"]],
"is_group": 0,
}
};
});
frm.set_query("bank_account", function () {
return {
filters: {
'is_company_account': 1
},
};
});
}, },
onload: function(frm) { onload: function(frm) {
@ -12,14 +29,7 @@ frappe.ui.form.on("Bank Clearance", {
locals[":Company"][frappe.defaults.get_user_default("Company")]["default_bank_account"]: ""; locals[":Company"][frappe.defaults.get_user_default("Company")]["default_bank_account"]: "";
frm.set_value("account", default_bank_account); frm.set_value("account", default_bank_account);
frm.set_query("account", function() {
return {
"filters": {
"account_type": ["in",["Bank","Cash"]],
"is_group": 0
}
};
});
frm.set_value("from_date", frappe.datetime.month_start()); frm.set_value("from_date", frappe.datetime.month_start());
frm.set_value("to_date", frappe.datetime.month_end()); frm.set_value("to_date", frappe.datetime.month_end());

View File

@ -1,6 +1,7 @@
{ {
"actions": [], "actions": [],
"allow_import": 1, "allow_import": 1,
"autoname": "naming_series:",
"creation": "2016-05-16 11:42:29.632528", "creation": "2016-05-16 11:42:29.632528",
"doctype": "DocType", "doctype": "DocType",
"editable_grid": 1, "editable_grid": 1,
@ -9,6 +10,7 @@
"budget_against", "budget_against",
"company", "company",
"cost_center", "cost_center",
"naming_series",
"project", "project",
"fiscal_year", "fiscal_year",
"column_break_3", "column_break_3",
@ -190,15 +192,26 @@
"label": "Budget Accounts", "label": "Budget Accounts",
"options": "Budget Account", "options": "Budget Account",
"reqd": 1 "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, "index_web_pages_for_search": 1,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2020-10-06 15:13:54.055854", "modified": "2022-10-10 22:14:36.361509",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Budget", "name": "Budget",
"naming_rule": "By \"Naming Series\" field",
"owner": "Administrator", "owner": "Administrator",
"permissions": [ "permissions": [
{ {
@ -220,5 +233,6 @@
], ],
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"states": [],
"track_changes": 1 "track_changes": 1
} }

View File

@ -5,7 +5,6 @@
import frappe import frappe
from frappe import _ from frappe import _
from frappe.model.document import Document 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 frappe.utils import add_months, flt, fmt_money, get_last_day, getdate
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
@ -23,11 +22,6 @@ class DuplicateBudgetError(frappe.ValidationError):
class Budget(Document): class Budget(Document):
def autoname(self):
self.name = make_autoname(
self.get(frappe.scrub(self.budget_against)) + "/" + self.fiscal_year + "/.###"
)
def validate(self): def validate(self):
if not self.get(frappe.scrub(self.budget_against)): if not self.get(frappe.scrub(self.budget_against)):
frappe.throw(_("{0} is mandatory").format(self.budget_against)) frappe.throw(_("{0} is mandatory").format(self.budget_against))
@ -109,8 +103,11 @@ class Budget(Document):
): ):
self.applicable_on_booking_actual_expenses = 1 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):
def validate_expense_against_budget(args, expense_amount=0):
args = frappe._dict(args) args = frappe._dict(args)
if args.get("company") and not args.fiscal_year: if args.get("company") and not args.fiscal_year:
@ -178,13 +175,13 @@ def validate_expense_against_budget(args):
) # nosec ) # nosec
if budget_records: if budget_records:
validate_budget_records(args, budget_records) validate_budget_records(args, budget_records, expense_amount)
def validate_budget_records(args, budget_records): def validate_budget_records(args, budget_records, expense_amount):
for budget in budget_records: for budget in budget_records:
if flt(budget.budget_amount): if flt(budget.budget_amount):
amount = get_amount(args, budget) amount = expense_amount or get_amount(args, budget)
yearly_action, monthly_action = get_actions(args, budget) yearly_action, monthly_action = get_actions(args, budget)
if monthly_action in ["Stop", "Warn"]: if monthly_action in ["Stop", "Warn"]:

View File

@ -334,6 +334,39 @@ class TestBudget(unittest.TestCase):
budget.cancel() budget.cancel()
jv.cancel() jv.cancel()
def test_monthly_budget_against_main_cost_center(self):
from erpnext.accounts.doctype.cost_center.test_cost_center import create_cost_center
from erpnext.accounts.doctype.cost_center_allocation.test_cost_center_allocation import (
create_cost_center_allocation,
)
cost_centers = [
"Main Budget Cost Center 1",
"Sub Budget Cost Center 1",
"Sub Budget Cost Center 2",
]
for cc in cost_centers:
create_cost_center(cost_center_name=cc, company="_Test Company")
create_cost_center_allocation(
"_Test Company",
"Main Budget Cost Center 1 - _TC",
{"Sub Budget Cost Center 1 - _TC": 60, "Sub Budget Cost Center 2 - _TC": 40},
)
make_budget(budget_against="Cost Center", cost_center="Main Budget Cost Center 1 - _TC")
jv = make_journal_entry(
"_Test Account Cost for Goods Sold - _TC",
"_Test Bank - _TC",
400000,
"Main Budget Cost Center 1 - _TC",
posting_date=nowdate(),
)
self.assertRaises(BudgetError, jv.submit)
def set_total_expense_zero(posting_date, budget_against_field=None, budget_against_CC=None): def set_total_expense_zero(posting_date, budget_against_field=None, budget_against_CC=None):
if budget_against_field == "project": if budget_against_field == "project":

View File

@ -173,8 +173,8 @@ frappe.ui.form.on("Journal Entry", {
var update_jv_details = function(doc, r) { var update_jv_details = function(doc, r) {
$.each(r, function(i, d) { $.each(r, function(i, d) {
var row = frappe.model.add_child(doc, "Journal Entry Account", "accounts"); var row = frappe.model.add_child(doc, "Journal Entry Account", "accounts");
row.account = d.account; frappe.model.set_value(row.doctype, row.name, "account", d.account)
row.balance = d.balance; frappe.model.set_value(row.doctype, row.name, "balance", d.balance)
}); });
refresh_field("accounts"); refresh_field("accounts");
} }
@ -312,8 +312,7 @@ erpnext.accounts.JournalEntry = class JournalEntry extends frappe.ui.form.Contro
} }
} }
get_outstanding(doctype, docname, company, child, due_date) { get_outstanding(doctype, docname, company, child) {
var me = this;
var args = { var args = {
"doctype": doctype, "doctype": doctype,
"docname": docname, "docname": docname,

View File

@ -1210,6 +1210,7 @@ def get_outstanding(args):
args = json.loads(args) args = json.loads(args)
company_currency = erpnext.get_company_currency(args.get("company")) company_currency = erpnext.get_company_currency(args.get("company"))
due_date = None
if args.get("doctype") == "Journal Entry": if args.get("doctype") == "Journal Entry":
condition = " and party=%(party)s" if args.get("party") else "" condition = " and party=%(party)s" if args.get("party") else ""
@ -1234,10 +1235,12 @@ def get_outstanding(args):
invoice = frappe.db.get_value( invoice = frappe.db.get_value(
args["doctype"], args["doctype"],
args["docname"], args["docname"],
["outstanding_amount", "conversion_rate", scrub(party_type)], ["outstanding_amount", "conversion_rate", scrub(party_type), "due_date"],
as_dict=1, as_dict=1,
) )
due_date = invoice.get("due_date")
exchange_rate = ( exchange_rate = (
invoice.conversion_rate if (args.get("account_currency") != company_currency) else 1 invoice.conversion_rate if (args.get("account_currency") != company_currency) else 1
) )
@ -1260,6 +1263,7 @@ def get_outstanding(args):
"exchange_rate": exchange_rate, "exchange_rate": exchange_rate,
"party_type": party_type, "party_type": party_type,
"party": invoice.get(scrub(party_type)), "party": invoice.get(scrub(party_type)),
"reference_due_date": due_date,
} }

View File

@ -202,6 +202,7 @@
"fieldname": "reference_type", "fieldname": "reference_type",
"fieldtype": "Select", "fieldtype": "Select",
"label": "Reference Type", "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" "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", "fieldtype": "Dynamic Link",
"in_list_view": 1, "in_list_view": 1,
"label": "Reference Name", "label": "Reference Name",
"no_copy": 1,
"options": "reference_type" "options": "reference_type"
}, },
{ {
"depends_on": "eval:doc.reference_type&&!in_list(doc.reference_type, ['Expense Claim', 'Asset', 'Employee Loan', 'Employee Advance'])", "depends_on": "eval:doc.reference_type&&!in_list(doc.reference_type, ['Expense Claim', 'Asset', 'Employee Loan', 'Employee Advance'])",
"fieldname": "reference_due_date", "fieldname": "reference_due_date",
"fieldtype": "Select", "fieldtype": "Date",
"label": "Reference Due Date" "label": "Reference Due Date",
"no_copy": 1
}, },
{ {
"fieldname": "project", "fieldname": "project",
@ -274,19 +277,22 @@
"fieldname": "reference_detail_no", "fieldname": "reference_detail_no",
"fieldtype": "Data", "fieldtype": "Data",
"hidden": 1, "hidden": 1,
"label": "Reference Detail No" "label": "Reference Detail No",
"no_copy": 1
} }
], ],
"idx": 1, "idx": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2021-08-30 21:27:32.200299", "modified": "2022-10-26 20:03:10.906259",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Journal Entry Account", "name": "Journal Entry Account",
"naming_rule": "Random",
"owner": "Administrator", "owner": "Administrator",
"permissions": [], "permissions": [],
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"states": [],
"track_changes": 1 "track_changes": 1
} }

View File

@ -22,13 +22,13 @@ frappe.ui.form.on('Opening Invoice Creation Tool', {
} }
if (data.user != frappe.session.user) return; if (data.user != frappe.session.user) return;
if (data.count == data.total) { if (data.count == data.total) {
setTimeout((title) => { setTimeout(() => {
frm.doc.import_in_progress = false; frm.doc.import_in_progress = false;
frm.clear_table("invoices"); frm.clear_table("invoices");
frm.refresh_fields(); frm.refresh_fields();
frm.page.clear_indicator(); frm.page.clear_indicator();
frm.dashboard.hide_progress(title); frm.dashboard.hide_progress();
frappe.msgprint(__("Opening {0} Invoice created", [frm.doc.invoice_type])); frappe.msgprint(__("Opening {0} Invoices created", [frm.doc.invoice_type]));
}, 1500, data.title); }, 1500, data.title);
return; return;
} }
@ -51,13 +51,6 @@ frappe.ui.form.on('Opening Invoice Creation Tool', {
method: "make_invoices", method: "make_invoices",
freeze: 1, freeze: 1,
freeze_message: __("Creating {0} Invoice", [frm.doc.invoice_type]), freeze_message: __("Creating {0} Invoice", [frm.doc.invoice_type]),
callback: function(r) {
if (r.message.length == 1) {
frappe.msgprint(__("{0} Invoice created successfully.", [frm.doc.invoice_type]));
} else if (r.message.length < 50) {
frappe.msgprint(__("{0} Invoices created successfully.", [frm.doc.invoice_type]));
}
}
}); });
}); });

View File

@ -255,8 +255,6 @@ def start_import(invoices):
def publish(index, total, doctype): def publish(index, total, doctype):
if total < 50:
return
frappe.publish_realtime( frappe.publish_realtime(
"opening_invoice_creation_progress", "opening_invoice_creation_progress",
dict( dict(

View File

@ -995,7 +995,9 @@ class PaymentEntry(AccountsController):
if self.payment_type in ("Receive", "Pay") and self.party: if self.payment_type in ("Receive", "Pay") and self.party:
for d in self.get("references"): for d in self.get("references"):
if d.allocated_amount and d.reference_doctype in frappe.get_hooks("advance_payment_doctypes"): 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): def on_recurring(self, reference_doc, auto_repeat_doc):
self.reference_no = reference_doc.name self.reference_no = reference_doc.name

View File

@ -3,12 +3,13 @@
import frappe import frappe
from frappe import qb from frappe import qb
from frappe.tests.utils import FrappeTestCase from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import nowdate from frappe.utils import nowdate
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry from erpnext.accounts.doctype.payment_entry.test_payment_entry import create_payment_entry
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
from erpnext.stock.doctype.item.test_item import create_item from erpnext.stock.doctype.item.test_item import create_item
@ -127,6 +128,25 @@ class TestPaymentLedgerEntry(FrappeTestCase):
payment.posting_date = posting_date payment.posting_date = posting_date
return payment return payment
def create_sales_order(
self, qty=1, rate=100, posting_date=nowdate(), do_not_save=False, do_not_submit=False
):
so = make_sales_order(
company=self.company,
transaction_date=posting_date,
customer=self.customer,
item_code=self.item,
cost_center=self.cost_center,
warehouse=self.warehouse,
debit_to=self.debit_to,
currency="INR",
qty=qty,
rate=100,
do_not_save=do_not_save,
do_not_submit=do_not_submit,
)
return so
def clear_old_entries(self): def clear_old_entries(self):
doctype_list = [ doctype_list = [
"GL Entry", "GL Entry",
@ -406,3 +426,89 @@ class TestPaymentLedgerEntry(FrappeTestCase):
] ]
self.assertEqual(pl_entries_for_crnote[0], expected_values[0]) self.assertEqual(pl_entries_for_crnote[0], expected_values[0])
self.assertEqual(pl_entries_for_crnote[1], expected_values[1]) self.assertEqual(pl_entries_for_crnote[1], expected_values[1])
@change_settings(
"Accounts Settings",
{"unlink_payment_on_cancellation_of_invoice": 1, "delete_linked_ledger_entries": 1},
)
def test_multi_payment_unlink_on_invoice_cancellation(self):
transaction_date = nowdate()
amount = 100
si = self.create_sales_invoice(qty=1, rate=amount, posting_date=transaction_date)
for amt in [40, 40, 20]:
# payment 1
pe = get_payment_entry(si.doctype, si.name)
pe.paid_amount = amt
pe.get("references")[0].allocated_amount = amt
pe = pe.save().submit()
si.reload()
si.cancel()
entries = frappe.db.get_list(
"Payment Ledger Entry",
filters={"against_voucher_type": si.doctype, "against_voucher_no": si.name, "delinked": 0},
)
self.assertEqual(entries, [])
# with references removed, deletion should be possible
si.delete()
self.assertRaises(frappe.DoesNotExistError, frappe.get_doc, si.doctype, si.name)
@change_settings(
"Accounts Settings",
{"unlink_payment_on_cancellation_of_invoice": 1, "delete_linked_ledger_entries": 1},
)
def test_multi_je_unlink_on_invoice_cancellation(self):
transaction_date = nowdate()
amount = 100
si = self.create_sales_invoice(qty=1, rate=amount, posting_date=transaction_date)
# multiple JE's against invoice
for amt in [40, 40, 20]:
je1 = self.create_journal_entry(
self.income_account, self.debit_to, amt, posting_date=transaction_date
)
je1.get("accounts")[1].party_type = "Customer"
je1.get("accounts")[1].party = self.customer
je1.get("accounts")[1].reference_type = si.doctype
je1.get("accounts")[1].reference_name = si.name
je1 = je1.save().submit()
si.reload()
si.cancel()
entries = frappe.db.get_list(
"Payment Ledger Entry",
filters={"against_voucher_type": si.doctype, "against_voucher_no": si.name, "delinked": 0},
)
self.assertEqual(entries, [])
# with references removed, deletion should be possible
si.delete()
self.assertRaises(frappe.DoesNotExistError, frappe.get_doc, si.doctype, si.name)
@change_settings(
"Accounts Settings",
{"unlink_payment_on_cancellation_of_invoice": 1, "delete_linked_ledger_entries": 1},
)
def test_advance_payment_unlink_on_order_cancellation(self):
transaction_date = nowdate()
amount = 100
so = self.create_sales_order(qty=1, rate=amount, posting_date=transaction_date).save().submit()
pe = get_payment_entry(so.doctype, so.name).save().submit()
so.reload()
so.cancel()
entries = frappe.db.get_list(
"Payment Ledger Entry",
filters={"against_voucher_type": so.doctype, "against_voucher_no": so.name, "delinked": 0},
)
self.assertEqual(entries, [])
# with references removed, deletion should be possible
so.delete()
self.assertRaises(frappe.DoesNotExistError, frappe.get_doc, so.doctype, so.name)

View File

@ -343,7 +343,8 @@
"no_copy": 1, "no_copy": 1,
"options": "POS Invoice", "options": "POS Invoice",
"print_hide": 1, "print_hide": 1,
"read_only": 1 "read_only": 1,
"search_index": 1
}, },
{ {
"default": "0", "default": "0",
@ -1553,7 +1554,7 @@
"icon": "fa fa-file-text", "icon": "fa fa-file-text",
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2022-09-27 13:00:24.166684", "modified": "2022-09-30 03:49:50.455199",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "POS Invoice", "name": "POS Invoice",

View File

@ -743,7 +743,3 @@ def add_return_modes(doc, pos_profile):
]: ]:
payment_mode = get_mode_of_payment_info(mode_of_payment, doc.company) payment_mode = get_mode_of_payment_info(mode_of_payment, doc.company)
append_payment(payment_mode[0]) append_payment(payment_mode[0])
def on_doctype_update():
frappe.db.add_index("POS Invoice", ["return_against"])

View File

@ -495,6 +495,67 @@ class TestPOSInvoice(unittest.TestCase):
self.assertRaises(frappe.ValidationError, pos.submit) 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): def test_loyalty_points(self):
from erpnext.accounts.doctype.loyalty_program.loyalty_program import ( from erpnext.accounts.doctype.loyalty_program.loyalty_program import (
get_loyalty_program_details_with_points, get_loyalty_program_details_with_points,

View File

@ -52,7 +52,10 @@
"free_item_rate", "free_item_rate",
"column_break_42", "column_break_42",
"free_item_uom", "free_item_uom",
"round_free_qty",
"is_recursive", "is_recursive",
"recurse_for",
"apply_recursion_over",
"section_break_23", "section_break_23",
"valid_from", "valid_from",
"valid_upto", "valid_upto",
@ -578,12 +581,34 @@
"fieldtype": "Select", "fieldtype": "Select",
"label": "Naming Series", "label": "Naming Series",
"options": "PRLE-.####" "options": "PRLE-.####"
},
{
"default": "0",
"fieldname": "round_free_qty",
"fieldtype": "Check",
"label": "Round Free Qty"
},
{
"depends_on": "is_recursive",
"description": "Give free item for every N quantity",
"fieldname": "recurse_for",
"fieldtype": "Float",
"label": "Recurse Every (As Per Transaction UOM)",
"mandatory_depends_on": "is_recursive"
},
{
"default": "0",
"depends_on": "is_recursive",
"description": "Qty for which recursion isn't applicable.",
"fieldname": "apply_recursion_over",
"fieldtype": "Float",
"label": "Apply Recursion Over (As Per Transaction UOM)"
} }
], ],
"icon": "fa fa-gift", "icon": "fa fa-gift",
"idx": 1, "idx": 1,
"links": [], "links": [],
"modified": "2022-09-16 16:00:38.356266", "modified": "2022-10-13 19:05:35.056304",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Pricing Rule", "name": "Pricing Rule",

View File

@ -24,6 +24,7 @@ class PricingRule(Document):
self.validate_applicable_for_selling_or_buying() self.validate_applicable_for_selling_or_buying()
self.validate_min_max_amt() self.validate_min_max_amt()
self.validate_min_max_qty() self.validate_min_max_qty()
self.validate_recursion()
self.cleanup_fields_value() self.cleanup_fields_value()
self.validate_rate_or_discount() self.validate_rate_or_discount()
self.validate_max_discount() self.validate_max_discount()
@ -109,6 +110,18 @@ class PricingRule(Document):
if self.min_amt and self.max_amt and flt(self.min_amt) > flt(self.max_amt): if self.min_amt and self.max_amt and flt(self.min_amt) > flt(self.max_amt):
throw(_("Min Amt can not be greater than Max Amt")) throw(_("Min Amt can not be greater than Max Amt"))
def validate_recursion(self):
if self.price_or_product_discount != "Product":
return
if self.free_item or self.same_item:
if flt(self.recurse_for) <= 0:
self.recurse_for = 1
if self.is_recursive:
if flt(self.apply_recursion_over) > flt(self.min_qty):
throw(_("Min Qty should be greater than Recurse Over Qty"))
if flt(self.apply_recursion_over) < 0:
throw(_("Recurse Over Qty cannot be less than 0"))
def cleanup_fields_value(self): def cleanup_fields_value(self):
for logic_field in ["apply_on", "applicable_for", "rate_or_discount"]: for logic_field in ["apply_on", "applicable_for", "rate_or_discount"]:
fieldname = frappe.scrub(self.get(logic_field) or "") fieldname = frappe.scrub(self.get(logic_field) or "")
@ -268,6 +281,18 @@ def get_serial_no_for_item(args):
return item_details 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): def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=False):
from erpnext.accounts.doctype.pricing_rule.utils import ( from erpnext.accounts.doctype.pricing_rule.utils import (
get_applied_pricing_rules, get_applied_pricing_rules,
@ -324,6 +349,7 @@ def get_pricing_rule_for_item(args, price_list_rate=0, doc=None, for_validate=Fa
if isinstance(pricing_rule, str): if isinstance(pricing_rule, str):
pricing_rule = frappe.get_cached_doc("Pricing Rule", pricing_rule) 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 [] pricing_rule.apply_rule_on_other_items = get_pricing_rule_items(pricing_rule) or []
if pricing_rule.get("suggestion"): if pricing_rule.get("suggestion"):
@ -440,12 +466,15 @@ def apply_price_discount_rule(pricing_rule, item_details, args):
if pricing_rule.currency == args.currency: if pricing_rule.currency == args.currency:
pricing_rule_rate = pricing_rule.rate pricing_rule_rate = pricing_rule.rate
# TODO https://github.com/frappe/erpnext/pull/23636 solve this in some other way.
if pricing_rule_rate: if pricing_rule_rate:
is_blank_uom = pricing_rule.get("uom") != args.get("uom")
# Override already set price list rate (from item price) # Override already set price list rate (from item price)
# if pricing_rule_rate > 0 # if pricing_rule_rate > 0
item_details.update( 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}) item_details.update({"discount_percentage": 0.0})

View File

@ -595,6 +595,247 @@ class TestPricingRule(unittest.TestCase):
frappe.get_doc("Item Price", {"item_code": "Water Flask"}).delete() frappe.get_doc("Item Price", {"item_code": "Water Flask"}).delete()
item.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_item_group_price_with_blank_uom_pricing_rule(self):
group = frappe.get_doc(doctype="Item Group", item_group_name="_Test Pricing Rule Item Group")
group.save()
properties = {
"item_code": "Item with Group Blank UOM",
"item_group": "_Test Pricing Rule Item Group",
"stock_uom": "Nos",
"sales_uom": "Box",
"uoms": [dict(uom="Box", conversion_factor=10)],
}
item = make_item(properties=properties)
make_item_price("Item with Group Blank UOM", "_Test Price List", 100)
pricing_rule_record = {
"doctype": "Pricing Rule",
"title": "_Test Item with Group Blank UOM Rule",
"apply_on": "Item Group",
"item_groups": [
{
"item_group": "_Test Pricing Rule Item Group",
}
],
"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 with Group 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 with Group 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 with Group Blank UOM"}).delete()
item.delete()
group.delete()
def test_item_group_price_with_selling_uom_pricing_rule(self):
group = frappe.get_doc(doctype="Item Group", item_group_name="_Test Pricing Rule Item Group UOM")
group.save()
properties = {
"item_code": "Item with Group UOM other than Stock",
"item_group": "_Test Pricing Rule Item Group UOM",
"stock_uom": "Nos",
"sales_uom": "Box",
"uoms": [dict(uom="Box", conversion_factor=10)],
}
item = make_item(properties=properties)
make_item_price("Item with Group UOM other than Stock", "_Test Price List", 100)
pricing_rule_record = {
"doctype": "Pricing Rule",
"title": "_Test Item with Group UOM other than Stock Rule",
"apply_on": "Item Group",
"item_groups": [
{
"item_group": "_Test Pricing Rule Item Group UOM",
"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 with Group 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 with Group 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 with Group UOM other than Stock"}).delete()
item.delete()
group.delete()
def test_pricing_rule_for_different_currency(self): def test_pricing_rule_for_different_currency(self):
make_item("Test Sanitizer Item") make_item("Test Sanitizer Item")
@ -828,6 +1069,45 @@ class TestPricingRule(unittest.TestCase):
si.delete() si.delete()
rule.delete() rule.delete()
def test_pricing_rule_for_product_free_item_rounded_qty_and_recursion(self):
frappe.delete_doc_if_exists("Pricing Rule", "_Test Pricing Rule")
test_record = {
"doctype": "Pricing Rule",
"title": "_Test Pricing Rule",
"apply_on": "Item Code",
"currency": "USD",
"items": [
{
"item_code": "_Test Item",
}
],
"selling": 1,
"rate": 0,
"min_qty": 3,
"max_qty": 7,
"price_or_product_discount": "Product",
"same_item": 1,
"free_qty": 1,
"round_free_qty": 1,
"is_recursive": 1,
"recurse_for": 2,
"company": "_Test Company",
}
frappe.get_doc(test_record.copy()).insert()
# With pricing rule
so = make_sales_order(item_code="_Test Item", qty=5)
so.load_from_db()
self.assertEqual(so.items[1].is_free_item, 1)
self.assertEqual(so.items[1].item_code, "_Test Item")
self.assertEqual(so.items[1].qty, 2)
so = make_sales_order(item_code="_Test Item", qty=7)
so.load_from_db()
self.assertEqual(so.items[1].is_free_item, 1)
self.assertEqual(so.items[1].item_code, "_Test Item")
self.assertEqual(so.items[1].qty, 4)
test_dependencies = ["Campaign"] test_dependencies = ["Campaign"]

View File

@ -111,6 +111,12 @@ def _get_pricing_rules(apply_on, args, values):
) )
if apply_on_field == "item_code": 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: if "variant_of" not in args:
args.variant_of = frappe.get_cached_value("Item", args.item_code, "variant_of") args.variant_of = frappe.get_cached_value("Item", args.item_code, "variant_of")
@ -121,6 +127,12 @@ def _get_pricing_rules(apply_on, args, values):
values["variant_of"] = args.variant_of values["variant_of"] = args.variant_of
elif apply_on_field == "item_group": elif apply_on_field == "item_group":
item_conditions = _get_tree_conditions(args, "Item Group", child_doc, False) item_conditions = _get_tree_conditions(args, "Item Group", child_doc, False)
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")
)
)
conditions += get_other_conditions(conditions, values, args) conditions += get_other_conditions(conditions, values, args)
warehouse_conditions = _get_tree_conditions(args, "Warehouse", "`tabPricing Rule`") warehouse_conditions = _get_tree_conditions(args, "Warehouse", "`tabPricing Rule`")
@ -621,9 +633,13 @@ def get_product_discount_rule(pricing_rule, item_details, args=None, doc=None):
qty = pricing_rule.free_qty or 1 qty = pricing_rule.free_qty or 1
if pricing_rule.is_recursive: if pricing_rule.is_recursive:
transaction_qty = args.get("qty") if args else doc.total_qty transaction_qty = (
args.get("qty") if args else doc.total_qty
) - pricing_rule.apply_recursion_over
if transaction_qty: if transaction_qty:
qty = flt(transaction_qty) * qty qty = flt(transaction_qty) * qty / pricing_rule.recurse_for
if pricing_rule.round_free_qty:
qty = round(qty)
free_item_data_args = { free_item_data_args = {
"item_code": free_item, "item_code": free_item,

View File

@ -9,6 +9,7 @@ frappe.ui.form.on('Process Statement Of Accounts', {
refresh: function(frm){ refresh: function(frm){
if(!frm.doc.__islocal) { if(!frm.doc.__islocal) {
frm.add_custom_button(__('Send Emails'), function(){ frm.add_custom_button(__('Send Emails'), function(){
if (frm.is_dirty()) frappe.throw(__("Please save before proceeding."))
frappe.call({ frappe.call({
method: "erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.send_emails", method: "erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.send_emails",
args: { args: {
@ -25,7 +26,8 @@ frappe.ui.form.on('Process Statement Of Accounts', {
}); });
}); });
frm.add_custom_button(__('Download'), function(){ frm.add_custom_button(__('Download'), function(){
var url = frappe.urllib.get_full_url( if (frm.is_dirty()) frappe.throw(__("Please save before proceeding."))
let url = frappe.urllib.get_full_url(
'/api/method/erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.download_statements?' '/api/method/erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.download_statements?'
+ 'document_name='+encodeURIComponent(frm.doc.name)) + 'document_name='+encodeURIComponent(frm.doc.name))
$.ajax({ $.ajax({

View File

@ -27,6 +27,7 @@
"customers", "customers",
"preferences", "preferences",
"orientation", "orientation",
"include_break",
"include_ageing", "include_ageing",
"ageing_based_on", "ageing_based_on",
"section_break_14", "section_break_14",
@ -284,10 +285,16 @@
"fieldtype": "Link", "fieldtype": "Link",
"label": "Terms and Conditions", "label": "Terms and Conditions",
"options": "Terms and Conditions" "options": "Terms and Conditions"
},
{
"default": "1",
"fieldname": "include_break",
"fieldtype": "Check",
"label": "Page Break After Each SoA"
} }
], ],
"links": [], "links": [],
"modified": "2021-09-06 21:00:45.732505", "modified": "2022-10-17 17:47:08.662475",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Process Statement Of Accounts", "name": "Process Statement Of Accounts",
@ -321,5 +328,6 @@
], ],
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"states": [],
"track_changes": 1 "track_changes": 1
} }

View File

@ -6,6 +6,7 @@ import copy
import frappe import frappe
from frappe import _ from frappe import _
from frappe.desk.reportview import get_match_cond
from frappe.model.document import Document from frappe.model.document import Document
from frappe.utils import add_days, add_months, format_date, getdate, today from frappe.utils import add_days, add_months, format_date, getdate, today
from frappe.utils.jinja import validate_template from frappe.utils.jinja import validate_template
@ -128,7 +129,8 @@ def get_report_pdf(doc, consolidated=True):
if not bool(statement_dict): if not bool(statement_dict):
return False return False
elif consolidated: elif consolidated:
result = "".join(list(statement_dict.values())) delimiter = '<div style="page-break-before: always;"></div>' if doc.include_break else ""
result = delimiter.join(list(statement_dict.values()))
return get_pdf(result, {"orientation": doc.orientation}) return get_pdf(result, {"orientation": doc.orientation})
else: else:
for customer, statement_html in statement_dict.items(): for customer, statement_html in statement_dict.items():
@ -240,8 +242,6 @@ def fetch_customers(customer_collection, collection_name, primary_mandatory):
if int(primary_mandatory): if int(primary_mandatory):
if primary_email == "": if primary_email == "":
continue continue
elif (billing_email == "") and (primary_email == ""):
continue
customer_list.append( customer_list.append(
{"name": customer.name, "primary_email": primary_email, "billing_email": billing_email} {"name": customer.name, "primary_email": primary_email, "billing_email": billing_email}
@ -273,8 +273,12 @@ def get_customer_emails(customer_name, primary_mandatory, billing_and_primary=Tr
link.link_doctype='Customer' link.link_doctype='Customer'
and link.link_name=%s and link.link_name=%s
and contact.is_billing_contact=1 and contact.is_billing_contact=1
{mcond}
ORDER BY ORDER BY
contact.creation desc""", contact.creation desc
""".format(
mcond=get_match_cond("Contact")
),
customer_name, customer_name,
) )
@ -313,6 +317,8 @@ def send_emails(document_name, from_scheduler=False):
attachments = [{"fname": customer + ".pdf", "fcontent": report_pdf}] attachments = [{"fname": customer + ".pdf", "fcontent": report_pdf}]
recipients, cc = get_recipients_and_cc(customer, doc) recipients, cc = get_recipients_and_cc(customer, doc)
if not recipients:
continue
context = get_context(customer, doc) context = get_context(customer, doc)
subject = frappe.render_template(doc.subject, context) subject = frappe.render_template(doc.subject, context)
message = frappe.render_template(doc.body, context) message = frappe.render_template(doc.body, context)

View File

@ -31,7 +31,7 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying.
super.onload(); super.onload();
// Ignore linked advances // 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) { if(!this.frm.doc.__islocal) {
// show credit_to in print format // show credit_to in print format
@ -569,6 +569,10 @@ frappe.ui.form.on("Purchase Invoice", {
erpnext.queries.setup_queries(frm, "Warehouse", function() { erpnext.queries.setup_queries(frm, "Warehouse", function() {
return erpnext.queries.warehouse(frm.doc); return erpnext.queries.warehouse(frm.doc);
}); });
if (frm.is_new()) {
frm.clear_table("tax_withheld_vouchers");
}
}, },
is_subcontracted: function(frm) { is_subcontracted: function(frm) {

File diff suppressed because it is too large Load Diff

View File

@ -71,6 +71,9 @@ class PurchaseInvoice(BuyingController):
supplier_tds = frappe.db.get_value("Supplier", self.supplier, "tax_withholding_category") supplier_tds = frappe.db.get_value("Supplier", self.supplier, "tax_withholding_category")
self.set_onload("supplier_tds", supplier_tds) self.set_onload("supplier_tds", supplier_tds)
if self.is_new():
self.set("tax_withheld_vouchers", [])
def before_save(self): def before_save(self):
if not self.on_hold: if not self.on_hold:
self.release_date = "" self.release_date = ""
@ -705,6 +708,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 # Intentionally passed negative debit amount to avoid incorrect GL Entry validation
gl_entries.append( gl_entries.append(
self.get_gl_dict( self.get_gl_dict(
@ -714,7 +721,7 @@ class PurchaseInvoice(BuyingController):
"cost_center": item.cost_center, "cost_center": item.cost_center,
"project": item.project or self.project, "project": item.project or self.project,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"), "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"], warehouse_account[item.from_warehouse]["account_currency"],
item=item, item=item,
@ -1411,6 +1418,7 @@ class PurchaseInvoice(BuyingController):
"Stock Ledger Entry", "Stock Ledger Entry",
"Repost Item Valuation", "Repost Item Valuation",
"Payment Ledger Entry", "Payment Ledger Entry",
"Tax Withheld Vouchers",
) )
self.update_advance_tax_references(cancel=1) self.update_advance_tax_references(cancel=1)

View File

@ -215,6 +215,7 @@
"reqd": 1 "reqd": 1
}, },
{ {
"default": "1",
"depends_on": "eval:doc.uom != doc.stock_uom", "depends_on": "eval:doc.uom != doc.stock_uom",
"fieldname": "conversion_factor", "fieldname": "conversion_factor",
"fieldtype": "Float", "fieldtype": "Float",
@ -712,6 +713,7 @@
"label": "Valuation Rate", "label": "Valuation Rate",
"no_copy": 1, "no_copy": 1,
"options": "Company:company:default_currency", "options": "Company:company:default_currency",
"precision": "6",
"print_hide": 1, "print_hide": 1,
"read_only": 1 "read_only": 1
}, },
@ -820,6 +822,7 @@
}, },
{ {
"collapsible": 1, "collapsible": 1,
"collapsible_depends_on": "eval: doc.margin_type || doc.discount_amount",
"fieldname": "section_break_26", "fieldname": "section_break_26",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Discount and Margin" "label": "Discount and Margin"
@ -877,7 +880,7 @@
"idx": 1, "idx": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2022-09-27 10:54:23.980713", "modified": "2022-10-26 16:05:37.304788",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Purchase Invoice Item", "name": "Purchase Invoice Item",

View File

@ -12,44 +12,29 @@
"customer", "customer",
"customer_name", "customer_name",
"tax_id", "tax_id",
"pos_profile",
"is_pos",
"is_consolidated",
"is_return",
"is_debit_note",
"update_billed_amount_in_sales_order",
"column_break1",
"company", "company",
"company_tax_id", "company_tax_id",
"column_break1",
"posting_date", "posting_date",
"posting_time", "posting_time",
"set_posting_time", "set_posting_time",
"due_date", "due_date",
"column_break_14",
"is_pos",
"pos_profile",
"is_consolidated",
"is_return",
"return_against", "return_against",
"update_billed_amount_in_sales_order",
"is_debit_note",
"amended_from", "amended_from",
"accounting_dimensions_section", "accounting_dimensions_section",
"project",
"dimension_col_break",
"cost_center", "cost_center",
"customer_po_details", "dimension_col_break",
"po_no", "project",
"column_break_23", "column_break_27",
"po_date", "campaign",
"address_and_contact", "source",
"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",
"currency_and_price_list", "currency_and_price_list",
"currency", "currency",
"conversion_rate", "conversion_rate",
@ -58,60 +43,35 @@
"price_list_currency", "price_list_currency",
"plc_conversion_rate", "plc_conversion_rate",
"ignore_pricing_rule", "ignore_pricing_rule",
"sec_warehouse",
"set_warehouse",
"column_break_55",
"set_target_warehouse",
"items_section", "items_section",
"update_stock",
"scan_barcode", "scan_barcode",
"update_stock",
"column_break_39",
"set_warehouse",
"set_target_warehouse",
"section_break_42",
"items", "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", "section_break_30",
"total_qty", "total_qty",
"total_net_weight",
"column_break_32",
"base_total", "base_total",
"base_net_total", "base_net_total",
"column_break_32", "column_break_52",
"total_net_weight",
"total", "total",
"net_total", "net_total",
"taxes_section", "taxes_section",
"taxes_and_charges", "taxes_and_charges",
"column_break_38", "column_break_38",
"shipping_rule", "shipping_rule",
"column_break_55",
"tax_category", "tax_category",
"section_break_40", "section_break_40",
"taxes", "taxes",
"sec_tax_breakup",
"other_charges_calculation",
"section_break_43", "section_break_43",
"base_total_taxes_and_charges", "base_total_taxes_and_charges",
"column_break_47", "column_break_47",
"total_taxes_and_charges", "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", "totals",
"base_grand_total", "base_grand_total",
"base_rounding_adjustment", "base_rounding_adjustment",
@ -125,21 +85,28 @@
"total_advance", "total_advance",
"outstanding_amount", "outstanding_amount",
"disable_rounded_total", "disable_rounded_total",
"column_break4", "section_break_49",
"write_off_amount", "apply_discount_on",
"base_write_off_amount", "base_discount_amount",
"write_off_outstanding_amount_automatically", "is_cash_or_non_trade_discount",
"column_break_74", "additional_discount_account",
"write_off_account", "column_break_51",
"write_off_cost_center", "additional_discount_percentage",
"advances_section", "discount_amount",
"allocate_advances_automatically", "sec_tax_breakup",
"get_advances", "other_charges_calculation",
"advances", "pricing_rule_details",
"payment_schedule_section", "pricing_rules",
"ignore_default_payment_terms_template", "packing_list",
"payment_terms_template", "packed_items",
"payment_schedule", "product_bundle_help",
"time_sheet_list",
"timesheets",
"section_break_104",
"total_billing_hours",
"column_break_106",
"total_billing_amount",
"payments_tab",
"payments_section", "payments_section",
"cash_bank_account", "cash_bank_account",
"payments", "payments",
@ -152,47 +119,95 @@
"column_break_90", "column_break_90",
"change_amount", "change_amount",
"account_for_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", "terms_section_break",
"tc_name", "tc_name",
"terms", "terms",
"edit_printing_settings", "more_info_tab",
"letter_head", "customer_po_details",
"group_same_items", "po_no",
"select_print_heading", "column_break_23",
"column_break_84", "po_date",
"language",
"more_information",
"status",
"inter_company_invoice_reference",
"represents_company",
"customer_group",
"campaign",
"col_break23",
"is_internal_customer",
"is_discounted",
"source",
"more_info", "more_info",
"debit_to", "debit_to",
"party_account_currency", "party_account_currency",
"is_opening", "is_opening",
"column_break8", "column_break8",
"unrealized_profit_loss_account", "unrealized_profit_loss_account",
"remarks", "against_income_account",
"sales_team_section_break", "sales_team_section_break",
"sales_partner", "sales_partner",
"column_break10",
"amount_eligible_for_commission", "amount_eligible_for_commission",
"column_break10",
"commission_rate", "commission_rate",
"total_commission", "total_commission",
"section_break2", "section_break2",
"sales_team", "sales_team",
"edit_printing_settings",
"letter_head",
"group_same_items",
"column_break_84",
"select_print_heading",
"language",
"subscription_section", "subscription_section",
"from_date", "from_date",
"to_date",
"column_break_140",
"auto_repeat", "auto_repeat",
"column_break_140",
"to_date",
"update_auto_repeat_reference", "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": [ "fields": [
{ {
@ -453,12 +468,11 @@
"label": "Customer's Purchase Order Date" "label": "Customer's Purchase Order Date"
}, },
{ {
"collapsible": 1,
"fieldname": "address_and_contact", "fieldname": "address_and_contact",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hide_days": 1, "hide_days": 1,
"hide_seconds": 1, "hide_seconds": 1,
"label": "Address and Contact" "label": "Billing Address"
}, },
{ {
"fieldname": "customer_address", "fieldname": "customer_address",
@ -560,7 +574,6 @@
{ {
"fieldname": "company_address_display", "fieldname": "company_address_display",
"fieldtype": "Small Text", "fieldtype": "Small Text",
"hidden": 1,
"hide_days": 1, "hide_days": 1,
"hide_seconds": 1, "hide_seconds": 1,
"label": "Company Address", "label": "Company Address",
@ -651,13 +664,6 @@
"label": "Ignore Pricing Rule", "label": "Ignore Pricing Rule",
"print_hide": 1 "print_hide": 1
}, },
{
"fieldname": "sec_warehouse",
"fieldtype": "Section Break",
"hide_days": 1,
"hide_seconds": 1,
"label": "Warehouse"
},
{ {
"depends_on": "update_stock", "depends_on": "update_stock",
"fieldname": "set_warehouse", "fieldname": "set_warehouse",
@ -671,6 +677,7 @@
{ {
"fieldname": "items_section", "fieldname": "items_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hide_border": 1,
"hide_days": 1, "hide_days": 1,
"hide_seconds": 1, "hide_seconds": 1,
"label": "Items", "label": "Items",
@ -702,7 +709,6 @@
"fieldtype": "Table", "fieldtype": "Table",
"hide_days": 1, "hide_days": 1,
"hide_seconds": 1, "hide_seconds": 1,
"label": "Items",
"oldfieldname": "entries", "oldfieldname": "entries",
"oldfieldtype": "Table", "oldfieldtype": "Table",
"options": "Sales Invoice Item", "options": "Sales Invoice Item",
@ -755,9 +761,10 @@
{ {
"collapsible": 1, "collapsible": 1,
"collapsible_depends_on": "eval:doc.total_billing_amount > 0", "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", "fieldname": "time_sheet_list",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hide_border": 1,
"hide_days": 1, "hide_days": 1,
"hide_seconds": 1, "hide_seconds": 1,
"label": "Time Sheet List" "label": "Time Sheet List"
@ -845,6 +852,7 @@
"read_only": 1 "read_only": 1
}, },
{ {
"depends_on": "total_net_weight",
"fieldname": "total_net_weight", "fieldname": "total_net_weight",
"fieldtype": "Float", "fieldtype": "Float",
"hide_days": 1, "hide_days": 1,
@ -856,8 +864,10 @@
{ {
"fieldname": "taxes_section", "fieldname": "taxes_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hide_border": 1,
"hide_days": 1, "hide_days": 1,
"hide_seconds": 1, "hide_seconds": 1,
"label": "Taxes and Charges",
"oldfieldtype": "Section Break", "oldfieldtype": "Section Break",
"options": "fa fa-money" "options": "fa fa-money"
}, },
@ -900,6 +910,7 @@
{ {
"fieldname": "section_break_40", "fieldname": "section_break_40",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hide_border": 1,
"hide_days": 1, "hide_days": 1,
"hide_seconds": 1 "hide_seconds": 1
}, },
@ -908,7 +919,6 @@
"fieldtype": "Table", "fieldtype": "Table",
"hide_days": 1, "hide_days": 1,
"hide_seconds": 1, "hide_seconds": 1,
"label": "Sales Taxes and Charges",
"oldfieldname": "other_charges", "oldfieldname": "other_charges",
"oldfieldtype": "Table", "oldfieldtype": "Table",
"options": "Sales Taxes and Charges" "options": "Sales Taxes and Charges"
@ -1046,7 +1056,6 @@
}, },
{ {
"collapsible": 1, "collapsible": 1,
"collapsible_depends_on": "discount_amount",
"fieldname": "section_break_49", "fieldname": "section_break_49",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hide_days": 1, "hide_days": 1,
@ -1102,6 +1111,7 @@
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hide_days": 1, "hide_days": 1,
"hide_seconds": 1, "hide_seconds": 1,
"label": "Totals",
"oldfieldtype": "Section Break", "oldfieldtype": "Section Break",
"options": "fa fa-money", "options": "fa fa-money",
"print_hide": 1 "print_hide": 1
@ -1283,8 +1293,6 @@
"print_hide": 1 "print_hide": 1
}, },
{ {
"collapsible": 1,
"collapsible_depends_on": "eval:(!doc.is_pos && !doc.is_return)",
"fieldname": "payment_schedule_section", "fieldname": "payment_schedule_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hide_days": 1, "hide_days": 1,
@ -1314,7 +1322,9 @@
"print_hide": 1 "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", "fieldname": "payments_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hide_days": 1, "hide_days": 1,
@ -1352,6 +1362,7 @@
"hide_seconds": 1 "hide_seconds": 1
}, },
{ {
"depends_on": "eval: doc.is_pos || doc.redeem_loyalty_points",
"fieldname": "base_paid_amount", "fieldname": "base_paid_amount",
"fieldtype": "Currency", "fieldtype": "Currency",
"hide_days": 1, "hide_days": 1,
@ -1383,10 +1394,13 @@
"read_only": 1 "read_only": 1
}, },
{ {
"collapsible": 1,
"depends_on": "is_pos",
"fieldname": "section_break_88", "fieldname": "section_break_88",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hide_days": 1, "hide_days": 1,
"hide_seconds": 1 "hide_seconds": 1,
"label": "Changes"
}, },
{ {
"depends_on": "is_pos", "depends_on": "is_pos",
@ -1427,17 +1441,6 @@
"options": "Account", "options": "Account",
"print_hide": 1 "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", "fieldname": "write_off_amount",
"fieldtype": "Currency", "fieldtype": "Currency",
@ -1495,8 +1498,6 @@
"print_hide": 1 "print_hide": 1
}, },
{ {
"collapsible": 1,
"collapsible_depends_on": "terms",
"fieldname": "terms_section_break", "fieldname": "terms_section_break",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hide_days": 1, "hide_days": 1,
@ -1530,7 +1531,7 @@
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hide_days": 1, "hide_days": 1,
"hide_seconds": 1, "hide_seconds": 1,
"label": "Printing Settings" "label": "Print Settings"
}, },
{ {
"allow_on_submit": 1, "allow_on_submit": 1,
@ -1591,7 +1592,7 @@
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hide_days": 1, "hide_days": 1,
"hide_seconds": 1, "hide_seconds": 1,
"label": "More Information" "label": "Additional Info"
}, },
{ {
"fieldname": "inter_company_invoice_reference", "fieldname": "inter_company_invoice_reference",
@ -1814,7 +1815,7 @@
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hide_days": 1, "hide_days": 1,
"hide_seconds": 1, "hide_seconds": 1,
"label": "Subscription Section" "label": "Subscription"
}, },
{ {
"allow_on_submit": 1, "allow_on_submit": 1,
@ -1935,10 +1936,6 @@
"options": "Company", "options": "Company",
"read_only": 1 "read_only": 1
}, },
{
"fieldname": "column_break_55",
"fieldtype": "Column Break"
},
{ {
"depends_on": "eval: doc.is_internal_customer && doc.update_stock", "depends_on": "eval: doc.is_internal_customer && doc.update_stock",
"fieldname": "set_target_warehouse", "fieldname": "set_target_warehouse",
@ -2009,6 +2006,97 @@
"fieldname": "is_cash_or_non_trade_discount", "fieldname": "is_cash_or_non_trade_discount",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Is Cash or Non Trade Discount" "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", "icon": "fa fa-file-text",
@ -2021,7 +2109,7 @@
"link_fieldname": "consolidated_invoice" "link_fieldname": "consolidated_invoice"
} }
], ],
"modified": "2022-09-16 17:44:22.227332", "modified": "2022-10-11 13:07:36.488095",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Sales Invoice", "name": "Sales Invoice",

View File

@ -2017,6 +2017,9 @@ def make_inter_company_transaction(doctype, source_name, target_doc=None):
update_address( update_address(
target_doc, "shipping_address", "shipping_address_display", source_doc.customer_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: if currency:
target_doc.currency = currency target_doc.currency = currency

View File

@ -32,10 +32,20 @@ from erpnext.stock.doctype.stock_entry.test_stock_entry import (
get_qty_after_transaction, get_qty_after_transaction,
make_stock_entry, 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): 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): def make(self):
w = frappe.copy_doc(test_records[0]) w = frappe.copy_doc(test_records[0])
w.is_pos = 0 w.is_pos = 0
@ -955,7 +965,8 @@ class TestSalesInvoice(unittest.TestCase):
pos_return.insert() pos_return.insert()
pos_return.submit() pos_return.submit()
self.assertEqual(pos_return.get("payments")[0].amount, -1000) self.assertEqual(pos_return.get("payments")[0].amount, -500)
self.assertEqual(pos_return.get("payments")[1].amount, -500)
def test_pos_change_amount(self): def test_pos_change_amount(self):
make_pos_profile( make_pos_profile(
@ -1705,7 +1716,7 @@ class TestSalesInvoice(unittest.TestCase):
si.save() si.save()
self.assertEqual(si.get("items")[0].rate, flt((price_list_rate * 25) / 100 + price_list_rate)) 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 ( from erpnext.accounts.doctype.journal_entry.test_journal_entry import (
test_records as jv_test_records, test_records as jv_test_records,
) )
@ -1749,7 +1760,7 @@ class TestSalesInvoice(unittest.TestCase):
flt(si.rounded_total + si.total_advance, si.precision("outstanding_amount")), 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( pe = frappe.get_doc(
{ {
"doctype": "Payment Entry", "doctype": "Payment Entry",
@ -2367,29 +2378,6 @@ class TestSalesInvoice(unittest.TestCase):
acc_settings.save() acc_settings.save()
def test_inter_company_transaction(self): 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( si = create_sales_invoice(
company="Wind Power LLC", company="Wind Power LLC",
customer="_Test Internal Customer", customer="_Test Internal Customer",
@ -2440,38 +2428,6 @@ class TestSalesInvoice(unittest.TestCase):
"Expenses Included In Valuation - _TC1", "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 # begin test
si = create_sales_invoice( si = create_sales_invoice(
company="Wind Power LLC", company="Wind Power LLC",
@ -2541,34 +2497,9 @@ class TestSalesInvoice(unittest.TestCase):
se.cancel() se.cancel()
def test_internal_transfer_gl_entry(self): 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( si = create_sales_invoice(
company="_Test Company with perpetual inventory", company="_Test Company with perpetual inventory",
customer=customer, customer="_Test Internal Customer 2",
debit_to="Debtors - TCP1", debit_to="Debtors - TCP1",
warehouse="Stores - TCP1", warehouse="Stores - TCP1",
income_account="Sales - TCP1", income_account="Sales - TCP1",
@ -2582,7 +2513,7 @@ class TestSalesInvoice(unittest.TestCase):
si.update_stock = 1 si.update_stock = 1
si.items[0].target_warehouse = "Work In Progress - TCP1" 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( make_stock_entry(
target="Stores - TCP1", company="_Test Company with perpetual inventory", qty=1, basic_rate=100 target="Stores - TCP1", company="_Test Company with perpetual inventory", qty=1, basic_rate=100
) )
@ -2638,6 +2569,77 @@ class TestSalesInvoice(unittest.TestCase):
check_gl_entries(self, target_doc.name, pi_gl_entries, add_days(nowdate(), -1)) 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): def test_item_tax_net_range(self):
item = create_item("T Shirt") item = create_item("T Shirt")
@ -3077,7 +3079,7 @@ class TestSalesInvoice(unittest.TestCase):
[deferred_account, 2022.47, 0.0, "2019-03-15"], [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 """select account, debit, credit, posting_date
from `tabGL Entry` from `tabGL Entry`
where voucher_type='Journal Entry' and voucher_detail_no=%s and posting_date <= %s where voucher_type='Journal Entry' and voucher_detail_no=%s and posting_date <= %s
@ -3227,6 +3229,22 @@ class TestSalesInvoice(unittest.TestCase):
self.assertTrue(return_si.docstatus == 1) 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(): def get_sales_invoice_for_e_invoice():
si = make_sales_invoice_for_ewaybill() si = make_sales_invoice_for_ewaybill()
@ -3306,6 +3324,7 @@ def create_sales_invoice(**args):
"item_name": args.item_name or "_Test Item", "item_name": args.item_name or "_Test Item",
"description": args.description or "_Test Item", "description": args.description or "_Test Item",
"warehouse": args.warehouse or "_Test Warehouse - _TC", "warehouse": args.warehouse or "_Test Warehouse - _TC",
"target_warehouse": args.target_warehouse,
"qty": args.qty or 1, "qty": args.qty or 1,
"uom": args.uom or "Nos", "uom": args.uom or "Nos",
"stock_uom": args.uom or "Nos", "stock_uom": args.uom or "Nos",
@ -3318,7 +3337,7 @@ def create_sales_invoice(**args):
"asset": args.asset or None, "asset": args.asset or None,
"cost_center": args.cost_center or "_Test Cost Center - _TC", "cost_center": args.cost_center or "_Test Cost Center - _TC",
"serial_no": args.serial_no, "serial_no": args.serial_no,
"conversion_factor": 1, "conversion_factor": args.get("conversion_factor", 1),
"incoming_rate": args.incoming_rate or 0, "incoming_rate": args.incoming_rate or 0,
"batch_no": args.batch_no or None, "batch_no": args.batch_no or None,
}, },
@ -3431,6 +3450,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): def create_internal_supplier(supplier_name, represents_company, allowed_to_interact_with):
if not frappe.db.exists("Supplier", supplier_name): if not frappe.db.exists("Supplier", supplier_name):
supplier = frappe.get_doc( supplier = frappe.get_doc(
@ -3453,6 +3500,19 @@ def create_internal_supplier(supplier_name, represents_company, allowed_to_inter
return supplier_name 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): def add_taxes(doc):
doc.append( doc.append(
"taxes", "taxes",

View File

@ -247,6 +247,7 @@
}, },
{ {
"collapsible": 1, "collapsible": 1,
"collapsible_depends_on": "eval: doc.margin_type || doc.discount_amount",
"fieldname": "discount_and_margin", "fieldname": "discount_and_margin",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Discount and Margin" "label": "Discount and Margin"
@ -820,6 +821,7 @@
"label": "Incoming Rate (Costing)", "label": "Incoming Rate (Costing)",
"no_copy": 1, "no_copy": 1,
"options": "Company:company:default_currency", "options": "Company:company:default_currency",
"precision": "6",
"print_hide": 1 "print_hide": 1
}, },
{ {
@ -875,7 +877,7 @@
"idx": 1, "idx": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2022-09-06 14:17:43.394309", "modified": "2022-10-26 11:38:36.119339",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Accounts", "module": "Accounts",
"name": "Sales Invoice Item", "name": "Sales Invoice Item",

View File

@ -3,6 +3,7 @@
import frappe import frappe
from dateutil import relativedelta
from frappe import _ from frappe import _
from frappe.model.document import Document from frappe.model.document import Document
from frappe.utils import date_diff, flt, get_first_day, get_last_day, getdate from frappe.utils import date_diff, flt, get_first_day, get_last_day, getdate
@ -49,7 +50,7 @@ def get_plan_rate(
start_date = getdate(start_date) start_date = getdate(start_date)
end_date = getdate(end_date) end_date = getdate(end_date)
no_of_months = (end_date.year - start_date.year) * 12 + (end_date.month - start_date.month) + 1 no_of_months = relativedelta.relativedelta(end_date, start_date).months + 1
cost = plan.cost * no_of_months cost = plan.cost * no_of_months
# Adjust cost if start or end date is not month start or end # Adjust cost if start or end date is not month start or end

View File

@ -252,6 +252,9 @@ def get_tax_amount(party_type, parties, inv, tax_details, posting_date, pan_no=N
) )
else: else:
tax_amount = net_total * tax_details.rate / 100 if net_total > 0 else 0 tax_amount = net_total * tax_details.rate / 100 if net_total > 0 else 0
# once tds is deducted, not need to add vouchers in the invoice
voucher_wise_amount = {}
else: else:
tax_amount = get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers) tax_amount = get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers)
@ -428,8 +431,11 @@ def get_tds_amount(ldc, parties, inv, tax_details, tax_deducted, vouchers):
): ):
# Get net total again as TDS is calculated on net total # Get net total again as TDS is calculated on net total
# Grand is used to just check for threshold breach # Grand is used to just check for threshold breach
net_total = frappe.db.get_value("Purchase Invoice", invoice_filters, "sum(tax_withholding_net_total)") or 0.0 net_total = 0
net_total += inv.tax_withholding_net_total if vouchers:
net_total = frappe.db.get_value("Purchase Invoice", invoice_filters, "sum(net_total)")
net_total += inv.net_total
supp_credit_amt = net_total - cumulative_threshold supp_credit_amt = net_total - cumulative_threshold
if ldc and is_valid_certificate( if ldc and is_valid_certificate(

View File

@ -128,6 +128,12 @@ def distribute_gl_based_on_cost_center_allocation(gl_map, precision=None):
new_gl_map = [] new_gl_map = []
for d in gl_map: for d in gl_map:
cost_center = d.get("cost_center") cost_center = d.get("cost_center")
# Validate budget against main cost center
validate_expense_against_budget(
d, expense_amount=flt(d.debit, precision) - flt(d.credit, precision)
)
if cost_center and cost_center_allocation.get(cost_center): if cost_center and cost_center_allocation.get(cost_center):
for sub_cost_center, percentage in cost_center_allocation.get(cost_center, {}).items(): for sub_cost_center, percentage in cost_center_allocation.get(cost_center, {}).items():
gle = copy.deepcopy(d) gle = copy.deepcopy(d)

View File

@ -51,6 +51,8 @@ frappe.query_reports["Accounts Payable"] = {
} else { } else {
frappe.query_report.set_filter_value('tax_id', ""); frappe.query_report.set_filter_value('tax_id', "");
} }
frappe.query_report.refresh();
} }
}, },
{ {

View File

@ -748,7 +748,7 @@ class ReceivablePayableReport(object):
self.add_accounting_dimensions_filters() self.add_accounting_dimensions_filters()
def get_cost_center_conditions(self, conditions): def get_cost_center_conditions(self):
lft, rgt = frappe.db.get_value("Cost Center", self.filters.cost_center, ["lft", "rgt"]) lft, rgt = frappe.db.get_value("Cost Center", self.filters.cost_center, ["lft", "rgt"])
cost_center_list = [ cost_center_list = [
center.name center.name

View File

@ -52,22 +52,22 @@
{% } %} {% } %}
</td> </td>
<td style="text-align: right"> <td style="text-align: right">
{%= format_currency(data[i].debit, filters.presentation_currency) %}</td> {%= format_currency(data[i].debit, filters.presentation_currency || data[i].account_currency) %}</td>
<td style="text-align: right"> <td style="text-align: right">
{%= format_currency(data[i].credit, filters.presentation_currency) %}</td> {%= format_currency(data[i].credit, filters.presentation_currency || data[i].account_currency) %}</td>
{% } else { %} {% } else { %}
<td></td> <td></td>
<td></td> <td></td>
<td><b>{%= frappe.format(data[i].account, {fieldtype: "Link"}) || "&nbsp;" %}</b></td> <td><b>{%= frappe.format(data[i].account, {fieldtype: "Link"}) || "&nbsp;" %}</b></td>
<td style="text-align: right"> <td style="text-align: right">
{%= data[i].account && format_currency(data[i].debit, filters.presentation_currency) %} {%= data[i].account && format_currency(data[i].debit, filters.presentation_currency || data[i].account_currency) %}
</td> </td>
<td style="text-align: right"> <td style="text-align: right">
{%= data[i].account && format_currency(data[i].credit, filters.presentation_currency) %} {%= data[i].account && format_currency(data[i].credit, filters.presentation_currency || data[i].account_currency) %}
</td> </td>
{% } %} {% } %}
<td style="text-align: right"> <td style="text-align: right">
{%= format_currency(data[i].balance, filters.presentation_currency) %} {%= format_currency(data[i].balance, filters.presentation_currency || data[i].account_currency) %}
</td> </td>
</tr> </tr>
{% } %} {% } %}

View File

@ -0,0 +1,59 @@
// Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
// For license information, please see license.txt
/* eslint-disable */
function get_filters() {
let filters = [
{
"fieldname":"company",
"label": __("Company"),
"fieldtype": "Link",
"options": "Company",
"default": frappe.defaults.get_user_default("Company"),
"reqd": 1
},
{
"fieldname":"period_start_date",
"label": __("Start Date"),
"fieldtype": "Date",
"reqd": 1,
"default": frappe.datetime.add_months(frappe.datetime.get_today(), -1)
},
{
"fieldname":"period_end_date",
"label": __("End Date"),
"fieldtype": "Date",
"reqd": 1,
"default": frappe.datetime.get_today()
},
{
"fieldname":"account",
"label": __("Account"),
"fieldtype": "MultiSelectList",
"options": "Account",
get_data: function(txt) {
return frappe.db.get_link_options('Account', txt, {
company: frappe.query_report.get_filter_value("company")
});
}
},
{
"fieldname":"voucher_no",
"label": __("Voucher No"),
"fieldtype": "Data",
"width": 100,
},
{
"fieldname":"against_voucher_no",
"label": __("Against Voucher No"),
"fieldtype": "Data",
"width": 100,
},
]
return filters;
}
frappe.query_reports["Payment Ledger"] = {
"filters": get_filters()
};

View File

@ -0,0 +1,32 @@
{
"add_total_row": 0,
"columns": [],
"creation": "2022-06-06 08:50:43.933708",
"disable_prepared_report": 0,
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"filters": [],
"idx": 0,
"is_standard": "Yes",
"modified": "2022-06-06 08:50:43.933708",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Ledger",
"owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "Payment Ledger Entry",
"report_name": "Payment Ledger",
"report_type": "Script Report",
"roles": [
{
"role": "Accounts User"
},
{
"role": "Accounts Manager"
},
{
"role": "Auditor"
}
]
}

View File

@ -0,0 +1,222 @@
# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt
from collections import OrderedDict
import frappe
from frappe import _, qb
from frappe.query_builder import Criterion
class PaymentLedger(object):
def __init__(self, filters=None):
self.filters = filters
self.columns, self.data = [], []
self.voucher_dict = OrderedDict()
self.voucher_amount = []
self.ple = qb.DocType("Payment Ledger Entry")
def init_voucher_dict(self):
if self.voucher_amount:
s = set()
# build a set of unique vouchers
for ple in self.voucher_amount:
key = (ple.voucher_type, ple.voucher_no, ple.party)
s.add(key)
# for each unique vouchers, initialize +/- list
for key in s:
self.voucher_dict[key] = frappe._dict(increase=list(), decrease=list())
# for each ple, using against voucher and amount, assign it to +/- list
# group by against voucher
for ple in self.voucher_amount:
against_key = (ple.against_voucher_type, ple.against_voucher_no, ple.party)
target = None
if self.voucher_dict.get(against_key):
if ple.amount > 0:
target = self.voucher_dict.get(against_key).increase
else:
target = self.voucher_dict.get(against_key).decrease
# this if condition will lose unassigned ple entries(against_voucher doc doesn't have ple)
# need to somehow include the stray entries as well.
if target is not None:
entry = frappe._dict(
company=ple.company,
account=ple.account,
party_type=ple.party_type,
party=ple.party,
voucher_type=ple.voucher_type,
voucher_no=ple.voucher_no,
against_voucher_type=ple.against_voucher_type,
against_voucher_no=ple.against_voucher_no,
amount=ple.amount,
currency=ple.account_currency,
)
if self.filters.include_account_currency:
entry["amount_in_account_currency"] = ple.amount_in_account_currency
target.append(entry)
def build_data(self):
self.data.clear()
for value in self.voucher_dict.values():
voucher_data = []
if value.increase != []:
voucher_data.extend(value.increase)
if value.decrease != []:
voucher_data.extend(value.decrease)
if voucher_data:
# balance row
total = 0
total_in_account_currency = 0
for x in voucher_data:
total += x.amount
if self.filters.include_account_currency:
total_in_account_currency += x.amount_in_account_currency
entry = frappe._dict(
against_voucher_no="Outstanding:",
amount=total,
currency=voucher_data[0].currency,
)
if self.filters.include_account_currency:
entry["amount_in_account_currency"] = total_in_account_currency
voucher_data.append(entry)
# empty row
voucher_data.append(frappe._dict())
self.data.extend(voucher_data)
def build_conditions(self):
self.conditions = []
if self.filters.company:
self.conditions.append(self.ple.company == self.filters.company)
if self.filters.account:
self.conditions.append(self.ple.account.isin(self.filters.account))
if self.filters.period_start_date:
self.conditions.append(self.ple.posting_date.gte(self.filters.period_start_date))
if self.filters.period_end_date:
self.conditions.append(self.ple.posting_date.lte(self.filters.period_end_date))
if self.filters.voucher_no:
self.conditions.append(self.ple.voucher_no == self.filters.voucher_no)
if self.filters.against_voucher_no:
self.conditions.append(self.ple.against_voucher_no == self.filters.against_voucher_no)
def get_data(self):
ple = self.ple
self.build_conditions()
# fetch data from table
self.voucher_amount = (
qb.from_(ple)
.select(ple.star)
.where(ple.delinked == 0)
.where(Criterion.all(self.conditions))
.run(as_dict=True)
)
def get_columns(self):
options = None
self.columns.append(
dict(label=_("Company"), fieldname="company", fieldtype="data", options=options, width="100")
)
self.columns.append(
dict(label=_("Account"), fieldname="account", fieldtype="data", options=options, width="100")
)
self.columns.append(
dict(
label=_("Party Type"), fieldname="party_type", fieldtype="data", options=options, width="100"
)
)
self.columns.append(
dict(label=_("Party"), fieldname="party", fieldtype="data", options=options, width="100")
)
self.columns.append(
dict(
label=_("Voucher Type"),
fieldname="voucher_type",
fieldtype="data",
options=options,
width="100",
)
)
self.columns.append(
dict(
label=_("Voucher No"), fieldname="voucher_no", fieldtype="data", options=options, width="100"
)
)
self.columns.append(
dict(
label=_("Against Voucher Type"),
fieldname="against_voucher_type",
fieldtype="data",
options=options,
width="100",
)
)
self.columns.append(
dict(
label=_("Against Voucher No"),
fieldname="against_voucher_no",
fieldtype="data",
options=options,
width="100",
)
)
self.columns.append(
dict(
label=_("Amount"),
fieldname="amount",
fieldtype="Currency",
options="Company:company:default_currency",
width="100",
)
)
if self.filters.include_account_currency:
self.columns.append(
dict(
label=_("Amount in Account Currency"),
fieldname="amount_in_account_currency",
fieldtype="Currency",
options="currency",
width="100",
)
)
self.columns.append(
dict(label=_("Currency"), fieldname="currency", fieldtype="Currency", hidden=True)
)
def run(self):
self.get_columns()
self.get_data()
# initialize dictionary and group using against voucher
self.init_voucher_dict()
# convert dictionary to list and add balance rows
self.build_data()
return self.columns, self.data
def execute(filters=None):
return PaymentLedger(filters).run()

View File

@ -0,0 +1,65 @@
import unittest
import frappe
from frappe import qb
from frappe.tests.utils import FrappeTestCase
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.accounts.report.payment_ledger.payment_ledger import execute
class TestPaymentLedger(FrappeTestCase):
def setUp(self):
self.create_company()
self.cleanup()
def cleanup(self):
doctypes = []
doctypes.append(qb.DocType("GL Entry"))
doctypes.append(qb.DocType("Payment Ledger Entry"))
doctypes.append(qb.DocType("Sales Invoice"))
doctypes.append(qb.DocType("Payment Entry"))
for doctype in doctypes:
qb.from_(doctype).delete().where(doctype.company == self.company).run()
def create_company(self):
name = "Test Payment Ledger"
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
self.cost_center = company.cost_center
self.warehouse = "All Warehouses" + " - " + company.abbr
self.income_account = company.default_income_account
self.expense_account = company.default_expense_account
self.debit_to = company.default_receivable_account
def test_unpaid_invoice_outstanding(self):
sinv = create_sales_invoice(
company=self.company,
debit_to=self.debit_to,
expense_account=self.expense_account,
cost_center=self.cost_center,
income_account=self.income_account,
warehouse=self.warehouse,
)
pe = get_payment_entry(sinv.doctype, sinv.name).save().submit()
filters = frappe._dict({"company": self.company})
columns, data = execute(filters=filters)
outstanding = [x for x in data if x.get("against_voucher_no") == "Outstanding:"]
self.assertEqual(outstanding[0].get("amount"), 0)

View File

@ -460,10 +460,6 @@ def reconcile_against_document(args): # nosemgrep
frappe.flags.ignore_party_validation = False 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): def check_if_advance_entry_modified(args):
""" """
@ -648,6 +644,16 @@ def unlink_ref_doc_from_payment_entries(ref_doc):
(now(), frappe.session.user, ref_doc.doctype, ref_doc.name), (now(), frappe.session.user, ref_doc.doctype, ref_doc.name),
) )
ple = qb.DocType("Payment Ledger Entry")
qb.update(ple).set(ple.against_voucher_type, ple.voucher_type).set(
ple.against_voucher_no, ple.voucher_no
).set(ple.modified, now()).set(ple.modified_by, frappe.session.user).where(
(ple.against_voucher_type == ref_doc.doctype)
& (ple.against_voucher_no == ref_doc.name)
& (ple.delinked == 0)
).run()
if ref_doc.doctype in ("Sales Invoice", "Purchase Invoice"): if ref_doc.doctype in ("Sales Invoice", "Purchase Invoice"):
ref_doc.set("advances", []) ref_doc.set("advances", [])
@ -1161,6 +1167,10 @@ def _delete_gl_entries(voucher_type, voucher_no):
where voucher_type=%s and voucher_no=%s""", where voucher_type=%s and voucher_no=%s""",
(voucher_type, voucher_no), (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( def sort_stock_vouchers_by_posting_date(
@ -1358,9 +1368,8 @@ def check_and_delete_linked_reports(report):
frappe.delete_doc("Desktop Icon", icon) frappe.delete_doc("Desktop Icon", icon)
def create_payment_ledger_entry( def get_payment_ledger_entries(gl_entries, cancel=0):
gl_entries, cancel=0, adv_adj=0, update_outstanding="Yes", from_repost=0 ple_map = []
):
if gl_entries: if gl_entries:
ple = None ple = None
@ -1400,44 +1409,57 @@ def create_payment_ledger_entry(
dr_or_cr *= -1 dr_or_cr *= -1
dr_or_cr_account_currency *= -1 dr_or_cr_account_currency *= -1
ple = frappe.get_doc( ple = frappe._dict(
{ doctype="Payment Ledger Entry",
"doctype": "Payment Ledger Entry", posting_date=gle.posting_date,
"posting_date": gle.posting_date, company=gle.company,
"company": gle.company, account_type=account_type,
"account_type": account_type, account=gle.account,
"account": gle.account, party_type=gle.party_type,
"party_type": gle.party_type, party=gle.party,
"party": gle.party, cost_center=gle.cost_center,
"cost_center": gle.cost_center, finance_book=gle.finance_book,
"finance_book": gle.finance_book, due_date=gle.due_date,
"due_date": gle.due_date, voucher_type=gle.voucher_type,
"voucher_type": gle.voucher_type, voucher_no=gle.voucher_no,
"voucher_no": gle.voucher_no, against_voucher_type=gle.against_voucher_type
"against_voucher_type": gle.against_voucher_type if gle.against_voucher_type
if gle.against_voucher_type else gle.voucher_type,
else gle.voucher_type, against_voucher_no=gle.against_voucher if gle.against_voucher else gle.voucher_no,
"against_voucher_no": gle.against_voucher if gle.against_voucher else gle.voucher_no, account_currency=gle.account_currency,
"account_currency": gle.account_currency, amount=dr_or_cr,
"amount": dr_or_cr, amount_in_account_currency=dr_or_cr_account_currency,
"amount_in_account_currency": dr_or_cr_account_currency, delinked=True if cancel else False,
"delinked": True if cancel else False, remarks=gle.remarks,
"remarks": gle.remarks,
}
) )
dimensions_and_defaults = get_dimensions() dimensions_and_defaults = get_dimensions()
if dimensions_and_defaults: if dimensions_and_defaults:
for dimension in dimensions_and_defaults[0]: for dimension in dimensions_and_defaults[0]:
ple.set(dimension.fieldname, gle.get(dimension.fieldname)) ple[dimension.fieldname] = gle.get(dimension.fieldname)
if cancel: ple_map.append(ple)
delink_original_entry(ple) return ple_map
ple.flags.ignore_permissions = 1
ple.flags.adv_adj = adv_adj
ple.flags.from_repost = from_repost def create_payment_ledger_entry(
ple.flags.update_outstanding = update_outstanding gl_entries, cancel=0, adv_adj=0, update_outstanding="Yes", from_repost=0
ple.submit() ):
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): def update_voucher_outstanding(voucher_type, voucher_no, account, party_type, party):
@ -1457,7 +1479,12 @@ def update_voucher_outstanding(voucher_type, voucher_no, account, party_type, pa
# on cancellation outstanding can be an empty list # on cancellation outstanding can be an empty list
voucher_outstanding = ple_query.get_voucher_outstandings(vouchers, common_filter=common_filter) 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] outstanding = voucher_outstanding[0]
ref_doc = frappe.get_doc(voucher_type, voucher_no) ref_doc = frappe.get_doc(voucher_type, voucher_no)

View File

@ -221,7 +221,7 @@ class TestAsset(AssetSetup):
asset.precision("gross_purchase_amount"), asset.precision("gross_purchase_amount"),
) )
pro_rata_amount, _, _ = asset.get_pro_rata_amt( pro_rata_amount, _, _ = asset.get_pro_rata_amt(
asset.finance_books[0], 9000, add_months(get_last_day(purchase_date), 1), date asset.finance_books[0], 9000, get_last_day(add_months(purchase_date, 1)), date
) )
pro_rata_amount = flt(pro_rata_amount, asset.precision("gross_purchase_amount")) pro_rata_amount = flt(pro_rata_amount, asset.precision("gross_purchase_amount"))
self.assertEquals(accumulated_depr_amount, 18000.00 + pro_rata_amount) self.assertEquals(accumulated_depr_amount, 18000.00 + pro_rata_amount)
@ -283,7 +283,7 @@ class TestAsset(AssetSetup):
self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Sold") self.assertEqual(frappe.db.get_value("Asset", asset.name, "status"), "Sold")
pro_rata_amount, _, _ = asset.get_pro_rata_amt( pro_rata_amount, _, _ = asset.get_pro_rata_amt(
asset.finance_books[0], 9000, add_months(get_last_day(purchase_date), 1), date asset.finance_books[0], 9000, get_last_day(add_months(purchase_date, 1)), date
) )
pro_rata_amount = flt(pro_rata_amount, asset.precision("gross_purchase_amount")) pro_rata_amount = flt(pro_rata_amount, asset.precision("gross_purchase_amount"))

View File

@ -135,6 +135,7 @@ class AssetRepair(AccountsController):
"basic_rate": stock_item.valuation_rate, "basic_rate": stock_item.valuation_rate,
"serial_no": stock_item.serial_no, "serial_no": stock_item.serial_no,
"cost_center": self.cost_center, "cost_center": self.cost_center,
"project": self.project,
}, },
) )

View File

@ -61,7 +61,9 @@ class AssetValueAdjustment(Document):
je.naming_series = depreciation_series je.naming_series = depreciation_series
je.posting_date = self.date je.posting_date = self.date
je.company = self.company je.company = self.company
je.remark = "Depreciation Entry against {0} worth {1}".format(self.asset, self.difference_amount) je.remark = _("Depreciation Entry against {0} worth {1}").format(
self.asset, self.difference_amount
)
je.finance_book = self.finance_book je.finance_book = self.finance_book
credit_entry = { credit_entry = {

View File

@ -101,6 +101,11 @@ frappe.ui.form.on("Purchase Order", {
erpnext.queries.setup_queries(frm, "Warehouse", function() { erpnext.queries.setup_queries(frm, "Warehouse", function() {
return erpnext.queries.warehouse(frm.doc); return erpnext.queries.warehouse(frm.doc);
}); });
// On cancel and amending a purchase order with advance payment, reset advance paid amount
if (frm.is_new()) {
frm.set_value("advance_paid", 0)
}
}, },
apply_tds: function(frm) { apply_tds: function(frm) {

View File

@ -12,43 +12,24 @@
"title", "title",
"naming_series", "naming_series",
"supplier", "supplier",
"get_items_from_open_material_requests",
"supplier_name", "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", "apply_tds",
"tax_withholding_category", "tax_withholding_category",
"is_subcontracted", "is_subcontracted",
"supplier_warehouse", "supplier_warehouse",
"column_break1",
"company",
"transaction_date",
"schedule_date",
"order_confirmation_no",
"order_confirmation_date",
"amended_from", "amended_from",
"accounting_dimensions_section", "accounting_dimensions_section",
"cost_center", "cost_center",
"dimension_col_break", "dimension_col_break",
"project", "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_and_price_list",
"currency", "currency",
"conversion_rate", "conversion_rate",
@ -57,7 +38,6 @@
"price_list_currency", "price_list_currency",
"plc_conversion_rate", "plc_conversion_rate",
"ignore_pricing_rule", "ignore_pricing_rule",
"section_break_45",
"before_items_section", "before_items_section",
"scan_barcode", "scan_barcode",
"set_from_warehouse", "set_from_warehouse",
@ -67,10 +47,11 @@
"items", "items",
"sb_last_purchase", "sb_last_purchase",
"total_qty", "total_qty",
"total_net_weight",
"column_break_40",
"base_total", "base_total",
"base_net_total", "base_net_total",
"column_break_26", "column_break_26",
"total_net_weight",
"total", "total",
"net_total", "net_total",
"section_break_48", "section_break_48",
@ -79,14 +60,13 @@
"set_reserve_warehouse", "set_reserve_warehouse",
"supplied_items", "supplied_items",
"taxes_section", "taxes_section",
"taxes_and_charges",
"column_break_53",
"tax_category", "tax_category",
"column_break_50", "column_break_50",
"shipping_rule", "shipping_rule",
"section_break_52", "section_break_52",
"taxes_and_charges",
"taxes", "taxes",
"sec_tax_breakup",
"other_charges_calculation",
"totals", "totals",
"base_taxes_and_charges_added", "base_taxes_and_charges_added",
"base_taxes_and_charges_deducted", "base_taxes_and_charges_deducted",
@ -95,12 +75,6 @@
"taxes_and_charges_added", "taxes_and_charges_added",
"taxes_and_charges_deducted", "taxes_and_charges_deducted",
"total_taxes_and_charges", "total_taxes_and_charges",
"discount_section",
"apply_discount_on",
"base_discount_amount",
"column_break_45",
"additional_discount_percentage",
"discount_amount",
"totals_section", "totals_section",
"base_grand_total", "base_grand_total",
"base_rounding_adjustment", "base_rounding_adjustment",
@ -113,37 +87,73 @@
"disable_rounded_total", "disable_rounded_total",
"in_words", "in_words",
"advance_paid", "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_schedule_section",
"payment_terms_template", "payment_terms_template",
"payment_schedule", "payment_schedule",
"terms_section_break",
"tc_name",
"terms",
"more_info_tab",
"tracking_section", "tracking_section",
"status", "status",
"column_break_75", "column_break_75",
"per_billed", "per_billed",
"per_received", "per_received",
"terms_section_break",
"tc_name",
"terms",
"column_break5", "column_break5",
"letter_head", "letter_head",
"select_print_heading",
"column_break_86",
"language",
"group_same_items", "group_same_items",
"column_break_86",
"select_print_heading",
"language",
"subscription_section", "subscription_section",
"from_date", "from_date",
"to_date", "to_date",
"column_break_97", "column_break_97",
"auto_repeat", "auto_repeat",
"update_auto_repeat_reference", "update_auto_repeat_reference",
"more_info", "additional_info_section",
"is_internal_supplier",
"represents_company",
"ref_sq", "ref_sq",
"column_break_74", "column_break_74",
"party_account_currency", "party_account_currency",
"is_internal_supplier",
"represents_company",
"inter_company_order_reference", "inter_company_order_reference",
"is_old_subcontracting_flow" "is_old_subcontracting_flow",
"dashboard"
], ],
"fields": [ "fields": [
{ {
@ -267,8 +277,9 @@
"read_only": 1 "read_only": 1
}, },
{ {
"depends_on": "eval:doc.customer",
"fieldname": "drop_ship", "fieldname": "drop_ship",
"fieldtype": "Section Break", "fieldtype": "Tab Break",
"label": "Drop Ship" "label": "Drop Ship"
}, },
{ {
@ -299,7 +310,6 @@
{ {
"fieldname": "customer_contact_display", "fieldname": "customer_contact_display",
"fieldtype": "Small Text", "fieldtype": "Small Text",
"hidden": 1,
"label": "Customer Contact", "label": "Customer Contact",
"print_hide": 1 "print_hide": 1
}, },
@ -319,10 +329,9 @@
"print_hide": 1 "print_hide": 1
}, },
{ {
"collapsible": 1,
"fieldname": "section_addresses", "fieldname": "section_addresses",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Address and Contact" "label": "Supplier Address"
}, },
{ {
"fieldname": "supplier_address", "fieldname": "supplier_address",
@ -445,7 +454,6 @@
"print_hide": 1 "print_hide": 1
}, },
{ {
"description": "Sets 'Warehouse' in each row of the Items table.",
"fieldname": "set_warehouse", "fieldname": "set_warehouse",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Set Target Warehouse", "label": "Set Target Warehouse",
@ -483,7 +491,6 @@
"allow_bulk_edit": 1, "allow_bulk_edit": 1,
"fieldname": "items", "fieldname": "items",
"fieldtype": "Table", "fieldtype": "Table",
"label": "Items",
"oldfieldname": "po_details", "oldfieldname": "po_details",
"oldfieldtype": "Table", "oldfieldtype": "Table",
"options": "Purchase Order Item", "options": "Purchase Order Item",
@ -570,6 +577,7 @@
"read_only": 1 "read_only": 1
}, },
{ {
"depends_on": "total_net_weight",
"fieldname": "total_net_weight", "fieldname": "total_net_weight",
"fieldtype": "Float", "fieldtype": "Float",
"label": "Total Net Weight", "label": "Total Net Weight",
@ -579,6 +587,8 @@
{ {
"fieldname": "taxes_section", "fieldname": "taxes_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hide_border": 1,
"label": "Taxes and Charges",
"oldfieldtype": "Section Break", "oldfieldtype": "Section Break",
"options": "fa fa-money" "options": "fa fa-money"
}, },
@ -633,7 +643,6 @@
{ {
"fieldname": "totals", "fieldname": "totals",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Taxes and Charges",
"oldfieldtype": "Section Break", "oldfieldtype": "Section Break",
"options": "fa fa-money" "options": "fa fa-money"
}, },
@ -698,7 +707,6 @@
"read_only": 1 "read_only": 1
}, },
{ {
"depends_on": "total_taxes_and_charges",
"fieldname": "total_taxes_and_charges", "fieldname": "total_taxes_and_charges",
"fieldtype": "Currency", "fieldtype": "Currency",
"label": "Total Taxes and Charges", "label": "Total Taxes and Charges",
@ -708,7 +716,6 @@
}, },
{ {
"collapsible": 1, "collapsible": 1,
"collapsible_depends_on": "apply_discount_on",
"fieldname": "discount_section", "fieldname": "discount_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Additional Discount" "label": "Additional Discount"
@ -773,7 +780,6 @@
"read_only": 1 "read_only": 1
}, },
{ {
"description": "In Words will be visible once you save the Purchase Order.",
"fieldname": "base_in_words", "fieldname": "base_in_words",
"fieldtype": "Data", "fieldtype": "Data",
"label": "In Words (Company Currency)", "label": "In Words (Company Currency)",
@ -851,7 +857,6 @@
"read_only": 1 "read_only": 1
}, },
{ {
"collapsible": 1,
"fieldname": "payment_schedule_section", "fieldname": "payment_schedule_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Payment Terms" "label": "Payment Terms"
@ -871,11 +876,10 @@
"print_hide": 1 "print_hide": 1
}, },
{ {
"collapsible": 1,
"collapsible_depends_on": "terms", "collapsible_depends_on": "terms",
"fieldname": "terms_section_break", "fieldname": "terms_section_break",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Terms and Conditions", "label": "Terms & Conditions",
"oldfieldtype": "Section Break", "oldfieldtype": "Section Break",
"options": "fa fa-legal" "options": "fa fa-legal"
}, },
@ -895,13 +899,6 @@
"oldfieldname": "terms", "oldfieldname": "terms",
"oldfieldtype": "Text Editor" "oldfieldtype": "Text Editor"
}, },
{
"collapsible": 1,
"fieldname": "more_info",
"fieldtype": "Section Break",
"label": "More Information",
"oldfieldtype": "Section Break"
},
{ {
"default": "Draft", "default": "Draft",
"fieldname": "status", "fieldname": "status",
@ -1023,7 +1020,7 @@
"collapsible": 1, "collapsible": 1,
"fieldname": "subscription_section", "fieldname": "subscription_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Subscription Section" "label": "Auto Repeat"
}, },
{ {
"allow_on_submit": 1, "allow_on_submit": 1,
@ -1098,7 +1095,9 @@
}, },
{ {
"fieldname": "before_items_section", "fieldname": "before_items_section",
"fieldtype": "Section Break" "fieldtype": "Section Break",
"hide_border": 1,
"label": "Items"
}, },
{ {
"fieldname": "items_col_break", "fieldname": "items_col_break",
@ -1133,10 +1132,6 @@
"label": "Tax Withholding Category", "label": "Tax Withholding Category",
"options": "Tax Withholding Category" "options": "Tax Withholding Category"
}, },
{
"fieldname": "section_break_45",
"fieldtype": "Section Break"
},
{ {
"collapsible": 1, "collapsible": 1,
"fieldname": "accounting_dimensions_section", "fieldname": "accounting_dimensions_section",
@ -1173,13 +1168,71 @@
"fieldtype": "Link", "fieldtype": "Link",
"label": "Set From Warehouse", "label": "Set From Warehouse",
"options": "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", "icon": "fa fa-file-text",
"idx": 105, "idx": 105,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2022-09-16 17:45:04.954055", "modified": "2022-10-11 13:01:41.674352",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Purchase Order", "name": "Purchase Order",

View File

@ -18,7 +18,7 @@ from erpnext.accounts.doctype.sales_invoice.sales_invoice import (
from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import ( from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category import (
get_party_tax_withholding_details, 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.buying.utils import check_on_hold_or_closed_status, validate_for_items
from erpnext.controllers.buying_controller import BuyingController from erpnext.controllers.buying_controller import BuyingController
from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
@ -349,7 +349,7 @@ class PurchaseOrder(BuyingController):
update_linked_doc(self.doctype, self.name, self.inter_company_order_reference) update_linked_doc(self.doctype, self.name, self.inter_company_order_reference)
def on_cancel(self): def on_cancel(self):
self.ignore_linked_doctypes = "Payment Ledger Entry" self.ignore_linked_doctypes = ("GL Entry", "Payment Ledger Entry")
super(PurchaseOrder, self).on_cancel() super(PurchaseOrder, self).on_cancel()
if self.is_against_so(): if self.is_against_so():
@ -558,6 +558,7 @@ def get_mapped_purchase_invoice(source_name, target_doc=None, ignore_permissions
target.set_advances() target.set_advances()
target.set_payment_schedule() target.set_payment_schedule()
target.credit_to = get_party_account("Supplier", source.supplier, source.company)
def update_item(obj, target, source_parent): def update_item(obj, target, source_parent):
target.amount = flt(obj.amount) - flt(obj.billed_amt) target.amount = flt(obj.amount) - flt(obj.billed_amt)

View File

@ -777,6 +777,7 @@
}, },
{ {
"collapsible": 1, "collapsible": 1,
"collapsible_depends_on": "eval: doc.margin_type || doc.discount_amount",
"fieldname": "discount_and_margin_section", "fieldname": "discount_and_margin_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Discount and Margin" "label": "Discount and Margin"
@ -894,7 +895,7 @@
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2022-09-07 11:12:38.634976", "modified": "2022-10-26 16:47:41.364387",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Purchase Order Item", "name": "Purchase Order Item",

View File

@ -3,6 +3,7 @@
import frappe import frappe
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
from frappe.test_runner import make_test_records from frappe.test_runner import make_test_records
from erpnext.accounts.party import get_due_date from erpnext.accounts.party import get_due_date
@ -152,6 +153,40 @@ class TestSupplier(FrappeTestCase):
# Rollback # Rollback
address.delete() address.delete()
def test_serach_fields_for_supplier(self):
from erpnext.controllers.queries import supplier_query
supplier_name = create_supplier(supplier_name="Test Supplier 1").name
make_property_setter(
"Supplier", None, "search_fields", "supplier_group", "Data", for_doctype="Doctype"
)
data = supplier_query(
"Supplier", supplier_name, "name", 0, 20, filters={"name": supplier_name}, as_dict=True
)
self.assertEqual(data[0].name, supplier_name)
self.assertEqual(data[0].supplier_group, "Services")
self.assertTrue("supplier_type" not in data[0])
make_property_setter(
"Supplier",
None,
"search_fields",
"supplier_group, supplier_type",
"Data",
for_doctype="Doctype",
)
data = supplier_query(
"Supplier", supplier_name, "name", 0, 20, filters={"name": supplier_name}, as_dict=True
)
self.assertEqual(data[0].name, supplier_name)
self.assertEqual(data[0].supplier_group, "Services")
self.assertEqual(data[0].supplier_type, "Company")
self.assertTrue("supplier_type" in data[0])
def create_supplier(**args): def create_supplier(**args):
args = frappe._dict(args) args = frappe._dict(args)

View File

@ -13,19 +13,13 @@
"naming_series", "naming_series",
"supplier", "supplier",
"supplier_name", "supplier_name",
"column_break1",
"company", "company",
"column_break1",
"status",
"transaction_date", "transaction_date",
"valid_till", "valid_till",
"quotation_number", "quotation_number",
"amended_from", "amended_from",
"address_section",
"supplier_address",
"contact_person",
"address_display",
"contact_display",
"contact_mobile",
"contact_email",
"currency_and_price_list", "currency_and_price_list",
"currency", "currency",
"conversion_rate", "conversion_rate",
@ -36,25 +30,23 @@
"ignore_pricing_rule", "ignore_pricing_rule",
"items_section", "items_section",
"items", "items",
"pricing_rule_details",
"pricing_rules",
"section_break_22", "section_break_22",
"total_qty", "total_qty",
"total_net_weight",
"column_break_26",
"base_total", "base_total",
"base_net_total", "base_net_total",
"column_break_24", "column_break_24",
"total", "total",
"net_total", "net_total",
"total_net_weight",
"taxes_section", "taxes_section",
"taxes_and_charges",
"column_break_34",
"tax_category", "tax_category",
"column_break_36", "column_break_36",
"shipping_rule", "shipping_rule",
"section_break_38", "section_break_38",
"taxes_and_charges",
"taxes", "taxes",
"tax_breakup",
"other_charges_calculation",
"totals", "totals",
"base_taxes_and_charges_added", "base_taxes_and_charges_added",
"base_taxes_and_charges_deducted", "base_taxes_and_charges_deducted",
@ -80,24 +72,36 @@
"rounded_total", "rounded_total",
"in_words", "in_words",
"disable_rounded_total", "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", "tc_name",
"terms", "terms",
"more_info_tab",
"printing_settings", "printing_settings",
"select_print_heading",
"group_same_items",
"column_break_72",
"letter_head", "letter_head",
"group_same_items",
"column_break_85",
"select_print_heading",
"language", "language",
"subscription_section", "subscription_section",
"auto_repeat", "auto_repeat",
"update_auto_repeat_reference", "update_auto_repeat_reference",
"more_info", "more_info",
"status",
"column_break_57",
"is_subcontracted", "is_subcontracted",
"reference", "column_break_57",
"opportunity" "opportunity",
"connections_tab"
], ],
"fields": [ "fields": [
{ {
@ -146,7 +150,7 @@
"fieldname": "supplier_name", "fieldname": "supplier_name",
"fieldtype": "Data", "fieldtype": "Data",
"in_global_search": 1, "in_global_search": 1,
"label": "Name", "label": "Supplier Name",
"read_only": 1 "read_only": 1
}, },
{ {
@ -193,12 +197,6 @@
"reqd": 1, "reqd": 1,
"search_index": 1 "search_index": 1
}, },
{
"collapsible": 1,
"fieldname": "address_section",
"fieldtype": "Section Break",
"label": "Address and Contact"
},
{ {
"fieldname": "supplier_address", "fieldname": "supplier_address",
"fieldtype": "Link", "fieldtype": "Link",
@ -309,6 +307,8 @@
{ {
"fieldname": "items_section", "fieldname": "items_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hide_border": 1,
"label": "Items",
"oldfieldtype": "Section Break", "oldfieldtype": "Section Break",
"options": "fa fa-shopping-cart" "options": "fa fa-shopping-cart"
}, },
@ -316,7 +316,6 @@
"allow_bulk_edit": 1, "allow_bulk_edit": 1,
"fieldname": "items", "fieldname": "items",
"fieldtype": "Table", "fieldtype": "Table",
"label": "Items",
"oldfieldname": "po_details", "oldfieldname": "po_details",
"oldfieldtype": "Table", "oldfieldtype": "Table",
"options": "Supplier Quotation Item", "options": "Supplier Quotation Item",
@ -394,6 +393,7 @@
{ {
"fieldname": "taxes_section", "fieldname": "taxes_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hide_border": 1,
"label": "Taxes and Charges", "label": "Taxes and Charges",
"oldfieldtype": "Section Break", "oldfieldtype": "Section Break",
"options": "fa fa-money" "options": "fa fa-money"
@ -417,7 +417,8 @@
}, },
{ {
"fieldname": "section_break_38", "fieldname": "section_break_38",
"fieldtype": "Section Break" "fieldtype": "Section Break",
"hide_border": 1
}, },
{ {
"fieldname": "taxes_and_charges", "fieldname": "taxes_and_charges",
@ -523,7 +524,6 @@
}, },
{ {
"collapsible": 1, "collapsible": 1,
"collapsible_depends_on": "discount_amount",
"fieldname": "section_break_41", "fieldname": "section_break_41",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Additional Discount" "label": "Additional Discount"
@ -563,7 +563,8 @@
}, },
{ {
"fieldname": "section_break_46", "fieldname": "section_break_46",
"fieldtype": "Section Break" "fieldtype": "Section Break",
"label": "Totals"
}, },
{ {
"fieldname": "base_grand_total", "fieldname": "base_grand_total",
@ -654,19 +655,10 @@
"fieldtype": "Check", "fieldtype": "Check",
"label": "Disable Rounded Total" "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", "fieldname": "tc_name",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Terms", "label": "Terms Template",
"oldfieldname": "tc_name", "oldfieldname": "tc_name",
"oldfieldtype": "Link", "oldfieldtype": "Link",
"options": "Terms and Conditions", "options": "Terms and Conditions",
@ -729,7 +721,7 @@
{ {
"fieldname": "subscription_section", "fieldname": "subscription_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Auto Repeat Section" "label": "Auto Repeat"
}, },
{ {
"fieldname": "auto_repeat", "fieldname": "auto_repeat",
@ -751,7 +743,7 @@
"collapsible": 1, "collapsible": 1,
"fieldname": "more_info", "fieldname": "more_info",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "More Information", "label": "Additional Info",
"oldfieldtype": "Section Break", "oldfieldtype": "Section Break",
"options": "fa fa-file-text" "options": "fa fa-file-text"
}, },
@ -779,11 +771,6 @@
"label": "Is Subcontracted", "label": "Is Subcontracted",
"print_hide": 1 "print_hide": 1
}, },
{
"fieldname": "reference",
"fieldtype": "Section Break",
"label": "Reference"
},
{ {
"fieldname": "opportunity", "fieldname": "opportunity",
"fieldtype": "Link", "fieldtype": "Link",
@ -803,6 +790,39 @@
"fieldname": "quotation_number", "fieldname": "quotation_number",
"fieldtype": "Data", "fieldtype": "Data",
"label": "Quotation Number" "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", "icon": "fa fa-shopping-cart",
@ -810,7 +830,7 @@
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2022-03-14 16:13:20.284572", "modified": "2022-09-27 18:20:09.462037",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Supplier Quotation", "name": "Supplier Quotation",

View File

@ -127,32 +127,27 @@ def get_columns(filters):
return columns return columns
def get_conditions(filters): def apply_filters_on_query(filters, parent, child, query):
conditions = ""
if filters.get("company"): 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"): if filters.get("cost_center") or filters.get("project"):
conditions += """ query = query.where(
AND (child.`cost_center`=%s OR child.`project`=%s) (child.cost_center == filters.get("cost_center")) | (child.project == filters.get("project"))
""" % (
frappe.db.escape(filters.get("cost_center")),
frappe.db.escape(filters.get("project")),
) )
if filters.get("from_date"): 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"): if filters.get("to_date"):
conditions += " AND parent.transaction_date<='%s'" % filters.get("to_date") query = query.where(parent.transaction_date <= filters.get("to_date"))
return conditions
return query
def get_data(filters): def get_data(filters):
conditions = get_conditions(filters) purchase_order_entry = get_po_entries(filters)
purchase_order_entry = get_po_entries(conditions) mr_records, procurement_record_against_mr = get_mapped_mr_details(filters)
mr_records, procurement_record_against_mr = get_mapped_mr_details(conditions)
pr_records = get_mapped_pr_records() pr_records = get_mapped_pr_records()
pi_records = get_mapped_pi_records() pi_records = get_mapped_pi_records()
@ -187,11 +182,15 @@ def get_data(filters):
return procurement_record return procurement_record
def get_mapped_mr_details(conditions): def get_mapped_mr_details(filters):
mr_records = {} mr_records = {}
mr_details = frappe.db.sql( parent = frappe.qb.DocType("Material Request")
""" child = frappe.qb.DocType("Material Request Item")
SELECT
query = (
frappe.qb.from_(parent)
.from_(child)
.select(
parent.transaction_date, parent.transaction_date,
parent.per_ordered, parent.per_ordered,
parent.owner, parent.owner,
@ -203,18 +202,13 @@ def get_mapped_mr_details(conditions):
child.uom, child.uom,
parent.status, parent.status,
child.project, child.project,
child.cost_center child.cost_center,
FROM `tabMaterial Request` parent, `tabMaterial Request Item` child )
WHERE .where((parent.per_ordered >= 0) & (parent.name == child.parent) & (parent.docstatus == 1))
parent.per_ordered>=0 )
AND parent.name=child.parent query = apply_filters_on_query(filters, parent, child, query)
AND parent.docstatus=1
{conditions} mr_details = query.run(as_dict=True)
""".format(
conditions=conditions
),
as_dict=1,
) # nosec
procurement_record_against_mr = [] procurement_record_against_mr = []
for record in mr_details: for record in mr_details:
@ -241,46 +235,49 @@ def get_mapped_mr_details(conditions):
def get_mapped_pi_records(): def get_mapped_pi_records():
return frappe._dict( po = frappe.qb.DocType("Purchase Order")
frappe.db.sql( pi_item = frappe.qb.DocType("Purchase Invoice Item")
""" pi_records = (
SELECT frappe.qb.from_(pi_item)
pi_item.po_detail, .inner_join(po)
pi_item.base_amount .on(pi_item.purchase_order == po.name)
FROM `tabPurchase Invoice Item` as pi_item .select(pi_item.po_detail, pi_item.base_amount)
INNER JOIN `tabPurchase Order` as po .where(
ON pi_item.`purchase_order` = po.`name` (pi_item.docstatus == 1)
WHERE & (po.status.notin(("Closed", "Completed", "Cancelled")))
pi_item.docstatus = 1 & (pi_item.po_detail.isnotnull())
AND po.status not in ('Closed','Completed','Cancelled')
AND pi_item.po_detail IS NOT NULL
"""
) )
) ).run()
return frappe._dict(pi_records)
def get_mapped_pr_records(): def get_mapped_pr_records():
return frappe._dict( pr = frappe.qb.DocType("Purchase Receipt")
frappe.db.sql( pr_item = frappe.qb.DocType("Purchase Receipt Item")
""" pr_records = (
SELECT frappe.qb.from_(pr)
pr_item.purchase_order_item, .from_(pr_item)
pr.posting_date .select(pr_item.purchase_order_item, pr.posting_date)
FROM `tabPurchase Receipt` pr, `tabPurchase Receipt Item` pr_item .where(
WHERE (pr.docstatus == 1)
pr.docstatus=1 & (pr.name == pr_item.parent)
AND pr.name=pr_item.parent & (pr_item.purchase_order_item.isnotnull())
AND pr_item.purchase_order_item IS NOT NULL & (pr.status.notin(("Closed", "Completed", "Cancelled")))
AND pr.status not in ('Closed','Completed','Cancelled')
"""
) )
) ).run()
return frappe._dict(pr_records)
def get_po_entries(conditions): def get_po_entries(filters):
return frappe.db.sql( parent = frappe.qb.DocType("Purchase Order")
""" child = frappe.qb.DocType("Purchase Order Item")
SELECT
query = (
frappe.qb.from_(parent)
.from_(child)
.select(
child.name, child.name,
child.parent, child.parent,
child.cost_center, child.cost_center,
@ -297,17 +294,15 @@ def get_po_entries(conditions):
parent.transaction_date, parent.transaction_date,
parent.supplier, parent.supplier,
parent.status, parent.status,
parent.owner parent.owner,
FROM `tabPurchase Order` parent, `tabPurchase Order Item` child )
WHERE .where(
parent.docstatus = 1 (parent.docstatus == 1)
AND parent.name = child.parent & (parent.name == child.parent)
AND parent.status not in ('Closed','Completed','Cancelled') & (parent.status.notin(("Closed", "Completed", "Cancelled")))
{conditions} )
GROUP BY .groupby(parent.name, child.item_code)
parent.name, child.item_code )
""".format( query = apply_filters_on_query(filters, parent, child, query)
conditions=conditions
), return query.run(as_dict=True)
as_dict=1,
) # nosec

View File

@ -6,6 +6,7 @@ import copy
import frappe import frappe
from frappe import _ from frappe import _
from frappe.query_builder.functions import IfNull
from frappe.utils import date_diff, flt, getdate from frappe.utils import date_diff, flt, getdate
@ -16,9 +17,7 @@ def execute(filters=None):
validate_filters(filters) validate_filters(filters)
columns = get_columns(filters) columns = get_columns(filters)
conditions = get_conditions(filters) data = get_data(filters)
data = get_data(conditions, filters)
if not data: if not data:
return [], [], None, [] return [], [], None, []
@ -37,60 +36,61 @@ def validate_filters(filters):
frappe.throw(_("To Date cannot be before From Date.")) frappe.throw(_("To Date cannot be before From Date."))
def get_conditions(filters): def get_data(filters):
conditions = "" po = frappe.qb.DocType("Purchase Order")
if filters.get("from_date") and filters.get("to_date"): po_item = frappe.qb.DocType("Purchase Order Item")
conditions += " and po.transaction_date between %(from_date)s and %(to_date)s" 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): 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"): if filters.get("status"):
conditions += " and po.status in %(status)s" query = query.where(po.status.isin(filters.get("status")))
if filters.get("project"): if filters.get("project"):
conditions += " and poi.project = %(project)s" query = query.where(po_item.project == filters.get("project"))
return conditions data = query.run(as_dict=True)
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,
)
return data return data

View File

@ -16,8 +16,7 @@ def execute(filters=None):
return [], [] return [], []
columns = get_columns(filters) columns = get_columns(filters)
conditions = get_conditions(filters) supplier_quotation_data = get_data(filters)
supplier_quotation_data = get_data(filters, conditions)
data, chart_data = prepare_data(supplier_quotation_data, filters) data, chart_data = prepare_data(supplier_quotation_data, filters)
message = get_message() message = get_message()
@ -25,50 +24,51 @@ def execute(filters=None):
return columns, data, message, chart_data return columns, data, message, chart_data
def get_conditions(filters): def get_data(filters):
conditions = "" 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"): 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"): 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"): 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"): 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"): if not filters.get("include_expired"):
conditions += " AND sq.status != 'Expired'" query = query.where(sq.status != "Expired")
return conditions supplier_quotation_data = query.run(as_dict=True)
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,
)
return supplier_quotation_data return supplier_quotation_data

View File

@ -78,18 +78,16 @@ def lead_query(doctype, txt, searchfield, start, page_len, filters):
@frappe.whitelist() @frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs @frappe.validate_and_sanitize_search_inputs
def customer_query(doctype, txt, searchfield, start, page_len, filters): def customer_query(doctype, txt, searchfield, start, page_len, filters, as_dict=False):
doctype = "Customer" doctype = "Customer"
conditions = [] conditions = []
cust_master_name = frappe.defaults.get_user_default("cust_master_name") cust_master_name = frappe.defaults.get_user_default("cust_master_name")
if cust_master_name == "Customer Name": fields = ["name"]
fields = ["name", "customer_group", "territory"] if cust_master_name != "Customer Name":
else: fields = ["customer_name"]
fields = ["name", "customer_name", "customer_group", "territory"]
fields = get_fields(doctype, fields) fields = get_fields(doctype, fields)
searchfields = frappe.get_meta(doctype).get_search_fields() searchfields = frappe.get_meta(doctype).get_search_fields()
searchfields = " or ".join(field + " like %(txt)s" for field in searchfields) searchfields = " or ".join(field + " like %(txt)s" for field in searchfields)
@ -112,20 +110,20 @@ def customer_query(doctype, txt, searchfield, start, page_len, filters):
} }
), ),
{"txt": "%%%s%%" % txt, "_txt": txt.replace("%", ""), "start": start, "page_len": page_len}, {"txt": "%%%s%%" % txt, "_txt": txt.replace("%", ""), "start": start, "page_len": page_len},
as_dict=as_dict,
) )
# searches for supplier # searches for supplier
@frappe.whitelist() @frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs @frappe.validate_and_sanitize_search_inputs
def supplier_query(doctype, txt, searchfield, start, page_len, filters): def supplier_query(doctype, txt, searchfield, start, page_len, filters, as_dict=False):
doctype = "Supplier" doctype = "Supplier"
supp_master_name = frappe.defaults.get_user_default("supp_master_name") supp_master_name = frappe.defaults.get_user_default("supp_master_name")
if supp_master_name == "Supplier Name": fields = ["name"]
fields = ["name", "supplier_group"] if supp_master_name != "Supplier Name":
else: fields = ["supplier_name"]
fields = ["name", "supplier_name", "supplier_group"]
fields = get_fields(doctype, fields) fields = get_fields(doctype, fields)
@ -145,6 +143,7 @@ def supplier_query(doctype, txt, searchfield, start, page_len, filters):
**{"field": ", ".join(fields), "key": searchfield, "mcond": get_match_cond(doctype)} **{"field": ", ".join(fields), "key": searchfield, "mcond": get_match_cond(doctype)}
), ),
{"txt": "%%%s%%" % txt, "_txt": txt.replace("%", ""), "start": start, "page_len": page_len}, {"txt": "%%%s%%" % txt, "_txt": txt.replace("%", ""), "start": start, "page_len": page_len},
as_dict=as_dict,
) )

View File

@ -442,11 +442,17 @@ class SellingController(StockController):
# For internal transfers use incoming rate as the valuation rate # For internal transfers use incoming rate as the valuation rate
if self.is_internal_transfer(): if self.is_internal_transfer():
if d.doctype == "Packed Item": 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: if d.incoming_rate != incoming_rate:
d.incoming_rate = incoming_rate d.incoming_rate = incoming_rate
else: 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: if d.rate != rate:
d.rate = rate d.rate = rate
frappe.msgprint( frappe.msgprint(

View File

@ -142,13 +142,15 @@ class StockController(AccountsController):
warehouse_with_no_account = [] warehouse_with_no_account = []
precision = self.get_debit_field_precision() precision = self.get_debit_field_precision()
for item_row in voucher_details: for item_row in voucher_details:
sle_list = sle_map.get(item_row.name) sle_list = sle_map.get(item_row.name)
sle_rounding_diff = 0.0
if sle_list: if sle_list:
for sle in sle_list: for sle in sle_list:
if warehouse_account.get(sle.warehouse): if warehouse_account.get(sle.warehouse):
# from warehouse account # from warehouse account
sle_rounding_diff += flt(sle.stock_value_difference)
self.check_expense_account(item_row) self.check_expense_account(item_row)
# expense account/ target_warehouse / source_warehouse # expense account/ target_warehouse / source_warehouse
@ -191,6 +193,46 @@ class StockController(AccountsController):
elif sle.warehouse not in warehouse_with_no_account: elif sle.warehouse not in warehouse_with_no_account:
warehouse_with_no_account.append(sle.warehouse) 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: if warehouse_with_no_account:
for wh in warehouse_with_no_account: for wh in warehouse_with_no_account:
if frappe.db.get_value("Warehouse", wh, "company"): if frappe.db.get_value("Warehouse", wh, "company"):

View File

@ -900,24 +900,33 @@ class calculate_taxes_and_totals(object):
self.doc.other_charges_calculation = get_itemised_tax_breakup_html(self.doc) self.doc.other_charges_calculation = get_itemised_tax_breakup_html(self.doc)
def set_total_amount_to_default_mop(self, total_amount_to_pay): def set_total_amount_to_default_mop(self, total_amount_to_pay):
default_mode_of_payment = frappe.db.get_value( total_paid_amount = 0
"POS Payment Method", for payment in self.doc.get("payments"):
{"parent": self.doc.pos_profile, "default": 1}, total_paid_amount += (
["mode_of_payment"], payment.amount if self.doc.party_account_currency == self.doc.currency else payment.base_amount
as_dict=1,
)
if default_mode_of_payment:
self.doc.payments = []
self.doc.append(
"payments",
{
"mode_of_payment": default_mode_of_payment.mode_of_payment,
"amount": total_amount_to_pay,
"default": 1,
},
) )
pending_amount = total_amount_to_pay - total_paid_amount
if pending_amount > 0:
default_mode_of_payment = frappe.db.get_value(
"POS Payment Method",
{"parent": self.doc.pos_profile, "default": 1},
["mode_of_payment"],
as_dict=1,
)
if default_mode_of_payment:
self.doc.payments = []
self.doc.append(
"payments",
{
"mode_of_payment": default_mode_of_payment.mode_of_payment,
"amount": pending_amount,
"default": 1,
},
)
def get_itemised_tax_breakup_html(doc): def get_itemised_tax_breakup_html(doc):
if not doc.taxes: if not doc.taxes:

View File

@ -375,7 +375,7 @@
"depends_on": "eval:!doc.__islocal", "depends_on": "eval:!doc.__islocal",
"fieldname": "notes_tab", "fieldname": "notes_tab",
"fieldtype": "Tab Break", "fieldtype": "Tab Break",
"label": "Notes" "label": "Comments"
}, },
{ {
"collapsible": 1, "collapsible": 1,
@ -506,7 +506,7 @@
{ {
"fieldname": "dashboard_tab", "fieldname": "dashboard_tab",
"fieldtype": "Tab Break", "fieldtype": "Tab Break",
"label": "Dashboard", "label": "Connections",
"show_dashboard": 1 "show_dashboard": 1
} }
], ],
@ -514,7 +514,7 @@
"idx": 5, "idx": 5,
"image_field": "image", "image_field": "image",
"links": [], "links": [],
"modified": "2022-08-09 18:26:17.101521", "modified": "2022-10-13 12:42:04.277879",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "CRM", "module": "CRM",
"name": "Lead", "name": "Lead",

View File

@ -544,14 +544,14 @@
"depends_on": "eval:!doc.__islocal", "depends_on": "eval:!doc.__islocal",
"fieldname": "dashboard_tab", "fieldname": "dashboard_tab",
"fieldtype": "Tab Break", "fieldtype": "Tab Break",
"label": "Dashboard", "label": "Connections",
"show_dashboard": 1 "show_dashboard": 1
}, },
{ {
"depends_on": "eval:!doc.__islocal", "depends_on": "eval:!doc.__islocal",
"fieldname": "notes_tab", "fieldname": "notes_tab",
"fieldtype": "Tab Break", "fieldtype": "Tab Break",
"label": "Notes" "label": "Comments"
}, },
{ {
"fieldname": "notes_html", "fieldname": "notes_html",
@ -622,7 +622,7 @@
"icon": "fa fa-info-sign", "icon": "fa fa-info-sign",
"idx": 195, "idx": 195,
"links": [], "links": [],
"modified": "2022-08-09 18:26:37.235964", "modified": "2022-10-13 12:42:21.545636",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "CRM", "module": "CRM",
"name": "Opportunity", "name": "Opportunity",

View File

@ -128,7 +128,7 @@
"depends_on": "eval:!doc.__islocal", "depends_on": "eval:!doc.__islocal",
"fieldname": "notes_section", "fieldname": "notes_section",
"fieldtype": "Tab Break", "fieldtype": "Tab Break",
"label": "Notes" "label": "Comments"
}, },
{ {
"depends_on": "eval: !doc.__islocal", "depends_on": "eval: !doc.__islocal",
@ -218,7 +218,7 @@
], ],
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"links": [], "links": [],
"modified": "2022-08-09 18:26:56.950185", "modified": "2022-10-13 12:29:33.674561",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "CRM", "module": "CRM",
"name": "Prospect", "name": "Prospect",

View File

@ -188,7 +188,8 @@
"in_list_view": 1, "in_list_view": 1,
"label": "Item Group", "label": "Item Group",
"options": "Item Group", "options": "Item Group",
"read_only": 1 "read_only": 1,
"search_index": 1
}, },
{ {
"default": "1", "default": "1",
@ -234,7 +235,8 @@
"fieldname": "brand", "fieldname": "brand",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Brand", "label": "Brand",
"options": "Brand" "options": "Brand",
"search_index": 1
}, },
{ {
"collapsible": 1, "collapsible": 1,
@ -346,7 +348,7 @@
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"links": [], "links": [],
"make_attachments_public": 1, "make_attachments_public": 1,
"modified": "2022-09-13 04:05:11.614087", "modified": "2022-09-30 04:01:52.090732",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "E-commerce", "module": "E-commerce",
"name": "Website Item", "name": "Website Item",

View File

@ -403,9 +403,6 @@ def on_doctype_update():
# since route is a Text column, it needs a length for indexing # since route is a Text column, it needs a length for indexing
frappe.db.add_index("Website Item", ["route(500)"]) frappe.db.add_index("Website Item", ["route(500)"])
frappe.db.add_index("Website Item", ["item_group"])
frappe.db.add_index("Website Item", ["brand"])
def check_if_user_is_customer(user=None): def check_if_user_is_customer(user=None):
from frappe.contacts.doctype.contact.contact import get_contact_name from frappe.contacts.doctype.contact.contact import get_contact_name

View File

@ -274,8 +274,6 @@ has_website_permission = {
"Timesheet": "erpnext.controllers.website_list_for_contact.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" before_tests = "erpnext.setup.utils.before_tests"
standard_queries = { standard_queries = {

View File

@ -61,6 +61,10 @@ frappe.ui.form.on('Loan', {
}, },
refresh: function (frm) { refresh: function (frm) {
if (frm.doc.repayment_schedule_type == "Pro-rated calendar months") {
frm.set_df_property("repayment_start_date", "label", "Interest Calculation Start Date");
}
if (frm.doc.docstatus == 1) { if (frm.doc.docstatus == 1) {
if (["Disbursed", "Partially Disbursed"].includes(frm.doc.status) && (!frm.doc.repay_from_salary)) { if (["Disbursed", "Partially Disbursed"].includes(frm.doc.status) && (!frm.doc.repay_from_salary)) {
frm.add_custom_button(__('Request Loan Closure'), function() { frm.add_custom_button(__('Request Loan Closure'), function() {
@ -103,6 +107,14 @@ frappe.ui.form.on('Loan', {
frm.trigger("toggle_fields"); frm.trigger("toggle_fields");
}, },
repayment_schedule_type: function(frm) {
if (frm.doc.repayment_schedule_type == "Pro-rated calendar months") {
frm.set_df_property("repayment_start_date", "label", "Interest Calculation Start Date");
} else {
frm.set_df_property("repayment_start_date", "label", "Repayment Start Date");
}
},
loan_type: function(frm) { loan_type: function(frm) {
frm.toggle_reqd("repayment_method", frm.doc.is_term_loan); frm.toggle_reqd("repayment_method", frm.doc.is_term_loan);
frm.toggle_display("repayment_method", frm.doc.is_term_loan); frm.toggle_display("repayment_method", frm.doc.is_term_loan);

View File

@ -18,6 +18,7 @@
"status", "status",
"section_break_8", "section_break_8",
"loan_type", "loan_type",
"repayment_schedule_type",
"loan_amount", "loan_amount",
"rate_of_interest", "rate_of_interest",
"is_secured_loan", "is_secured_loan",
@ -158,7 +159,8 @@
"depends_on": "is_term_loan", "depends_on": "is_term_loan",
"fieldname": "repayment_start_date", "fieldname": "repayment_start_date",
"fieldtype": "Date", "fieldtype": "Date",
"label": "Repayment Start Date" "label": "Repayment Start Date",
"mandatory_depends_on": "is_term_loan"
}, },
{ {
"fieldname": "column_break_11", "fieldname": "column_break_11",
@ -402,12 +404,20 @@
"fieldname": "is_npa", "fieldname": "is_npa",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Is NPA" "label": "Is NPA"
},
{
"depends_on": "is_term_loan",
"fetch_from": "loan_type.repayment_schedule_type",
"fieldname": "repayment_schedule_type",
"fieldtype": "Data",
"label": "Repayment Schedule Type",
"read_only": 1
} }
], ],
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2022-07-12 11:50:31.957360", "modified": "2022-09-30 10:36:47.902903",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Loan Management", "module": "Loan Management",
"name": "Loan", "name": "Loan",

View File

@ -7,7 +7,16 @@ import math
import frappe import frappe
from frappe import _ from frappe import _
from frappe.utils import add_months, flt, get_last_day, getdate, now_datetime, nowdate from frappe.utils import (
add_days,
add_months,
date_diff,
flt,
get_last_day,
getdate,
now_datetime,
nowdate,
)
import erpnext import erpnext
from erpnext.accounts.doctype.journal_entry.journal_entry import get_payment_entry from erpnext.accounts.doctype.journal_entry.journal_entry import get_payment_entry
@ -107,30 +116,81 @@ class Loan(AccountsController):
if not self.repayment_start_date: if not self.repayment_start_date:
frappe.throw(_("Repayment Start Date is mandatory for term loans")) frappe.throw(_("Repayment Start Date is mandatory for term loans"))
schedule_type_details = frappe.db.get_value(
"Loan Type", self.loan_type, ["repayment_schedule_type", "repayment_date_on"], as_dict=1
)
self.repayment_schedule = [] self.repayment_schedule = []
payment_date = self.repayment_start_date payment_date = self.repayment_start_date
balance_amount = self.loan_amount balance_amount = self.loan_amount
while balance_amount > 0:
interest_amount = flt(balance_amount * flt(self.rate_of_interest) / (12 * 100))
principal_amount = self.monthly_repayment_amount - interest_amount
balance_amount = flt(balance_amount + interest_amount - self.monthly_repayment_amount)
if balance_amount < 0:
principal_amount += balance_amount
balance_amount = 0.0
total_payment = principal_amount + interest_amount while balance_amount > 0:
self.append( interest_amount, principal_amount, balance_amount, total_payment = self.get_amounts(
"repayment_schedule", payment_date,
{ balance_amount,
"payment_date": payment_date, schedule_type_details.repayment_schedule_type,
"principal_amount": principal_amount, schedule_type_details.repayment_date_on,
"interest_amount": interest_amount,
"total_payment": total_payment,
"balance_loan_amount": balance_amount,
},
) )
next_payment_date = add_single_month(payment_date)
payment_date = next_payment_date if schedule_type_details.repayment_schedule_type == "Pro-rated calendar months":
next_payment_date = get_last_day(payment_date)
if schedule_type_details.repayment_date_on == "Start of the next month":
next_payment_date = add_days(next_payment_date, 1)
payment_date = next_payment_date
self.add_repayment_schedule_row(
payment_date, principal_amount, interest_amount, total_payment, balance_amount
)
if (
schedule_type_details.repayment_schedule_type == "Monthly as per repayment start date"
or schedule_type_details.repayment_date_on == "End of the current month"
):
next_payment_date = add_single_month(payment_date)
payment_date = next_payment_date
def get_amounts(self, payment_date, balance_amount, schedule_type, repayment_date_on):
if schedule_type == "Monthly as per repayment start date":
days = 1
months = 12
else:
expected_payment_date = get_last_day(payment_date)
if repayment_date_on == "Start of the next month":
expected_payment_date = add_days(expected_payment_date, 1)
if expected_payment_date == payment_date:
# using 30 days for calculating interest for all full months
days = 30
months = 365
else:
days = date_diff(get_last_day(payment_date), payment_date)
months = 365
interest_amount = flt(balance_amount * flt(self.rate_of_interest) * days / (months * 100))
principal_amount = self.monthly_repayment_amount - interest_amount
balance_amount = flt(balance_amount + interest_amount - self.monthly_repayment_amount)
if balance_amount < 0:
principal_amount += balance_amount
balance_amount = 0.0
total_payment = principal_amount + interest_amount
return interest_amount, principal_amount, balance_amount, total_payment
def add_repayment_schedule_row(
self, payment_date, principal_amount, interest_amount, total_payment, balance_loan_amount
):
self.append(
"repayment_schedule",
{
"payment_date": payment_date,
"principal_amount": principal_amount,
"interest_amount": interest_amount,
"total_payment": total_payment,
"balance_loan_amount": balance_loan_amount,
},
)
def set_repayment_period(self): def set_repayment_period(self):
if self.repayment_method == "Repay Fixed Amount per Period": if self.repayment_method == "Repay Fixed Amount per Period":

View File

@ -4,7 +4,16 @@
import unittest import unittest
import frappe import frappe
from frappe.utils import add_days, add_months, add_to_date, date_diff, flt, get_datetime, nowdate from frappe.utils import (
add_days,
add_months,
add_to_date,
date_diff,
flt,
format_date,
get_datetime,
nowdate,
)
from erpnext.loan_management.doctype.loan.loan import ( from erpnext.loan_management.doctype.loan.loan import (
make_loan_write_off, make_loan_write_off,
@ -47,6 +56,51 @@ class TestLoan(unittest.TestCase):
loan_account="Loan Account - _TC", loan_account="Loan Account - _TC",
interest_income_account="Interest Income Account - _TC", interest_income_account="Interest Income Account - _TC",
penalty_income_account="Penalty Income Account - _TC", penalty_income_account="Penalty Income Account - _TC",
repayment_schedule_type="Monthly as per repayment start date",
)
create_loan_type(
"Term Loan Type 1",
12000,
7.5,
is_term_loan=1,
mode_of_payment="Cash",
disbursement_account="Disbursement Account - _TC",
payment_account="Payment Account - _TC",
loan_account="Loan Account - _TC",
interest_income_account="Interest Income Account - _TC",
penalty_income_account="Penalty Income Account - _TC",
repayment_schedule_type="Monthly as per repayment start date",
)
create_loan_type(
"Term Loan Type 2",
12000,
7.5,
is_term_loan=1,
mode_of_payment="Cash",
disbursement_account="Disbursement Account - _TC",
payment_account="Payment Account - _TC",
loan_account="Loan Account - _TC",
interest_income_account="Interest Income Account - _TC",
penalty_income_account="Penalty Income Account - _TC",
repayment_schedule_type="Pro-rated calendar months",
repayment_date_on="Start of the next month",
)
create_loan_type(
"Term Loan Type 3",
12000,
7.5,
is_term_loan=1,
mode_of_payment="Cash",
disbursement_account="Disbursement Account - _TC",
payment_account="Payment Account - _TC",
loan_account="Loan Account - _TC",
interest_income_account="Interest Income Account - _TC",
penalty_income_account="Penalty Income Account - _TC",
repayment_schedule_type="Pro-rated calendar months",
repayment_date_on="End of the current month",
) )
create_loan_type( create_loan_type(
@ -62,6 +116,7 @@ class TestLoan(unittest.TestCase):
"Loan Account - _TC", "Loan Account - _TC",
"Interest Income Account - _TC", "Interest Income Account - _TC",
"Penalty Income Account - _TC", "Penalty Income Account - _TC",
repayment_schedule_type="Monthly as per repayment start date",
) )
create_loan_type( create_loan_type(
@ -902,6 +957,69 @@ class TestLoan(unittest.TestCase):
amounts = calculate_amounts(loan.name, add_days(last_date, 5)) amounts = calculate_amounts(loan.name, add_days(last_date, 5))
self.assertEqual(flt(amounts["pending_principal_amount"], 0), 0) self.assertEqual(flt(amounts["pending_principal_amount"], 0), 0)
def test_term_loan_schedule_types(self):
loan = create_loan(
self.applicant1,
"Term Loan Type 1",
12000,
"Repay Over Number of Periods",
12,
repayment_start_date="2022-10-17",
)
# Check for first, second and last installment date
self.assertEqual(
format_date(loan.get("repayment_schedule")[0].payment_date, "dd-MM-yyyy"), "17-10-2022"
)
self.assertEqual(
format_date(loan.get("repayment_schedule")[1].payment_date, "dd-MM-yyyy"), "17-11-2022"
)
self.assertEqual(
format_date(loan.get("repayment_schedule")[-1].payment_date, "dd-MM-yyyy"), "17-09-2023"
)
loan.loan_type = "Term Loan Type 2"
loan.save()
# Check for first, second and last installment date
self.assertEqual(
format_date(loan.get("repayment_schedule")[0].payment_date, "dd-MM-yyyy"), "01-11-2022"
)
self.assertEqual(
format_date(loan.get("repayment_schedule")[1].payment_date, "dd-MM-yyyy"), "01-12-2022"
)
self.assertEqual(
format_date(loan.get("repayment_schedule")[-1].payment_date, "dd-MM-yyyy"), "01-10-2023"
)
loan.loan_type = "Term Loan Type 3"
loan.save()
# Check for first, second and last installment date
self.assertEqual(
format_date(loan.get("repayment_schedule")[0].payment_date, "dd-MM-yyyy"), "31-10-2022"
)
self.assertEqual(
format_date(loan.get("repayment_schedule")[1].payment_date, "dd-MM-yyyy"), "30-11-2022"
)
self.assertEqual(
format_date(loan.get("repayment_schedule")[-1].payment_date, "dd-MM-yyyy"), "30-09-2023"
)
loan.repayment_method = "Repay Fixed Amount per Period"
loan.monthly_repayment_amount = 1042
loan.save()
self.assertEqual(
format_date(loan.get("repayment_schedule")[0].payment_date, "dd-MM-yyyy"), "31-10-2022"
)
self.assertEqual(
format_date(loan.get("repayment_schedule")[1].payment_date, "dd-MM-yyyy"), "30-11-2022"
)
self.assertEqual(
format_date(loan.get("repayment_schedule")[-1].payment_date, "dd-MM-yyyy"), "30-09-2023"
)
def create_loan_scenario_for_penalty(doc): def create_loan_scenario_for_penalty(doc):
pledge = [{"loan_security": "Test Security 1", "qty": 4000.00}] pledge = [{"loan_security": "Test Security 1", "qty": 4000.00}]
@ -1033,6 +1151,8 @@ def create_loan_type(
penalty_income_account=None, penalty_income_account=None,
repayment_method=None, repayment_method=None,
repayment_periods=None, repayment_periods=None,
repayment_schedule_type=None,
repayment_date_on=None,
): ):
if not frappe.db.exists("Loan Type", loan_name): if not frappe.db.exists("Loan Type", loan_name):
@ -1042,6 +1162,7 @@ def create_loan_type(
"company": "_Test Company", "company": "_Test Company",
"loan_name": loan_name, "loan_name": loan_name,
"is_term_loan": is_term_loan, "is_term_loan": is_term_loan,
"repayment_schedule_type": "Monthly as per repayment start date",
"maximum_loan_amount": maximum_loan_amount, "maximum_loan_amount": maximum_loan_amount,
"rate_of_interest": rate_of_interest, "rate_of_interest": rate_of_interest,
"penalty_interest_rate": penalty_interest_rate, "penalty_interest_rate": penalty_interest_rate,
@ -1056,8 +1177,14 @@ def create_loan_type(
"repayment_periods": repayment_periods, "repayment_periods": repayment_periods,
"write_off_amount": 100, "write_off_amount": 100,
} }
).insert() )
if loan_type.is_term_loan:
loan_type.repayment_schedule_type = repayment_schedule_type
if loan_type.repayment_schedule_type != "Monthly as per repayment start date":
loan_type.repayment_date_on = repayment_date_on
loan_type.insert()
loan_type.submit() loan_type.submit()

View File

@ -16,6 +16,8 @@
"company", "company",
"is_term_loan", "is_term_loan",
"disabled", "disabled",
"repayment_schedule_type",
"repayment_date_on",
"description", "description",
"account_details_section", "account_details_section",
"mode_of_payment", "mode_of_payment",
@ -157,12 +159,30 @@
"label": "Disbursement Account", "label": "Disbursement Account",
"options": "Account", "options": "Account",
"reqd": 1 "reqd": 1
},
{
"depends_on": "is_term_loan",
"description": "The schedule type that will be used for generating the term loan schedules (will affect the payment date and monthly repayment amount)",
"fieldname": "repayment_schedule_type",
"fieldtype": "Select",
"label": "Repayment Schedule Type",
"mandatory_depends_on": "is_term_loan",
"options": "\nMonthly as per repayment start date\nPro-rated calendar months"
},
{
"depends_on": "eval:doc.repayment_schedule_type == \"Pro-rated calendar months\"",
"description": "Select whether the repayment date should be the end of the current month or start of the upcoming month",
"fieldname": "repayment_date_on",
"fieldtype": "Select",
"label": "Repayment Date On",
"mandatory_depends_on": "eval:doc.repayment_schedule_type == \"Pro-rated calendar months\"",
"options": "\nStart of the next month\nEnd of the current month"
} }
], ],
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2022-01-25 16:23:57.009349", "modified": "2022-10-22 17:43:03.954201",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Loan Management", "module": "Loan Management",
"name": "Loan Type", "name": "Loan Type",

View File

@ -385,6 +385,7 @@ class BOM(WebsiteGenerator):
if self.docstatus == 2: if self.docstatus == 2:
return return
self.flags.cost_updated = False
existing_bom_cost = self.total_cost existing_bom_cost = self.total_cost
if self.docstatus == 1: if self.docstatus == 1:
@ -407,7 +408,11 @@ class BOM(WebsiteGenerator):
frappe.get_doc("BOM", bom).update_cost(from_child_bom=True) frappe.get_doc("BOM", bom).update_cost(from_child_bom=True)
if not from_child_bom: if not from_child_bom:
frappe.msgprint(_("Cost Updated"), alert=True) msg = "Cost Updated"
if not self.flags.cost_updated:
msg = "No changes in cost found"
frappe.msgprint(_(msg), alert=True)
def update_parent_cost(self): def update_parent_cost(self):
if self.total_cost: if self.total_cost:
@ -593,11 +598,16 @@ class BOM(WebsiteGenerator):
# not via doc event, table is not regenerated and needs updation # not via doc event, table is not regenerated and needs updation
self.calculate_exploded_cost() self.calculate_exploded_cost()
old_cost = self.total_cost
self.total_cost = self.operating_cost + self.raw_material_cost - self.scrap_material_cost self.total_cost = self.operating_cost + self.raw_material_cost - self.scrap_material_cost
self.base_total_cost = ( self.base_total_cost = (
self.base_operating_cost + self.base_raw_material_cost - self.base_scrap_material_cost self.base_operating_cost + self.base_raw_material_cost - self.base_scrap_material_cost
) )
if self.total_cost != old_cost:
self.flags.cost_updated = True
def calculate_op_cost(self, update_hour_rate=False): def calculate_op_cost(self, update_hour_rate=False):
"""Update workstation rate and calculates totals""" """Update workstation rate and calculates totals"""
self.operating_cost = 0 self.operating_cost = 0

View File

@ -9,7 +9,10 @@ import frappe
from frappe.tests.utils import FrappeTestCase from frappe.tests.utils import FrappeTestCase
from frappe.utils import cstr, flt from frappe.utils import cstr, flt
from erpnext.controllers.tests.test_subcontracting_controller import set_backflush_based_on from erpnext.controllers.tests.test_subcontracting_controller import (
make_stock_in_entry,
set_backflush_based_on,
)
from erpnext.manufacturing.doctype.bom.bom import BOMRecursionError, item_query, make_variant_bom from erpnext.manufacturing.doctype.bom.bom import BOMRecursionError, item_query, make_variant_bom
from erpnext.manufacturing.doctype.bom_update_log.test_bom_update_log import ( from erpnext.manufacturing.doctype.bom_update_log.test_bom_update_log import (
update_cost_in_all_boms_in_test, update_cost_in_all_boms_in_test,
@ -639,6 +642,28 @@ class TestBOM(FrappeTestCase):
bom.submit() bom.submit()
self.assertEqual(bom.exploded_items[0].rate, bom.items[0].base_rate) self.assertEqual(bom.exploded_items[0].rate, bom.items[0].base_rate)
def test_bom_cost_update_flag(self):
rm_item = make_item(
properties={"is_stock_item": 1, "valuation_rate": 99, "last_purchase_rate": 89}
).name
fg_item = make_item(properties={"is_stock_item": 1}).name
from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
bom = make_bom(item=fg_item, raw_materials=[rm_item])
create_stock_reconciliation(
item_code=rm_item, warehouse="_Test Warehouse - _TC", qty=100, rate=600
)
bom.load_from_db()
bom.update_cost()
self.assertTrue(bom.flags.cost_updated)
bom.load_from_db()
bom.update_cost()
self.assertFalse(bom.flags.cost_updated)
def get_default_bom(item_code="_Test FG Item 2"): def get_default_bom(item_code="_Test FG Item 2"):
return frappe.db.get_value("BOM", {"item": item_code, "is_active": 1, "is_default": 1}) return frappe.db.get_value("BOM", {"item": item_code, "is_active": 1, "is_default": 1})

View File

@ -133,7 +133,7 @@ class JobCard(Document):
(%(from_time)s <= jctl.from_time and %(to_time)s >= jctl.to_time) {0} (%(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} 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 extra_cond, validate_overlap_for
), ),
{ {

View File

@ -136,6 +136,45 @@ class TestJobCard(FrappeTestCase):
) )
self.assertRaises(OverlapError, jc2.save) 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): def test_job_card_multiple_materials_transfer(self):
"Test transferring RMs separately against Job Card with multiple RMs." "Test transferring RMs separately against Job Card with multiple RMs."
self.transfer_material_against = "Job Card" self.transfer_material_against = "Job Card"

View File

@ -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.bom.bom import validate_bom_no
from erpnext.manufacturing.doctype.work_order.work_order import get_item_details 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.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 from erpnext.utilities.transaction_base import validate_uom_is_integer
@ -648,13 +649,23 @@ class ProductionPlan(Document):
else: else:
material_request = material_request_map[key] 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 # add item
material_request.append( material_request.append(
"items", "items",
{ {
"item_code": item.item_code, "item_code": item.item_code,
"from_warehouse": item.from_warehouse, "from_warehouse": item.from_warehouse,
"qty": item.quantity, "qty": item.quantity / conversion_factor,
"schedule_date": schedule_date, "schedule_date": schedule_date,
"warehouse": item.warehouse, "warehouse": item.warehouse,
"sales_order": item.sales_order, "sales_order": item.sales_order,

View File

@ -806,6 +806,35 @@ class TestProductionPlan(FrappeTestCase):
self.assertEqual(pln.status, "Completed") self.assertEqual(pln.status, "Completed")
self.assertEqual(pln.po_items[0].produced_qty, 5) 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): def create_production_plan(**args):
""" """

View File

@ -17,6 +17,7 @@ from erpnext.manufacturing.doctype.work_order.work_order import (
close_work_order, close_work_order,
make_job_card, make_job_card,
make_stock_entry, make_stock_entry,
make_stock_return_entry,
stop_unstop, stop_unstop,
) )
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
@ -1408,6 +1409,77 @@ class TestWorkOrder(FrappeTestCase):
) )
self.assertEqual(manufacture_ste_doc2.items[1].qty, 1) self.assertEqual(manufacture_ste_doc2.items[1].qty, 1)
def test_non_consumed_material_return_against_work_order(self):
frappe.db.set_value(
"Manufacturing Settings",
None,
"backflush_raw_materials_based_on",
"Material Transferred for Manufacture",
)
item = make_item(
"Test FG Item To Test Return Case",
{
"is_stock_item": 1,
},
)
item_code = item.name
bom_doc = make_bom(
item=item_code,
source_warehouse="Stores - _TC",
raw_materials=["Test Batch MCC Keyboard", "Test Serial No BTT Headphone"],
)
# Create a work order
wo_doc = make_wo_order_test_record(production_item=item_code, qty=5)
wo_doc.save()
self.assertEqual(wo_doc.bom_no, bom_doc.name)
# Transfer material for manufacture
ste_doc = frappe.get_doc(make_stock_entry(wo_doc.name, "Material Transfer for Manufacture", 5))
for row in ste_doc.items:
row.qty += 2
row.transfer_qty += 2
nste_doc = test_stock_entry.make_stock_entry(
item_code=row.item_code, target="Stores - _TC", qty=row.qty, basic_rate=100
)
row.batch_no = nste_doc.items[0].batch_no
row.serial_no = nste_doc.items[0].serial_no
ste_doc.save()
ste_doc.submit()
ste_doc.load_from_db()
# Create a stock entry to manufacture the item
ste_doc = frappe.get_doc(make_stock_entry(wo_doc.name, "Manufacture", 5))
for row in ste_doc.items:
if row.s_warehouse and not row.t_warehouse:
row.qty -= 2
row.transfer_qty -= 2
if row.serial_no:
serial_nos = get_serial_nos(row.serial_no)
row.serial_no = "\n".join(serial_nos[0:5])
ste_doc.save()
ste_doc.submit()
wo_doc.load_from_db()
for row in wo_doc.required_items:
self.assertEqual(row.transferred_qty, 7)
self.assertEqual(row.consumed_qty, 5)
self.assertEqual(wo_doc.status, "Completed")
return_ste_doc = make_stock_return_entry(wo_doc.name)
return_ste_doc.save()
self.assertTrue(return_ste_doc.is_return)
for row in return_ste_doc.items:
self.assertEqual(row.qty, 2)
def prepare_data_for_backflush_based_on_materials_transferred(): def prepare_data_for_backflush_based_on_materials_transferred():
batch_item_doc = make_item( batch_item_doc = make_item(

View File

@ -180,6 +180,37 @@ frappe.ui.form.on("Work Order", {
frm.trigger("make_bom"); frm.trigger("make_bom");
}); });
} }
frm.trigger("add_custom_button_to_return_components");
},
add_custom_button_to_return_components: function(frm) {
if (frm.doc.docstatus === 1 && in_list(["Closed", "Completed"], frm.doc.status)) {
let non_consumed_items = frm.doc.required_items.filter(d =>{
return flt(d.consumed_qty) < flt(d.transferred_qty - d.returned_qty)
});
if (non_consumed_items && non_consumed_items.length) {
frm.add_custom_button(__("Return Components"), function() {
frm.trigger("create_stock_return_entry");
}).addClass("btn-primary");
}
}
},
create_stock_return_entry: function(frm) {
frappe.call({
method: "erpnext.manufacturing.doctype.work_order.work_order.make_stock_return_entry",
args: {
"work_order": frm.doc.name,
},
callback: function(r) {
if(!r.exc) {
let doc = frappe.model.sync(r.message);
frappe.set_route("Form", doc[0].doctype, doc[0].name);
}
}
});
}, },
make_job_card: function(frm) { make_job_card: function(frm) {
@ -517,7 +548,8 @@ frappe.ui.form.on("Work Order Operation", {
erpnext.work_order = { erpnext.work_order = {
set_custom_buttons: function(frm) { set_custom_buttons: function(frm) {
var doc = frm.doc; var doc = frm.doc;
if (doc.docstatus === 1 && doc.status != "Closed") {
if (doc.status !== "Closed") {
frm.add_custom_button(__('Close'), function() { frm.add_custom_button(__('Close'), function() {
frappe.confirm(__("Once the Work Order is Closed. It can't be resumed."), frappe.confirm(__("Once the Work Order is Closed. It can't be resumed."),
() => { () => {
@ -525,7 +557,9 @@ erpnext.work_order = {
} }
); );
}, __("Status")); }, __("Status"));
}
if (doc.docstatus === 1 && !in_list(["Closed", "Completed"], doc.status)) {
if (doc.status != 'Stopped' && doc.status != 'Completed') { if (doc.status != 'Stopped' && doc.status != 'Completed') {
frm.add_custom_button(__('Stop'), function() { frm.add_custom_button(__('Stop'), function() {
erpnext.work_order.change_work_order_status(frm, "Stopped"); erpnext.work_order.change_work_order_status(frm, "Stopped");

View File

@ -20,6 +20,7 @@ from frappe.utils import (
nowdate, nowdate,
time_diff_in_hours, time_diff_in_hours,
) )
from pypika import functions as fn
from erpnext.manufacturing.doctype.bom.bom import ( from erpnext.manufacturing.doctype.bom.bom import (
get_bom_item_rate, get_bom_item_rate,
@ -859,6 +860,7 @@ class WorkOrder(Document):
if self.docstatus == 1: if self.docstatus == 1:
# calculate transferred qty based on submitted stock entries # calculate transferred qty based on submitted stock entries
self.update_transferred_qty_for_required_items() self.update_transferred_qty_for_required_items()
self.update_returned_qty()
# update in bin # update in bin
self.update_reserved_qty_for_production() self.update_reserved_qty_for_production()
@ -930,23 +932,62 @@ class WorkOrder(Document):
self.set_available_qty() self.set_available_qty()
def update_transferred_qty_for_required_items(self): def update_transferred_qty_for_required_items(self):
"""update transferred qty from submitted stock entries for that item against ste = frappe.qb.DocType("Stock Entry")
the work order""" ste_child = frappe.qb.DocType("Stock Entry Detail")
for d in self.required_items: query = (
transferred_qty = frappe.db.sql( frappe.qb.from_(ste)
"""select sum(qty) .inner_join(ste_child)
from `tabStock Entry` entry, `tabStock Entry Detail` detail .on((ste_child.parent == ste.name))
where .select(
entry.work_order = %(name)s ste_child.item_code,
and entry.purpose = 'Material Transfer for Manufacture' ste_child.original_item,
and entry.docstatus = 1 fn.Sum(ste_child.qty).as_("qty"),
and detail.parent = entry.name )
and (detail.item_code = %(item)s or detail.original_item = %(item)s)""", .where(
{"name": self.name, "item": d.item_code}, (ste.docstatus == 1)
)[0][0] & (ste.work_order == self.name)
& (ste.purpose == "Material Transfer for Manufacture")
& (ste.is_return == 0)
)
.groupby(ste_child.item_code)
)
d.db_set("transferred_qty", flt(transferred_qty), update_modified=False) data = query.run(as_dict=1) or []
transferred_items = frappe._dict({d.original_item or d.item_code: d.qty for d in data})
for row in self.required_items:
row.db_set(
"transferred_qty", (transferred_items.get(row.item_code) or 0.0), update_modified=False
)
def update_returned_qty(self):
ste = frappe.qb.DocType("Stock Entry")
ste_child = frappe.qb.DocType("Stock Entry Detail")
query = (
frappe.qb.from_(ste)
.inner_join(ste_child)
.on((ste_child.parent == ste.name))
.select(
ste_child.item_code,
ste_child.original_item,
fn.Sum(ste_child.qty).as_("qty"),
)
.where(
(ste.docstatus == 1)
& (ste.work_order == self.name)
& (ste.purpose == "Material Transfer for Manufacture")
& (ste.is_return == 1)
)
.groupby(ste_child.item_code)
)
data = query.run(as_dict=1) or []
returned_dict = frappe._dict({d.original_item or d.item_code: d.qty for d in data})
for row in self.required_items:
row.db_set("returned_qty", (returned_dict.get(row.item_code) or 0.0), update_modified=False)
def update_consumed_qty_for_required_items(self): def update_consumed_qty_for_required_items(self):
""" """
@ -1470,3 +1511,25 @@ def get_reserved_qty_for_production(item_code: str, warehouse: str) -> float:
) )
) )
).run()[0][0] or 0.0 ).run()[0][0] or 0.0
@frappe.whitelist()
def make_stock_return_entry(work_order):
from erpnext.stock.doctype.stock_entry.stock_entry import get_available_materials
non_consumed_items = get_available_materials(work_order)
if not non_consumed_items:
return
wo_doc = frappe.get_cached_doc("Work Order", work_order)
stock_entry = frappe.new_doc("Stock Entry")
stock_entry.from_bom = 1
stock_entry.is_return = 1
stock_entry.work_order = work_order
stock_entry.purpose = "Material Transfer for Manufacture"
stock_entry.bom_no = wo_doc.bom_no
stock_entry.add_transfered_raw_materials_in_items()
stock_entry.set_stock_entry_type()
return stock_entry

View File

@ -20,6 +20,7 @@
"column_break_11", "column_break_11",
"transferred_qty", "transferred_qty",
"consumed_qty", "consumed_qty",
"returned_qty",
"available_qty_at_source_warehouse", "available_qty_at_source_warehouse",
"available_qty_at_wip_warehouse" "available_qty_at_wip_warehouse"
], ],
@ -97,6 +98,7 @@
"fieldtype": "Column Break" "fieldtype": "Column Break"
}, },
{ {
"columns": 1,
"depends_on": "eval:!parent.skip_transfer", "depends_on": "eval:!parent.skip_transfer",
"fieldname": "consumed_qty", "fieldname": "consumed_qty",
"fieldtype": "Float", "fieldtype": "Float",
@ -127,11 +129,19 @@
"fieldtype": "Currency", "fieldtype": "Currency",
"label": "Amount", "label": "Amount",
"read_only": 1 "read_only": 1
},
{
"columns": 1,
"fieldname": "returned_qty",
"fieldtype": "Float",
"in_list_view": 1,
"label": "Returned Qty ",
"read_only": 1
} }
], ],
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2020-04-13 18:46:32.966416", "modified": "2022-09-28 10:50:43.512562",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Manufacturing", "module": "Manufacturing",
"name": "Work Order Item", "name": "Work Order Item",
@ -140,5 +150,6 @@
"quick_entry": 1, "quick_entry": 1,
"sort_field": "modified", "sort_field": "modified",
"sort_order": "DESC", "sort_order": "DESC",
"states": [],
"track_changes": 1 "track_changes": 1
} }

View File

@ -50,7 +50,7 @@ frappe.query_reports["Work Order Consumed Materials"] = {
label: __("Status"), label: __("Status"),
fieldname: "status", fieldname: "status",
fieldtype: "Select", fieldtype: "Select",
options: ["In Process", "Completed", "Stopped"] options: ["", "In Process", "Completed", "Stopped"]
}, },
{ {
label: __("Excess Materials Consumed"), label: __("Excess Materials Consumed"),

View File

@ -1,6 +1,8 @@
# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors # Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
# For license information, please see license.txt # For license information, please see license.txt
from collections import defaultdict
import frappe import frappe
from frappe import _ from frappe import _
@ -18,7 +20,11 @@ def get_data(report_filters):
filters = get_filter_condition(report_filters) filters = get_filter_condition(report_filters)
wo_items = {} wo_items = {}
for d in frappe.get_all("Work Order", filters=filters, fields=fields):
work_orders = frappe.get_all("Work Order", filters=filters, fields=fields)
returned_materials = get_returned_materials(work_orders)
for d in work_orders:
d.extra_consumed_qty = 0.0 d.extra_consumed_qty = 0.0
if d.consumed_qty and d.consumed_qty > d.required_qty: if d.consumed_qty and d.consumed_qty > d.required_qty:
d.extra_consumed_qty = d.consumed_qty - d.required_qty d.extra_consumed_qty = d.consumed_qty - d.required_qty
@ -39,6 +45,28 @@ def get_data(report_filters):
return data return data
def get_returned_materials(work_orders):
raw_materials_qty = defaultdict(float)
raw_materials = frappe.get_all(
"Stock Entry",
fields=["`tabStock Entry Detail`.`item_code`", "`tabStock Entry Detail`.`qty`"],
filters=[
["Stock Entry", "is_return", "=", 1],
["Stock Entry Detail", "docstatus", "=", 1],
["Stock Entry", "work_order", "in", [d.name for d in work_orders]],
],
)
for d in raw_materials:
raw_materials_qty[d.item_code] += d.qty
for row in work_orders:
row.returned_qty = 0.0
if raw_materials_qty.get(row.raw_material_item_code):
row.returned_qty = raw_materials_qty.get(row.raw_material_item_code)
def get_fields(): def get_fields():
return [ return [
"`tabWork Order Item`.`parent`", "`tabWork Order Item`.`parent`",
@ -65,7 +93,7 @@ def get_filter_condition(report_filters):
for field in ["name", "production_item", "company", "status"]: for field in ["name", "production_item", "company", "status"]:
value = report_filters.get(field) value = report_filters.get(field)
if value: if value:
key = f"`{field}`" key = f"{field}"
filters.update({key: value}) filters.update({key: value})
return filters return filters
@ -112,4 +140,10 @@ def get_columns():
"fieldtype": "Float", "fieldtype": "Float",
"width": 100, "width": 100,
}, },
{
"label": _("Returned Qty"),
"fieldname": "returned_qty",
"fieldtype": "Float",
"width": 100,
},
] ]

View File

@ -315,4 +315,5 @@ erpnext.patches.v14_0.fix_crm_no_of_employees
erpnext.patches.v14_0.create_accounting_dimensions_in_subcontracting_doctypes erpnext.patches.v14_0.create_accounting_dimensions_in_subcontracting_doctypes
erpnext.patches.v14_0.fix_subcontracting_receipt_gl_entries erpnext.patches.v14_0.fix_subcontracting_receipt_gl_entries
erpnext.patches.v14_0.migrate_remarks_from_gl_to_payment_ledger erpnext.patches.v14_0.migrate_remarks_from_gl_to_payment_ledger
erpnext.patches.v13_0.update_schedule_type_in_loans
erpnext.patches.v14_0.create_accounting_dimensions_for_asset_capitalization erpnext.patches.v14_0.create_accounting_dimensions_for_asset_capitalization

View File

@ -0,0 +1,14 @@
import frappe
def execute():
loan = frappe.qb.DocType("Loan")
loan_type = frappe.qb.DocType("Loan Type")
frappe.qb.update(loan_type).set(
loan_type.repayment_schedule_type, "Monthly as per repayment start date"
).where(loan_type.is_term_loan == 1).run()
frappe.qb.update(loan).set(
loan.repayment_schedule_type, "Monthly as per repayment start date"
).where(loan.is_term_loan == 1).run()

View File

@ -3,6 +3,29 @@ from frappe import qb
from frappe.utils import create_batch from frappe.utils import create_batch
def remove_duplicate_entries(pl_entries):
unique_vouchers = set()
for x in pl_entries:
unique_vouchers.add(
(x.company, x.account, x.party_type, x.party, x.voucher_type, x.voucher_no, x.gle_remarks)
)
entries = []
for x in unique_vouchers:
entries.append(
frappe._dict(
company=x[0],
account=x[1],
party_type=x[2],
party=x[3],
voucher_type=x[4],
voucher_no=x[5],
gle_remarks=x[6],
)
)
return entries
def execute(): def execute():
if frappe.reload_doc("accounts", "doctype", "payment_ledger_entry"): if frappe.reload_doc("accounts", "doctype", "payment_ledger_entry"):
@ -34,6 +57,8 @@ def execute():
.run(as_dict=True) .run(as_dict=True)
) )
pl_entries = remove_duplicate_entries(pl_entries)
if pl_entries: if pl_entries:
# split into multiple batches, update and commit for each batch # split into multiple batches, update and commit for each batch
batch_size = 1000 batch_size = 1000

View File

@ -5,7 +5,7 @@
"label": "Open Projects" "label": "Open Projects"
} }
], ],
"content": "[{\"type\":\"chart\",\"data\":{\"chart_name\":\"Open Projects\",\"col\":12}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"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\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"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\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"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\":\"<span class=\\\"h4\\\"><b>Reports &amp; Masters</b></span>\",\"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", "creation": "2020-03-02 15:46:04.874669",
"docstatus": 0, "docstatus": 0,
"doctype": "Workspace", "doctype": "Workspace",
@ -170,9 +170,27 @@
"link_type": "Report", "link_type": "Report",
"onboard": 0, "onboard": 0,
"type": "Link" "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", "modified_by": "Administrator",
"module": "Projects", "module": "Projects",
"name": "Projects", "name": "Projects",

View File

@ -426,6 +426,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
if(!this.validate_company_and_party()) { if(!this.validate_company_and_party()) {
this.frm.fields_dict["items"].grid.grid_rows[item.idx - 1].remove(); this.frm.fields_dict["items"].grid.grid_rows[item.idx - 1].remove();
} else { } else {
item.pricing_rules = ''
return this.frm.call({ return this.frm.call({
method: "erpnext.stock.get_item_details.get_item_details", method: "erpnext.stock.get_item_details.get_item_details",
child: item, child: item,
@ -1045,6 +1046,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
uom(doc, cdt, cdn) { uom(doc, cdt, cdn) {
var me = this; var me = this;
var item = frappe.get_doc(cdt, cdn); var item = frappe.get_doc(cdt, cdn);
item.pricing_rules = ''
if(item.item_code && item.uom) { if(item.item_code && item.uom) {
return this.frm.call({ return this.frm.call({
method: "erpnext.stock.get_item_details.get_conversion_factor", method: "erpnext.stock.get_item_details.get_conversion_factor",
@ -1121,6 +1123,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
qty(doc, cdt, cdn) { qty(doc, cdt, cdn) {
let item = frappe.get_doc(cdt, cdn); let item = frappe.get_doc(cdt, cdn);
item.pricing_rules = ''
this.conversion_factor(doc, cdt, cdn, true); this.conversion_factor(doc, cdt, cdn, true);
this.calculate_stock_uom_rate(doc, cdt, cdn); this.calculate_stock_uom_rate(doc, cdt, cdn);
this.apply_pricing_rule(item, true); this.apply_pricing_rule(item, true);
@ -1401,7 +1404,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
if (!r.exc && r.message) { if (!r.exc && r.message) {
me._set_values_for_item_list(r.message); me._set_values_for_item_list(r.message);
if(item) me.set_gross_profit(item); if(item) me.set_gross_profit(item);
if(me.frm.doc.apply_discount_on) me.frm.trigger("apply_discount_on") if (me.frm.doc.apply_discount_on) me.frm.trigger("apply_discount_on")
} }
} }
}); });
@ -1574,6 +1577,7 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe
for (let key in pr_row) { for (let key in pr_row) {
row_to_modify[key] = pr_row[key]; row_to_modify[key] = pr_row[key];
} }
this.frm.script_manager.copy_from_first_row("items", row_to_modify, ["expense_account", "income_account"]);
}); });
// free_item_data is a temporary variable // free_item_data is a temporary variable

View File

@ -28,7 +28,7 @@ erpnext.financial_statements = {
}, },
"open_general_ledger": function(data) { "open_general_ledger": function(data) {
if (!data.account) return; 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 = { frappe.route_options = {
"account": data.account, "account": data.account,
@ -37,7 +37,16 @@ erpnext.financial_statements = {
"to_date": data.to_date || data.year_end_date, "to_date": data.to_date || data.year_end_date,
"project": (project && project.length > 0) ? project[0].$input.val() : "" "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, "tree": true,
"name_field": "account", "name_field": "account",

View File

@ -242,20 +242,29 @@ erpnext.utils.set_taxes = function(frm, triggered_from_field) {
}); });
}; };
erpnext.utils.get_contact_details = function(frm) { erpnext.utils.get_contact_details = function (frm) {
if (frm.updating_party_details) return; if (frm.updating_party_details) return;
if (frm.doc["contact_person"]) { if (frm.doc["contact_person"]) {
frappe.call({ frappe.call({
method: "frappe.contacts.doctype.contact.contact.get_contact_details", method: "frappe.contacts.doctype.contact.contact.get_contact_details",
args: {contact: frm.doc.contact_person }, args: { contact: frm.doc.contact_person },
callback: function(r) { callback: function (r) {
if (r.message) if (r.message) frm.set_value(r.message);
frm.set_value(r.message); },
} });
}) } else {
frm.set_value({
contact_person: "",
contact_display: "",
contact_email: "",
contact_mobile: "",
contact_phone: "",
contact_designation: "",
contact_department: "",
});
} }
} };
erpnext.utils.validate_mandatory = function(frm, label, value, trigger_on) { erpnext.utils.validate_mandatory = function(frm, label, value, trigger_on) {
if (!value) { if (!value) {

View File

@ -0,0 +1,115 @@
#page-order {
.main-column {
.page-content-wrapper {
.breadcrumb-container {
@media screen and (min-width: 567px) {
padding-left: var(--padding-sm);
}
}
.container.my-4 {
background-color: var(--fg-color);
@media screen and (min-width: 567px) {
padding: 1.25rem 1.5rem;
border-radius: var(--border-radius-md);
box-shadow: var(--card-shadow);
}
}
}
}
}
.indicator-container {
@media screen and (max-width: 567px) {
padding-bottom: 0.8rem;
}
}
.order-items {
padding: 1.5rem 0;
border-bottom: 1px solid var(--border-color);
color: var(--gray-700);
@media screen and (max-width: 567px) {
align-items: flex-start !important;
}
.col-2 {
@media screen and (max-width: 567px) {
flex: auto;
max-width: 28%;
}
}
.order-item-name {
font-size: var(--text-base);
font-weight: 500;
}
.btn:focus,
.btn:hover {
background-color: var(--control-bg);
}
.col-6 {
@media screen and (max-width: 567px) {
max-width: 100%;
}
&.order-item-name {
font-size: var(--text-base);
}
}
}
.item-grand-total {
font-size: var(--text-base);
}
.list-item-name,
.item-total,
.order-container,
.order-qty {
font-size: var(--text-md);
}
.d-s-n {
@media screen and (max-width: 567px) {
display: none;
}
}
.d-l-n {
@media screen and (min-width: 567px) {
display: none;
}
}
.border-btm {
border-bottom: 1px solid var(--border-color);
}
.order-taxes {
display: flex;
@media screen and (min-width: 567px) {
justify-content: flex-end;
}
.col-4 {
padding-right: 0;
.col-8 {
padding-left: 0;
padding-right: 0;
}
@media screen and (max-width: 567px) {
padding-left: 0;
flex: auto;
max-width: 100%;
}
}
}

View File

@ -1,3 +1,4 @@
@import './order-page';
.filter-options { .filter-options {
max-height: 300px; max-height: 300px;
@ -32,19 +33,29 @@
height: 24px; height: 24px;
} }
.website-list .result { .website-list {
margin-top: 2rem; background-color: var(--fg-color);
} padding: 0 var(--padding-lg);
border-radius: var(--border-radius-md);
.result { @media screen and (max-width: 567px) {
border-bottom: 1px solid var(--border-color); margin-left: -2rem;
}
&.result {
border-bottom: 1px solid var(--border-color);
}
} }
.transaction-list-item { .transaction-list-item {
padding: 1rem 0; padding: 1rem 0;
border-top: 1px solid var(--border-color); border-bottom: 1px solid var(--border-color);
position: relative; position: relative;
&:only-child, &:last-child {
border: 0;
}
a.transaction-item-link { a.transaction-item-link {
position: absolute; position: absolute;
top: 0; top: 0;
@ -68,3 +79,13 @@
line-height: 1.3; line-height: 1.3;
} }
} }
.list-item-name, .item-total {
font-size: var(--font-size-sm);
}
.items-preview {
@media screen and (max-width: 567px) {
margin-top: 1rem;
}
}

View File

@ -37,7 +37,7 @@ def make_custom_fields():
fieldname="vat_section", fieldname="vat_section",
label="VAT Details", label="VAT Details",
fieldtype="Section Break", fieldtype="Section Break",
insert_after="group_same_items", insert_after="language",
print_hide=1, print_hide=1,
collapsible=1, collapsible=1,
), ),

View File

@ -3,6 +3,7 @@
import frappe import frappe
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
from frappe.test_runner import make_test_records from frappe.test_runner import make_test_records
from frappe.tests.utils import FrappeTestCase from frappe.tests.utils import FrappeTestCase
from frappe.utils import flt from frappe.utils import flt
@ -341,6 +342,33 @@ class TestCustomer(FrappeTestCase):
due_date = get_due_date("2017-01-22", "Customer", "_Test Customer") due_date = get_due_date("2017-01-22", "Customer", "_Test Customer")
self.assertEqual(due_date, "2017-01-22") self.assertEqual(due_date, "2017-01-22")
def test_serach_fields_for_customer(self):
from erpnext.controllers.queries import customer_query
make_property_setter(
"Customer", None, "search_fields", "customer_group", "Data", for_doctype="Doctype"
)
data = customer_query(
"Customer", "_Test Customer", "", 0, 20, filters={"name": "_Test Customer"}, as_dict=True
)
self.assertEqual(data[0].name, "_Test Customer")
self.assertEqual(data[0].customer_group, "_Test Customer Group")
self.assertTrue("territory" not in data[0])
make_property_setter(
"Customer", None, "search_fields", "customer_group, territory", "Data", for_doctype="Doctype"
)
data = customer_query(
"Customer", "_Test Customer", "", 0, 20, filters={"name": "_Test Customer"}, as_dict=True
)
self.assertEqual(data[0].name, "_Test Customer")
self.assertEqual(data[0].customer_group, "_Test Customer Group")
self.assertEqual(data[0].territory, "_Test Territory")
self.assertTrue("territory" in data[0])
def get_customer_dict(customer_name): def get_customer_dict(customer_name):
return { return {

View File

@ -84,11 +84,12 @@ erpnext.selling.QuotationController = class QuotationController extends erpnext.
} }
} }
if(doc.docstatus == 1 && !(['Lost', 'Ordered']).includes(doc.status)) { 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) { this.frm.add_custom_button(
cur_frm.add_custom_button(__('Sales Order'), __("Sales Order"),
cur_frm.cscript['Make Sales Order'], __('Create')); this.frm.cscript["Make Sales Order"],
} __("Create")
);
if(doc.status!=="Ordered") { if(doc.status!=="Ordered") {
this.frm.add_custom_button(__('Set as Lost'), () => { this.frm.add_custom_button(__('Set as Lost'), () => {

View File

@ -15,26 +15,13 @@
"quotation_to", "quotation_to",
"party_name", "party_name",
"customer_name", "customer_name",
"column_break1", "column_break_7",
"amended_from",
"company",
"transaction_date", "transaction_date",
"valid_till", "valid_till",
"column_break1",
"order_type", "order_type",
"contact_section", "company",
"customer_address", "amended_from",
"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",
"currency_and_price_list", "currency_and_price_list",
"currency", "currency",
"conversion_rate", "conversion_rate",
@ -43,79 +30,105 @@
"price_list_currency", "price_list_currency",
"plc_conversion_rate", "plc_conversion_rate",
"ignore_pricing_rule", "ignore_pricing_rule",
"section_break_33",
"scan_barcode",
"items_section", "items_section",
"scan_barcode",
"items", "items",
"bundle_items_section",
"packed_items",
"pricing_rule_details",
"pricing_rules",
"sec_break23", "sec_break23",
"total_qty", "total_qty",
"total_net_weight",
"column_break_28",
"base_total", "base_total",
"base_net_total", "base_net_total",
"column_break_28", "column_break_31",
"total", "total",
"net_total", "net_total",
"total_net_weight",
"taxes_section", "taxes_section",
"taxes_and_charges",
"column_break_36",
"tax_category", "tax_category",
"column_break_34", "column_break_34",
"shipping_rule", "shipping_rule",
"section_break_36", "section_break_36",
"taxes_and_charges",
"taxes", "taxes",
"sec_tax_breakup",
"other_charges_calculation",
"section_break_39", "section_break_39",
"base_total_taxes_and_charges", "base_total_taxes_and_charges",
"column_break_42", "column_break_42",
"total_taxes_and_charges", "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", "totals",
"base_grand_total", "base_grand_total",
"base_rounding_adjustment", "base_rounding_adjustment",
"base_in_words",
"base_rounded_total", "base_rounded_total",
"base_in_words",
"column_break3", "column_break3",
"grand_total", "grand_total",
"rounding_adjustment", "rounding_adjustment",
"rounded_total", "rounded_total",
"in_words", "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_schedule_section",
"payment_terms_template", "payment_terms_template",
"payment_schedule", "payment_schedule",
"terms_section_break", "terms_section_break",
"tc_name", "tc_name",
"terms", "terms",
"more_info_tab",
"subscription_section",
"auto_repeat",
"update_auto_repeat_reference",
"print_settings", "print_settings",
"letter_head", "letter_head",
"group_same_items", "group_same_items",
"column_break_73", "column_break_73",
"select_print_heading", "select_print_heading",
"language", "language",
"subscription_section", "lost_reasons_section",
"auto_repeat", "lost_reasons",
"update_auto_repeat_reference", "competitors",
"more_info", "column_break_117",
"order_lost_reason",
"additional_info_section",
"status",
"customer_group",
"territory",
"column_break_108",
"campaign", "campaign",
"source", "source",
"order_lost_reason",
"column_break4", "column_break4",
"status",
"enq_det",
"supplier_quotation",
"opportunity", "opportunity",
"lost_reasons", "supplier_quotation",
"competitors" "enq_det",
"connections_tab"
], ],
"fields": [ "fields": [
{ {
@ -241,14 +254,6 @@
"print_hide": 1, "print_hide": 1,
"reqd": 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", "fieldname": "customer_address",
"fieldtype": "Link", "fieldtype": "Link",
@ -296,7 +301,7 @@
"read_only": 1 "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", "fieldname": "col_break98",
"fieldtype": "Column Break", "fieldtype": "Column Break",
"width": "50%" "width": "50%"
@ -316,7 +321,7 @@
"read_only": 1 "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", "fieldname": "customer_group",
"fieldtype": "Link", "fieldtype": "Link",
"hidden": 1, "hidden": 1,
@ -408,6 +413,8 @@
{ {
"fieldname": "items_section", "fieldname": "items_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hide_border": 1,
"label": "Items",
"oldfieldtype": "Section Break", "oldfieldtype": "Section Break",
"options": "fa fa-shopping-cart" "options": "fa fa-shopping-cart"
}, },
@ -415,7 +422,6 @@
"allow_bulk_edit": 1, "allow_bulk_edit": 1,
"fieldname": "items", "fieldname": "items",
"fieldtype": "Table", "fieldtype": "Table",
"label": "Items",
"oldfieldname": "quotation_details", "oldfieldname": "quotation_details",
"oldfieldtype": "Table", "oldfieldtype": "Table",
"options": "Quotation Item", "options": "Quotation Item",
@ -423,6 +429,7 @@
"width": "40px" "width": "40px"
}, },
{ {
"collapsible": 1,
"fieldname": "pricing_rule_details", "fieldname": "pricing_rule_details",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Pricing Rules" "label": "Pricing Rules"
@ -483,6 +490,7 @@
"read_only": 1 "read_only": 1
}, },
{ {
"depends_on": "total_net_weight",
"fieldname": "total_net_weight", "fieldname": "total_net_weight",
"fieldtype": "Float", "fieldtype": "Float",
"label": "Total Net Weight", "label": "Total Net Weight",
@ -492,6 +500,7 @@
{ {
"fieldname": "taxes_section", "fieldname": "taxes_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hide_border": 1,
"label": "Taxes and Charges", "label": "Taxes and Charges",
"oldfieldtype": "Section Break", "oldfieldtype": "Section Break",
"options": "fa fa-money" "options": "fa fa-money"
@ -517,7 +526,8 @@
}, },
{ {
"fieldname": "section_break_36", "fieldname": "section_break_36",
"fieldtype": "Section Break" "fieldtype": "Section Break",
"hide_border": 1
}, },
{ {
"fieldname": "taxes_and_charges", "fieldname": "taxes_and_charges",
@ -579,10 +589,9 @@
}, },
{ {
"collapsible": 1, "collapsible": 1,
"collapsible_depends_on": "discount_amount",
"fieldname": "section_break_44", "fieldname": "section_break_44",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Additional Discount and Coupon Code" "label": "Additional Discount"
}, },
{ {
"fieldname": "coupon_code", "fieldname": "coupon_code",
@ -632,6 +641,7 @@
{ {
"fieldname": "totals", "fieldname": "totals",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Totals",
"oldfieldtype": "Section Break", "oldfieldtype": "Section Break",
"options": "fa fa-money", "options": "fa fa-money",
"print_hide": 1 "print_hide": 1
@ -657,7 +667,6 @@
"read_only": 1 "read_only": 1
}, },
{ {
"description": "In Words will be visible once you save the Quotation.",
"fieldname": "base_in_words", "fieldname": "base_in_words",
"fieldtype": "Data", "fieldtype": "Data",
"label": "In Words (Company Currency)", "label": "In Words (Company Currency)",
@ -665,8 +674,7 @@
"oldfieldname": "in_words", "oldfieldname": "in_words",
"oldfieldtype": "Data", "oldfieldtype": "Data",
"print_hide": 1, "print_hide": 1,
"read_only": 1, "read_only": 1
"width": "200px"
}, },
{ {
"fieldname": "base_rounded_total", "fieldname": "base_rounded_total",
@ -749,7 +757,6 @@
"print_hide": 1 "print_hide": 1
}, },
{ {
"collapsible": 1,
"collapsible_depends_on": "terms", "collapsible_depends_on": "terms",
"fieldname": "terms_section_break", "fieldname": "terms_section_break",
"fieldtype": "Section Break", "fieldtype": "Section Break",
@ -824,7 +831,7 @@
{ {
"fieldname": "subscription_section", "fieldname": "subscription_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Auto Repeat Section" "label": "Auto Repeat"
}, },
{ {
"fieldname": "auto_repeat", "fieldname": "auto_repeat",
@ -837,20 +844,11 @@
}, },
{ {
"allow_on_submit": 1, "allow_on_submit": 1,
"depends_on": "eval: doc.auto_repeat", "depends_on": "eval:doc.auto_repeat",
"fieldname": "update_auto_repeat_reference", "fieldname": "update_auto_repeat_reference",
"fieldtype": "Button", "fieldtype": "Button",
"label": "Update Auto Repeat Reference" "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", "fieldname": "campaign",
"fieldtype": "Link", "fieldtype": "Link",
@ -871,7 +869,7 @@
}, },
{ {
"allow_on_submit": 1, "allow_on_submit": 1,
"depends_on": "eval:doc.status===\"Lost\"", "depends_on": "eval:doc.status=='Lost'",
"fieldname": "order_lost_reason", "fieldname": "order_lost_reason",
"fieldtype": "Small Text", "fieldtype": "Small Text",
"label": "Detailed Reason", "label": "Detailed Reason",
@ -934,7 +932,6 @@
"read_only": 1 "read_only": 1
}, },
{ {
"depends_on": "packed_items",
"fieldname": "packed_items", "fieldname": "packed_items",
"fieldtype": "Table", "fieldtype": "Table",
"label": "Bundle Items", "label": "Bundle Items",
@ -943,7 +940,6 @@
}, },
{ {
"collapsible": 1, "collapsible": 1,
"collapsible_depends_on": "packed_items",
"depends_on": "packed_items", "depends_on": "packed_items",
"fieldname": "bundle_items_section", "fieldname": "bundle_items_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
@ -970,22 +966,99 @@
"label": "Company Address", "label": "Company Address",
"read_only": 1 "read_only": 1
}, },
{
"fieldname": "section_break_33",
"fieldtype": "Section Break"
},
{ {
"fieldname": "scan_barcode", "fieldname": "scan_barcode",
"fieldtype": "Data", "fieldtype": "Data",
"label": "Scan Barcode", "label": "Scan Barcode",
"options": "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", "icon": "fa fa-shopping-cart",
"idx": 82, "idx": 82,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2022-09-16 17:44:43.221804", "modified": "2022-10-11 13:06:33.479650",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Selling", "module": "Selling",
"name": "Quotation", "name": "Quotation",
@ -1083,4 +1156,4 @@
"states": [], "states": [],
"timeline_field": "party_name", "timeline_field": "party_name",
"title_field": "title" "title_field": "title"
} }

View File

@ -124,6 +124,11 @@ frappe.ui.form.on("Sales Order", {
return query; return query;
}); });
// On cancel and amending a sales order with advance payment, reset advance paid amount
if (frm.is_new()) {
frm.set_value("advance_paid", 0)
}
frm.ignore_doctypes_on_cancel_all = ['Purchase Order']; frm.ignore_doctypes_on_cancel_all = ['Purchase Order'];
}, },

View File

@ -15,37 +15,25 @@
"naming_series", "naming_series",
"customer", "customer",
"customer_name", "customer_name",
"tax_id",
"order_type", "order_type",
"skip_delivery_note", "column_break_7",
"column_break1",
"amended_from",
"company",
"transaction_date", "transaction_date",
"delivery_date", "delivery_date",
"column_break1",
"po_no", "po_no",
"po_date", "po_date",
"tax_id", "company",
"skip_delivery_note",
"amended_from",
"accounting_dimensions_section", "accounting_dimensions_section",
"cost_center", "cost_center",
"dimension_col_break", "dimension_col_break",
"project", "project",
"contact_info", "column_break_77",
"customer_address", "source",
"address_display", "campaign",
"contact_person", "custom_dimensions_section",
"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",
"currency_and_price_list", "currency_and_price_list",
"currency", "currency",
"conversion_rate", "conversion_rate",
@ -55,46 +43,32 @@
"plc_conversion_rate", "plc_conversion_rate",
"ignore_pricing_rule", "ignore_pricing_rule",
"sec_warehouse", "sec_warehouse",
"scan_barcode",
"column_break_28",
"set_warehouse", "set_warehouse",
"items_section", "items_section",
"scan_barcode",
"items", "items",
"packing_list",
"packed_items",
"pricing_rule_details",
"pricing_rules",
"section_break_31", "section_break_31",
"column_break_33a",
"total_qty", "total_qty",
"total_net_weight",
"column_break_33",
"base_total", "base_total",
"base_net_total", "base_net_total",
"column_break_33", "column_break_33a",
"total_net_weight",
"total", "total",
"net_total", "net_total",
"taxes_section", "taxes_section",
"tax_category", "taxes_and_charges",
"column_break_38", "column_break_38",
"tax_category",
"column_break_49",
"shipping_rule", "shipping_rule",
"section_break_40", "section_break_40",
"taxes_and_charges",
"taxes", "taxes",
"sec_tax_breakup",
"other_charges_calculation",
"section_break_43", "section_break_43",
"base_total_taxes_and_charges", "base_total_taxes_and_charges",
"column_break_46", "column_break_46",
"total_taxes_and_charges", "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", "totals",
"base_grand_total", "base_grand_total",
"base_rounding_adjustment", "base_rounding_adjustment",
@ -107,26 +81,49 @@
"in_words", "in_words",
"advance_paid", "advance_paid",
"disable_rounded_total", "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_schedule_section",
"payment_terms_section",
"payment_terms_template", "payment_terms_template",
"payment_schedule", "payment_schedule",
"terms_section_break", "terms_section_break",
"tc_name", "tc_name",
"terms", "terms",
"more_info", "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", "section_break_78",
"status", "status",
"delivery_status", "delivery_status",
@ -143,12 +140,29 @@
"total_commission", "total_commission",
"section_break1", "section_break1",
"sales_team", "sales_team",
"loyalty_points_redemption",
"loyalty_points",
"column_break_116",
"loyalty_amount",
"subscription_section", "subscription_section",
"from_date", "from_date",
"to_date", "to_date",
"column_break_108", "column_break_108",
"auto_repeat", "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": [ "fields": [
{ {
@ -334,10 +348,10 @@
"collapsible": 1, "collapsible": 1,
"depends_on": "customer", "depends_on": "customer",
"fieldname": "contact_info", "fieldname": "contact_info",
"fieldtype": "Section Break", "fieldtype": "Tab Break",
"hide_days": 1, "hide_days": 1,
"hide_seconds": 1, "hide_seconds": 1,
"label": "Address and Contact", "label": "Address & Contact",
"options": "fa fa-bullhorn" "options": "fa fa-bullhorn"
}, },
{ {
@ -414,9 +428,10 @@
}, },
{ {
"fieldname": "col_break46", "fieldname": "col_break46",
"fieldtype": "Column Break", "fieldtype": "Section Break",
"hide_days": 1, "hide_days": 1,
"hide_seconds": 1, "hide_seconds": 1,
"label": "Company Address",
"width": "50%" "width": "50%"
}, },
{ {
@ -550,8 +565,10 @@
{ {
"fieldname": "sec_warehouse", "fieldname": "sec_warehouse",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hide_border": 1,
"hide_days": 1, "hide_days": 1,
"hide_seconds": 1 "hide_seconds": 1,
"label": "Items"
}, },
{ {
"fieldname": "set_warehouse", "fieldname": "set_warehouse",
@ -565,6 +582,7 @@
{ {
"fieldname": "items_section", "fieldname": "items_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hide_border": 1,
"hide_days": 1, "hide_days": 1,
"hide_seconds": 1, "hide_seconds": 1,
"oldfieldtype": "Section Break", "oldfieldtype": "Section Break",
@ -675,6 +693,7 @@
"read_only": 1 "read_only": 1
}, },
{ {
"depends_on": "total_net_weight",
"fieldname": "total_net_weight", "fieldname": "total_net_weight",
"fieldtype": "Float", "fieldtype": "Float",
"hide_days": 1, "hide_days": 1,
@ -688,7 +707,7 @@
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hide_days": 1, "hide_days": 1,
"hide_seconds": 1, "hide_seconds": 1,
"label": "Taxes and Charges", "label": "Taxes",
"oldfieldtype": "Section Break", "oldfieldtype": "Section Break",
"options": "fa fa-money" "options": "fa fa-money"
}, },
@ -720,6 +739,7 @@
{ {
"fieldname": "section_break_40", "fieldname": "section_break_40",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hide_border": 1,
"hide_days": 1, "hide_days": 1,
"hide_seconds": 1 "hide_seconds": 1
}, },
@ -804,7 +824,7 @@
"hidden": 1, "hidden": 1,
"hide_days": 1, "hide_days": 1,
"hide_seconds": 1, "hide_seconds": 1,
"label": "Loyalty Points Redemption", "label": "Loyalty Points",
"print_hide": 1 "print_hide": 1
}, },
{ {
@ -828,12 +848,11 @@
}, },
{ {
"collapsible": 1, "collapsible": 1,
"collapsible_depends_on": "discount_amount",
"fieldname": "section_break_48", "fieldname": "section_break_48",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hide_days": 1, "hide_days": 1,
"hide_seconds": 1, "hide_seconds": 1,
"label": "Additional Discount and Coupon Code" "label": "Additional Discount"
}, },
{ {
"fieldname": "coupon_code", "fieldname": "coupon_code",
@ -891,6 +910,7 @@
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hide_days": 1, "hide_days": 1,
"hide_seconds": 1, "hide_seconds": 1,
"label": "Totals",
"oldfieldtype": "Section Break", "oldfieldtype": "Section Break",
"options": "fa fa-money", "options": "fa fa-money",
"print_hide": 1 "print_hide": 1
@ -1045,10 +1065,10 @@
}, },
{ {
"fieldname": "payment_schedule_section", "fieldname": "payment_schedule_section",
"fieldtype": "Section Break", "fieldtype": "Tab Break",
"hide_days": 1, "hide_days": 1,
"hide_seconds": 1, "hide_seconds": 1,
"label": "Payment Terms" "label": "Terms"
}, },
{ {
"fieldname": "payment_terms_template", "fieldname": "payment_terms_template",
@ -1070,13 +1090,12 @@
"print_hide": 1 "print_hide": 1
}, },
{ {
"collapsible": 1,
"collapsible_depends_on": "terms", "collapsible_depends_on": "terms",
"fieldname": "terms_section_break", "fieldname": "terms_section_break",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hide_days": 1, "hide_days": 1,
"hide_seconds": 1, "hide_seconds": 1,
"label": "Terms and Conditions", "label": "Terms & Conditions",
"oldfieldtype": "Section Break", "oldfieldtype": "Section Break",
"options": "fa fa-legal" "options": "fa fa-legal"
}, },
@ -1104,10 +1123,10 @@
"collapsible": 1, "collapsible": 1,
"collapsible_depends_on": "project", "collapsible_depends_on": "project",
"fieldname": "more_info", "fieldname": "more_info",
"fieldtype": "Section Break", "fieldtype": "Tab Break",
"hide_days": 1, "hide_days": 1,
"hide_seconds": 1, "hide_seconds": 1,
"label": "More Information", "label": "More Info",
"oldfieldtype": "Section Break", "oldfieldtype": "Section Break",
"options": "fa fa-file-text", "options": "fa fa-file-text",
"print_hide": 1 "print_hide": 1
@ -1122,7 +1141,6 @@
"read_only": 1 "read_only": 1
}, },
{ {
"description": "Track this Sales Order against any Project",
"fieldname": "project", "fieldname": "project",
"fieldtype": "Link", "fieldtype": "Link",
"hide_days": 1, "hide_days": 1,
@ -1240,7 +1258,7 @@
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hide_days": 1, "hide_days": 1,
"hide_seconds": 1, "hide_seconds": 1,
"label": "Billing and Delivery Status", "label": "Status",
"oldfieldtype": "Column Break", "oldfieldtype": "Column Break",
"print_hide": 1, "print_hide": 1,
"width": "50%" "width": "50%"
@ -1410,7 +1428,7 @@
"fieldtype": "Section Break", "fieldtype": "Section Break",
"hide_days": 1, "hide_days": 1,
"hide_seconds": 1, "hide_seconds": 1,
"label": "Auto Repeat Section", "label": "Auto Repeat",
"no_copy": 1, "no_copy": 1,
"print_hide": 1, "print_hide": 1,
"read_only": 1 "read_only": 1
@ -1542,13 +1560,76 @@
{ {
"fieldname": "dimension_col_break", "fieldname": "dimension_col_break",
"fieldtype": "Column 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", "icon": "fa fa-file-text",
"idx": 105, "idx": 105,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2022-09-16 17:43:57.007441", "modified": "2022-10-11 13:06:10.469796",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Selling", "module": "Selling",
"name": "Sales Order", "name": "Sales Order",

View File

@ -18,6 +18,7 @@ from erpnext.accounts.doctype.sales_invoice.sales_invoice import (
update_linked_doc, update_linked_doc,
validate_inter_company_party, validate_inter_company_party,
) )
from erpnext.accounts.party import get_party_account
from erpnext.controllers.selling_controller import SellingController from erpnext.controllers.selling_controller import SellingController
from erpnext.manufacturing.doctype.production_plan.production_plan import ( from erpnext.manufacturing.doctype.production_plan.production_plan import (
get_items_for_material_requests, get_items_for_material_requests,
@ -626,6 +627,7 @@ def make_project(source_name, target_doc=None):
"field_map": { "field_map": {
"name": "sales_order", "name": "sales_order",
"base_grand_total": "estimated_costing", "base_grand_total": "estimated_costing",
"net_total": "total_sales_amount",
}, },
}, },
}, },
@ -727,6 +729,8 @@ def make_sales_invoice(source_name, target_doc=None, ignore_permissions=False):
if source.loyalty_points and source.order_type == "Shopping Cart": if source.loyalty_points and source.order_type == "Shopping Cart":
target.redeem_loyalty_points = 1 target.redeem_loyalty_points = 1
target.debit_to = get_party_account("Customer", source.customer, source.company)
def update_item(source, target, source_parent): def update_item(source, target, source_parent):
target.amount = flt(source.amount) - flt(source.billed_amt) target.amount = flt(source.amount) - flt(source.billed_amt)
target.base_amount = target.amount * flt(source_parent.conversion_rate) target.base_amount = target.amount * flt(source_parent.conversion_rate)

View File

@ -272,6 +272,7 @@
}, },
{ {
"collapsible": 1, "collapsible": 1,
"collapsible_depends_on": "eval: doc.margin_type || doc.discount_amount",
"fieldname": "discount_and_margin", "fieldname": "discount_and_margin",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Discount and Margin" "label": "Discount and Margin"
@ -842,7 +843,7 @@
"idx": 1, "idx": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2022-09-06 13:24:18.065312", "modified": "2022-10-26 16:05:02.712705",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Selling", "module": "Selling",
"name": "Sales Order Item", "name": "Sales Order Item",

View File

@ -74,7 +74,35 @@ function get_filters() {
] ]
} }
} }
} },
{
"fieldname":"from_due_date",
"label": __("From Due Date"),
"fieldtype": "Date",
},
{
"fieldname":"to_due_date",
"label": __("To Due Date"),
"fieldtype": "Date",
},
{
"fieldname":"status",
"label": __("Status"),
"fieldtype": "MultiSelectList",
"width": 100,
get_data: function(txt) {
let status = ["Overdue", "Unpaid", "Completed", "Partly Paid"]
let options = []
for (let option of status){
options.push({
"value": option,
"label": __(option),
"description": ""
})
}
return options
}
},
] ]
return filters; return filters;
} }

Some files were not shown because too many files have changed in this diff Show More