diff --git a/.github/workflows/initiate_release.yml b/.github/workflows/initiate_release.yml index ef38974ae2..ee60bad104 100644 --- a/.github/workflows/initiate_release.yml +++ b/.github/workflows/initiate_release.yml @@ -9,7 +9,7 @@ on: workflow_dispatch: jobs: - release: + stable-release: name: Release runs-on: ubuntu-latest strategy: @@ -30,3 +30,23 @@ jobs: head: version-${{ matrix.version }}-hotfix env: GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} + + beta-release: + name: Release + runs-on: ubuntu-latest + strategy: + fail-fast: false + + steps: + - uses: octokit/request-action@v2.x + with: + route: POST /repos/{owner}/{repo}/pulls + owner: frappe + repo: erpnext + title: |- + "chore: release v15 beta" + body: "Automated beta release." + base: version-15-beta + head: develop + env: + GITHUB_TOKEN: ${{ secrets.RELEASE_TOKEN }} diff --git a/.github/workflows/release_notes.yml b/.github/workflows/release_notes.yml new file mode 100644 index 0000000000..e765a66f69 --- /dev/null +++ b/.github/workflows/release_notes.yml @@ -0,0 +1,38 @@ +# This action: +# +# 1. Generates release notes using github API. +# 2. Strips unnecessary info like chore/style etc from notes. +# 3. Updates release info. + +# This action needs to be maintained on all branches that do releases. + +name: 'Release Notes' + +on: + workflow_dispatch: + inputs: + tag_name: + description: 'Tag of release like v13.0.0' + required: true + type: string + release: + types: [released] + +permissions: + contents: read + +jobs: + regen-notes: + name: 'Regenerate release notes' + runs-on: ubuntu-latest + + steps: + - name: Update notes + run: | + NEW_NOTES=$(gh api --method POST -H "Accept: application/vnd.github+json" /repos/frappe/erpnext/releases/generate-notes -f tag_name=$RELEASE_TAG | jq -r '.body' | sed -E '/^\* (chore|ci|test|docs|style)/d' ) + RELEASE_ID=$(gh api -H "Accept: application/vnd.github+json" /repos/frappe/erpnext/releases/tags/$RELEASE_TAG | jq -r '.id') + gh api --method PATCH -H "Accept: application/vnd.github+json" /repos/frappe/erpnext/releases/$RELEASE_ID -f body="$NEW_NOTES" + + env: + GH_TOKEN: ${{ secrets.RELEASE_TOKEN }} + RELEASE_TAG: ${{ github.event.inputs.tag_name || github.event.release.tag_name }} diff --git a/erpnext/accounts/deferred_revenue.py b/erpnext/accounts/deferred_revenue.py index 45e04ee6b0..fb49ef3a42 100644 --- a/erpnext/accounts/deferred_revenue.py +++ b/erpnext/accounts/deferred_revenue.py @@ -136,7 +136,7 @@ def convert_deferred_revenue_to_income( send_mail(deferred_process) -def get_booking_dates(doc, item, posting_date=None): +def get_booking_dates(doc, item, posting_date=None, prev_posting_date=None): if not posting_date: posting_date = add_days(today(), -1) @@ -146,39 +146,42 @@ def get_booking_dates(doc, item, posting_date=None): "deferred_revenue_account" if doc.doctype == "Sales Invoice" else "deferred_expense_account" ) - prev_gl_entry = frappe.db.sql( - """ - select name, posting_date from `tabGL Entry` where company=%s and account=%s and - voucher_type=%s and voucher_no=%s and voucher_detail_no=%s - and is_cancelled = 0 - order by posting_date desc limit 1 - """, - (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), - as_dict=True, - ) + if not prev_posting_date: + prev_gl_entry = frappe.db.sql( + """ + select name, posting_date from `tabGL Entry` where company=%s and account=%s and + voucher_type=%s and voucher_no=%s and voucher_detail_no=%s + and is_cancelled = 0 + order by posting_date desc limit 1 + """, + (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), + as_dict=True, + ) - prev_gl_via_je = frappe.db.sql( - """ - SELECT p.name, p.posting_date FROM `tabJournal Entry` p, `tabJournal Entry Account` c - WHERE p.name = c.parent and p.company=%s and c.account=%s - and c.reference_type=%s and c.reference_name=%s - and c.reference_detail_no=%s and c.docstatus < 2 order by posting_date desc limit 1 - """, - (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), - as_dict=True, - ) + prev_gl_via_je = frappe.db.sql( + """ + SELECT p.name, p.posting_date FROM `tabJournal Entry` p, `tabJournal Entry Account` c + WHERE p.name = c.parent and p.company=%s and c.account=%s + and c.reference_type=%s and c.reference_name=%s + and c.reference_detail_no=%s and c.docstatus < 2 order by posting_date desc limit 1 + """, + (doc.company, item.get(deferred_account), doc.doctype, doc.name, item.name), + as_dict=True, + ) - if prev_gl_via_je: - if (not prev_gl_entry) or ( - prev_gl_entry and prev_gl_entry[0].posting_date < prev_gl_via_je[0].posting_date - ): - prev_gl_entry = prev_gl_via_je + if prev_gl_via_je: + if (not prev_gl_entry) or ( + prev_gl_entry and prev_gl_entry[0].posting_date < prev_gl_via_je[0].posting_date + ): + prev_gl_entry = prev_gl_via_je + + if prev_gl_entry: + start_date = getdate(add_days(prev_gl_entry[0].posting_date, 1)) + else: + start_date = item.service_start_date - if prev_gl_entry: - start_date = getdate(add_days(prev_gl_entry[0].posting_date, 1)) else: - start_date = item.service_start_date - + start_date = getdate(add_days(prev_posting_date, 1)) end_date = get_last_day(start_date) if end_date >= item.service_end_date: end_date = item.service_end_date @@ -341,9 +344,15 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None): accounts_frozen_upto = frappe.get_cached_value("Accounts Settings", "None", "acc_frozen_upto") def _book_deferred_revenue_or_expense( - item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on + item, + via_journal_entry, + submit_journal_entry, + book_deferred_entries_based_on, + prev_posting_date=None, ): - start_date, end_date, last_gl_entry = get_booking_dates(doc, item, posting_date=posting_date) + start_date, end_date, last_gl_entry = get_booking_dates( + doc, item, posting_date=posting_date, prev_posting_date=prev_posting_date + ) if not (start_date and end_date): return @@ -377,9 +386,12 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None): if not amount: return + gl_posting_date = end_date + prev_posting_date = None # check if books nor frozen till endate: if accounts_frozen_upto and getdate(end_date) <= getdate(accounts_frozen_upto): - end_date = get_last_day(add_days(accounts_frozen_upto, 1)) + gl_posting_date = get_last_day(add_days(accounts_frozen_upto, 1)) + prev_posting_date = end_date if via_journal_entry: book_revenue_via_journal_entry( @@ -388,7 +400,7 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None): debit_account, amount, base_amount, - end_date, + gl_posting_date, project, account_currency, item.cost_center, @@ -404,7 +416,7 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None): against, amount, base_amount, - end_date, + gl_posting_date, project, account_currency, item.cost_center, @@ -418,7 +430,11 @@ def book_deferred_income_or_expense(doc, deferred_process, posting_date=None): if getdate(end_date) < getdate(posting_date) and not last_gl_entry: _book_deferred_revenue_or_expense( - item, via_journal_entry, submit_journal_entry, book_deferred_entries_based_on + item, + via_journal_entry, + submit_journal_entry, + book_deferred_entries_based_on, + prev_posting_date, ) via_journal_entry = cint( diff --git a/erpnext/accounts/doctype/account/chart_of_accounts/verified/nl_grootboekschema.json b/erpnext/accounts/doctype/account/chart_of_accounts/verified/nl_grootboekschema.json index 58b91227f7..9fb47bb02d 100644 --- a/erpnext/accounts/doctype/account/chart_of_accounts/verified/nl_grootboekschema.json +++ b/erpnext/accounts/doctype/account/chart_of_accounts/verified/nl_grootboekschema.json @@ -2,75 +2,13 @@ "country_code": "nl", "name": "Netherlands - Grootboekschema", "tree": { - "FABRIKAGEREKENINGEN": { - "is_group": 1, - "root_type": "Expense" - }, "FINANCIELE REKENINGEN, KORTLOPENDE VORDERINGEN EN SCHULDEN": { "Bank": { "RABO Bank": { "account_type": "Bank" }, "account_type": "Bank" - }, - "KORTLOPENDE SCHULDEN": { - "Af te dragen Btw-verlegd": { - "account_type": "Tax" - }, - "Afdracht loonheffing": {}, - "Btw af te dragen hoog": { - "account_type": "Tax" - }, - "Btw af te dragen laag": { - "account_type": "Tax" - }, - "Btw af te dragen overig": { - "account_type": "Tax" - }, - "Btw oude jaren": { - "account_type": "Tax" - }, - "Btw te vorderen hoog": { - "account_type": "Tax" - }, - "Btw te vorderen laag": { - "account_type": "Tax" - }, - "Btw te vorderen overig": { - "account_type": "Tax" - }, - "Btw-afdracht": { - "account_type": "Tax" - }, - "Crediteuren": { - "account_type": "Payable" - }, - "Dividend": {}, - "Dividendbelasting": {}, - "Energiekosten 1": {}, - "Investeringsaftrek": {}, - "Loonheffing": {}, - "Overige te betalen posten": {}, - "Pensioenpremies 1": {}, - "Premie WIR": {}, - "Rekening-courant inkoopvereniging": {}, - "Rente": {}, - "Sociale lasten 1": {}, - "Stock Recieved niet gefactureerd": { - "account_type": "Stock Received But Not Billed" - }, - "Tanti\u00e8mes 1": {}, - "Te vorderen Btw-verlegd": { - "account_type": "Tax" - }, - "Telefoon/telefax 1": {}, - "Termijnen onderh. werk": {}, - "Vakantiedagen": {}, - "Vakantiegeld 1": {}, - "Vakantiezegels": {}, - "Vennootschapsbelasting": {}, - "Vooruit ontvangen bedr.": {} - }, + }, "LIQUIDE MIDDELEN": { "ABN-AMRO bank": {}, "Bankbetaalkaarten": {}, @@ -91,6 +29,110 @@ }, "account_type": "Cash" }, + "TUSSENREKENINGEN": { + "Betaalwijze cadeaubonnen": { + "account_type": "Cash" + }, + "Betaalwijze chipknip": { + "account_type": "Cash" + }, + "Betaalwijze contant": { + "account_type": "Cash" + }, + "Betaalwijze pin": { + "account_type": "Cash" + }, + "Inkopen Nederland hoog": { + "account_type": "Cash" + }, + "Inkopen Nederland laag": { + "account_type": "Cash" + }, + "Inkopen Nederland onbelast": { + "account_type": "Cash" + }, + "Inkopen Nederland overig": { + "account_type": "Cash" + }, + "Inkopen Nederland verlegd": { + "account_type": "Cash" + }, + "Inkopen binnen EU hoog": { + "account_type": "Cash" + }, + "Inkopen binnen EU laag": { + "account_type": "Cash" + }, + "Inkopen binnen EU overig": { + "account_type": "Cash" + }, + "Inkopen buiten EU hoog": { + "account_type": "Cash" + }, + "Inkopen buiten EU laag": { + "account_type": "Cash" + }, + "Inkopen buiten EU overig": { + "account_type": "Cash" + }, + "Kassa 1": { + "account_type": "Cash" + }, + "Kassa 2": { + "account_type": "Cash" + }, + "Netto lonen": { + "account_type": "Cash" + }, + "Tegenrekening Inkopen": { + "account_type": "Cash" + }, + "Tussenrek. autom. betalingen": { + "account_type": "Cash" + }, + "Tussenrek. autom. loonbetalingen": { + "account_type": "Cash" + }, + "Tussenrek. cadeaubonbetalingen": { + "account_type": "Cash" + }, + "Tussenrekening balans": { + "account_type": "Cash" + }, + "Tussenrekening chipknip": { + "account_type": "Cash" + }, + "Tussenrekening correcties": { + "account_type": "Cash" + }, + "Tussenrekening pin": { + "account_type": "Cash" + }, + "Vraagposten": { + "account_type": "Cash" + }, + "VOORRAAD GRONDSTOFFEN, HULPMATERIALEN EN HANDELSGOEDEREN": { + "Emballage": {}, + "Gereed product 1": {}, + "Gereed product 2": {}, + "Goederen 1": {}, + "Goederen 2": {}, + "Goederen in consignatie": {}, + "Goederen onderweg": {}, + "Grondstoffen 1": {}, + "Grondstoffen 2": {}, + "Halffabrikaten 1": {}, + "Halffabrikaten 2": {}, + "Hulpstoffen 1": {}, + "Hulpstoffen 2": {}, + "Kantoorbenodigdheden": {}, + "Onderhanden werk": {}, + "Verpakkingsmateriaal": {}, + "Zegels": {}, + "root_type": "Asset" + }, + "root_type": "Asset" + }, "VORDERINGEN": { "Debiteuren": { "account_type": "Receivable" @@ -104,278 +146,299 @@ "Voorziening dubieuze debiteuren": {} }, "root_type": "Asset" - }, - "INDIRECTE KOSTEN": { + }, + "KORTLOPENDE SCHULDEN": { + "Af te dragen Btw-verlegd": { + "account_type": "Tax" + }, + "Afdracht loonheffing": {}, + "Btw af te dragen hoog": { + "account_type": "Tax" + }, + "Btw af te dragen laag": { + "account_type": "Tax" + }, + "Btw af te dragen overig": { + "account_type": "Tax" + }, + "Btw oude jaren": { + "account_type": "Tax" + }, + "Btw te vorderen hoog": { + "account_type": "Tax" + }, + "Btw te vorderen laag": { + "account_type": "Tax" + }, + "Btw te vorderen overig": { + "account_type": "Tax" + }, + "Btw-afdracht": { + "account_type": "Tax" + }, + "Crediteuren": { + "account_type": "Payable" + }, + "Dividend": {}, + "Dividendbelasting": {}, + "Energiekosten 1": {}, + "Investeringsaftrek": {}, + "Loonheffing": {}, + "Overige te betalen posten": {}, + "Pensioenpremies 1": {}, + "Premie WIR": {}, + "Rekening-courant inkoopvereniging": {}, + "Rente": {}, + "Sociale lasten 1": {}, + "Stock Recieved niet gefactureerd": { + "account_type": "Stock Received But Not Billed" + }, + "Tanti\u00e8mes 1": {}, + "Te vorderen Btw-verlegd": { + "account_type": "Tax" + }, + "Telefoon/telefax 1": {}, + "Termijnen onderh. werk": {}, + "Vakantiedagen": {}, + "Vakantiegeld 1": {}, + "Vakantiezegels": {}, + "Vennootschapsbelasting": {}, + "Vooruit ontvangen bedr.": {}, + "is_group": 1, + "root_type": "Liability" + }, + "FABRIKAGEREKENINGEN": { "is_group": 1, - "root_type": "Expense" - }, - "KOSTENREKENINGEN": { - "AFSCHRIJVINGEN": { - "Aanhangwagens": {}, - "Aankoopkosten": {}, - "Aanloopkosten": {}, - "Auteursrechten": {}, - "Bedrijfsgebouwen": {}, - "Bedrijfsinventaris": { + "root_type": "Expense", + "INDIRECTE KOSTEN": { + "is_group": 1, + "root_type": "Expense" + }, + "KOSTENREKENINGEN": { + "AFSCHRIJVINGEN": { + "Aanhangwagens": {}, + "Aankoopkosten": {}, + "Aanloopkosten": {}, + "Auteursrechten": {}, + "Bedrijfsgebouwen": {}, + "Bedrijfsinventaris": { + "account_type": "Depreciation" + }, + "Drankvergunningen": {}, + "Fabrieksinventaris": { + "account_type": "Depreciation" + }, + "Gebouwen": {}, + "Gereedschappen": {}, + "Goodwill": {}, + "Grondverbetering": {}, + "Heftrucks": {}, + "Kantine-inventaris": {}, + "Kantoorinventaris": { + "account_type": "Depreciation" + }, + "Kantoormachines": {}, + "Licenties": {}, + "Machines 1": {}, + "Magazijninventaris": {}, + "Octrooien": {}, + "Ontwikkelingskosten": {}, + "Pachtersinvestering": {}, + "Parkeerplaats": {}, + "Personenauto's": { + "account_type": "Depreciation" + }, + "Rijwielen en bromfietsen": {}, + "Tonnagevergunningen": {}, + "Verbouwingen": {}, + "Vergunningen": {}, + "Voorraadverschillen": {}, + "Vrachtauto's": {}, + "Winkels": {}, + "Woon-winkelhuis": {}, "account_type": "Depreciation" }, - "Drankvergunningen": {}, - "Fabrieksinventaris": { - "account_type": "Depreciation" + "ALGEMENE KOSTEN": { + "Accountantskosten": {}, + "Advieskosten": {}, + "Assuranties 1": {}, + "Bankkosten": {}, + "Juridische kosten": {}, + "Overige algemene kosten": {}, + "Toev. Ass. eigen risico": {} }, - "Gebouwen": {}, - "Gereedschappen": {}, - "Goodwill": {}, - "Grondverbetering": {}, - "Heftrucks": {}, - "Kantine-inventaris": {}, - "Kantoorinventaris": { - "account_type": "Depreciation" + "BEDRIJFSKOSTEN": { + "Assuranties 2": {}, + "Energie (krachtstroom)": {}, + "Gereedschappen 1": {}, + "Hulpmaterialen 1": {}, + "Huur inventaris": {}, + "Huur machines": {}, + "Leasing invent.operational": {}, + "Leasing mach. operational": {}, + "Onderhoud inventaris": {}, + "Onderhoud machines": {}, + "Ophalen/vervoer afval": {}, + "Overige bedrijfskosten": {} }, - "Kantoormachines": {}, - "Licenties": {}, - "Machines 1": {}, - "Magazijninventaris": {}, - "Octrooien": {}, - "Ontwikkelingskosten": {}, - "Pachtersinvestering": {}, - "Parkeerplaats": {}, - "Personenauto's": { - "account_type": "Depreciation" + "FINANCIERINGSKOSTEN 1": { + "Overige rentebaten": {}, + "Overige rentelasten": {}, + "Rente bankkrediet": {}, + "Rente huurkoopcontracten": {}, + "Rente hypotheek": {}, + "Rente leasecontracten": {}, + "Rente lening o/g": {}, + "Rente lening u/g": {} }, - "Rijwielen en bromfietsen": {}, - "Tonnagevergunningen": {}, - "Verbouwingen": {}, - "Vergunningen": {}, - "Voorraadverschillen": {}, - "Vrachtauto's": {}, - "Winkels": {}, - "Woon-winkelhuis": {}, - "account_type": "Depreciation" - }, - "ALGEMENE KOSTEN": { - "Accountantskosten": {}, - "Advieskosten": {}, - "Assuranties 1": {}, - "Bankkosten": {}, - "Juridische kosten": {}, - "Overige algemene kosten": {}, - "Toev. Ass. eigen risico": {} - }, - "BEDRIJFSKOSTEN": { - "Assuranties 2": {}, - "Energie (krachtstroom)": {}, - "Gereedschappen 1": {}, - "Hulpmaterialen 1": {}, - "Huur inventaris": {}, - "Huur machines": {}, - "Leasing invent.operational": {}, - "Leasing mach. operational": {}, - "Onderhoud inventaris": {}, - "Onderhoud machines": {}, - "Ophalen/vervoer afval": {}, - "Overige bedrijfskosten": {} - }, - "FINANCIERINGSKOSTEN 1": { - "Overige rentebaten": {}, - "Overige rentelasten": {}, - "Rente bankkrediet": {}, - "Rente huurkoopcontracten": {}, - "Rente hypotheek": {}, - "Rente leasecontracten": {}, - "Rente lening o/g": {}, - "Rente lening u/g": {} - }, - "HUISVESTINGSKOSTEN": { - "Assurantie onroerend goed": {}, - "Belastingen onr. Goed": {}, - "Energiekosten": {}, - "Groot onderhoud onr. Goed": {}, - "Huur": {}, - "Huurwaarde woongedeelte": {}, - "Onderhoud onroerend goed": {}, - "Ontvangen huren": {}, - "Overige huisvestingskosten": {}, - "Pacht": {}, - "Schoonmaakkosten": {}, - "Toevoeging egalisatieres. Groot onderhoud": {} - }, - "KANTOORKOSTEN": { - "Administratiekosten": {}, - "Contributies/abonnementen": {}, - "Huur kantoorapparatuur": {}, - "Internetaansluiting": {}, - "Kantoorbenodigdh./drukw.": {}, - "Onderhoud kantoorinvent.": {}, - "Overige kantoorkosten": {}, - "Porti": {}, - "Telefoon/telefax": {} - }, - "OVERIGE BATEN EN LASTEN": { - "Betaalde schadevergoed.": {}, - "Boekverlies vaste activa": {}, - "Boekwinst van vaste activa": {}, - "K.O. regeling OB": {}, - "Kasverschillen": {}, - "Kosten loonbelasting": {}, - "Kosten omzetbelasting": {}, - "Nadelige koersverschillen": {}, - "Naheffing bedrijfsver.": {}, - "Ontvangen schadevergoed.": {}, - "Overige baten": {}, - "Overige lasten": {}, - "Voordelige koersverschil.": {} - }, - "PERSONEELSKOSTEN": { - "Autokostenvergoeding": {}, - "Bedrijfskleding": {}, - "Belastingvrije uitkeringen": {}, - "Bijzondere beloningen": {}, - "Congressen, seminars en symposia": {}, - "Gereedschapsgeld": {}, - "Geschenken personeel": {}, - "Gratificaties": {}, - "Inhouding pensioenpremies": {}, - "Inhouding sociale lasten": {}, - "Kantinekosten": {}, - "Lonen en salarissen": {}, - "Loonwerk": {}, - "Managementvergoedingen": {}, - "Opleidingskosten": {}, - "Oprenting stamrechtverpl.": {}, - "Overhevelingstoeslag": {}, - "Overige kostenverg.": {}, - "Overige personeelskosten": {}, - "Overige uitkeringen": {}, - "Pensioenpremies": {}, - "Provisie 1": {}, - "Reiskosten": {}, - "Rijwielvergoeding": {}, - "Sociale lasten": {}, - "Tanti\u00e8mes": {}, - "Thuiswerkers": {}, - "Toev. Backservice pens.verpl.": {}, - "Toevoeging pensioenverpl.": {}, - "Uitkering ziekengeld": {}, - "Uitzendkrachten": {}, - "Vakantiebonnen": {}, - "Vakantiegeld": {}, - "Vergoeding studiekosten": {}, - "Wervingskosten personeel": {} - }, - "VERKOOPKOSTEN": { - "Advertenties": {}, - "Afschrijving dubieuze deb.": {}, - "Beurskosten": {}, - "Etalagekosten": {}, - "Exportkosten": {}, - "Kascorrecties": {}, - "Overige verkoopkosten": {}, - "Provisie": {}, - "Reclame": {}, - "Reis en verblijfkosten": {}, - "Relatiegeschenken": {}, - "Representatiekosten": {}, - "Uitgaande vrachten": {}, - "Veilingkosten": {}, - "Verpakkingsmateriaal 1": {}, - "Websitekosten": {} - }, - "VERVOERSKOSTEN": { - "Assuranties auto's": {}, - "Brandstoffen": {}, - "Leasing auto's": {}, - "Onderhoud personenauto's": {}, - "Onderhoud vrachtauto's": {}, - "Overige vervoerskosten": {}, - "Priv\u00e9-gebruik auto's": {}, - "Wegenbelasting": {} - }, - "root_type": "Expense" - }, - "TUSSENREKENINGEN": { - "Betaalwijze cadeaubonnen": { - "account_type": "Cash" - }, - "Betaalwijze chipknip": { - "account_type": "Cash" - }, - "Betaalwijze contant": { - "account_type": "Cash" - }, - "Betaalwijze pin": { - "account_type": "Cash" - }, - "Inkopen Nederland hoog": { - "account_type": "Cash" - }, - "Inkopen Nederland laag": { - "account_type": "Cash" - }, - "Inkopen Nederland onbelast": { - "account_type": "Cash" - }, - "Inkopen Nederland overig": { - "account_type": "Cash" - }, - "Inkopen Nederland verlegd": { - "account_type": "Cash" - }, - "Inkopen binnen EU hoog": { - "account_type": "Cash" - }, - "Inkopen binnen EU laag": { - "account_type": "Cash" - }, - "Inkopen binnen EU overig": { - "account_type": "Cash" - }, - "Inkopen buiten EU hoog": { - "account_type": "Cash" - }, - "Inkopen buiten EU laag": { - "account_type": "Cash" - }, - "Inkopen buiten EU overig": { - "account_type": "Cash" - }, - "Kassa 1": { - "account_type": "Cash" - }, - "Kassa 2": { - "account_type": "Cash" - }, - "Netto lonen": { - "account_type": "Cash" - }, - "Tegenrekening Inkopen": { - "account_type": "Cash" - }, - "Tussenrek. autom. betalingen": { - "account_type": "Cash" - }, - "Tussenrek. autom. loonbetalingen": { - "account_type": "Cash" - }, - "Tussenrek. cadeaubonbetalingen": { - "account_type": "Cash" - }, - "Tussenrekening balans": { - "account_type": "Cash" - }, - "Tussenrekening chipknip": { - "account_type": "Cash" - }, - "Tussenrekening correcties": { - "account_type": "Cash" - }, - "Tussenrekening pin": { - "account_type": "Cash" - }, - "Vraagposten": { - "account_type": "Cash" - }, - "root_type": "Asset" + "HUISVESTINGSKOSTEN": { + "Assurantie onroerend goed": {}, + "Belastingen onr. Goed": {}, + "Energiekosten": {}, + "Groot onderhoud onr. Goed": {}, + "Huur": {}, + "Huurwaarde woongedeelte": {}, + "Onderhoud onroerend goed": {}, + "Ontvangen huren": {}, + "Overige huisvestingskosten": {}, + "Pacht": {}, + "Schoonmaakkosten": {}, + "Toevoeging egalisatieres. Groot onderhoud": {} + }, + "KANTOORKOSTEN": { + "Administratiekosten": {}, + "Contributies/abonnementen": {}, + "Huur kantoorapparatuur": {}, + "Internetaansluiting": {}, + "Kantoorbenodigdh./drukw.": {}, + "Onderhoud kantoorinvent.": {}, + "Overige kantoorkosten": {}, + "Porti": {}, + "Telefoon/telefax": {} + }, + "OVERIGE BATEN EN LASTEN": { + "Betaalde schadevergoed.": {}, + "Boekverlies vaste activa": {}, + "Boekwinst van vaste activa": {}, + "K.O. regeling OB": {}, + "Kasverschillen": {}, + "Kosten loonbelasting": {}, + "Kosten omzetbelasting": {}, + "Nadelige koersverschillen": {}, + "Naheffing bedrijfsver.": {}, + "Ontvangen schadevergoed.": {}, + "Overige baten": {}, + "Overige lasten": {}, + "Voordelige koersverschil.": {} + }, + "PERSONEELSKOSTEN": { + "Autokostenvergoeding": {}, + "Bedrijfskleding": {}, + "Belastingvrije uitkeringen": {}, + "Bijzondere beloningen": {}, + "Congressen, seminars en symposia": {}, + "Gereedschapsgeld": {}, + "Geschenken personeel": {}, + "Gratificaties": {}, + "Inhouding pensioenpremies": {}, + "Inhouding sociale lasten": {}, + "Kantinekosten": {}, + "Lonen en salarissen": {}, + "Loonwerk": {}, + "Managementvergoedingen": {}, + "Opleidingskosten": {}, + "Oprenting stamrechtverpl.": {}, + "Overhevelingstoeslag": {}, + "Overige kostenverg.": {}, + "Overige personeelskosten": {}, + "Overige uitkeringen": {}, + "Pensioenpremies": {}, + "Provisie 1": {}, + "Reiskosten": {}, + "Rijwielvergoeding": {}, + "Sociale lasten": {}, + "Tanti\u00e8mes": {}, + "Thuiswerkers": {}, + "Toev. Backservice pens.verpl.": {}, + "Toevoeging pensioenverpl.": {}, + "Uitkering ziekengeld": {}, + "Uitzendkrachten": {}, + "Vakantiebonnen": {}, + "Vakantiegeld": {}, + "Vergoeding studiekosten": {}, + "Wervingskosten personeel": {} + }, + "VERKOOPKOSTEN": { + "Advertenties": {}, + "Afschrijving dubieuze deb.": {}, + "Beurskosten": {}, + "Etalagekosten": {}, + "Exportkosten": {}, + "Kascorrecties": {}, + "Overige verkoopkosten": {}, + "Provisie": {}, + "Reclame": {}, + "Reis en verblijfkosten": {}, + "Relatiegeschenken": {}, + "Representatiekosten": {}, + "Uitgaande vrachten": {}, + "Veilingkosten": {}, + "Verpakkingsmateriaal 1": {}, + "Websitekosten": {} + }, + "VERVOERSKOSTEN": { + "Assuranties auto's": {}, + "Brandstoffen": {}, + "Leasing auto's": {}, + "Onderhoud personenauto's": {}, + "Onderhoud vrachtauto's": {}, + "Overige vervoerskosten": {}, + "Priv\u00e9-gebruik auto's": {}, + "Wegenbelasting": {} + }, + "VOORRAAD GEREED PRODUCT EN ONDERHANDEN WERK": { + "Betalingskort. crediteuren": {}, + "Garantiekosten": {}, + "Hulpmaterialen": {}, + "Inkomende vrachten": { + "account_type": "Expenses Included In Valuation" + }, + "Inkoop import buiten EU hoog": {}, + "Inkoop import buiten EU laag": {}, + "Inkoop import buiten EU overig": {}, + "Inkoopbonussen": {}, + "Inkoopkosten": {}, + "Inkoopprovisie": {}, + "Inkopen BTW verlegd": {}, + "Inkopen EU hoog tarief": {}, + "Inkopen EU laag tarief": {}, + "Inkopen EU overig": {}, + "Inkopen hoog": {}, + "Inkopen laag": {}, + "Inkopen nul": {}, + "Inkopen overig": {}, + "Invoerkosten": {}, + "Kosten inkoopvereniging": {}, + "Kostprijs omzet grondstoffen": { + "account_type": "Cost of Goods Sold" + }, + "Kostprijs omzet handelsgoederen": {}, + "Onttrekking uitgev.garantie": {}, + "Priv\u00e9-gebruik goederen": {}, + "Stock aanpassing": { + "account_type": "Stock Adjustment" + }, + "Tegenrekening inkoop": {}, + "Toev. Voorz. incour. grondst.": {}, + "Toevoeging garantieverpl.": {}, + "Toevoeging voorz. incour. handelsgoed.": {}, + "Uitbesteed werk": {}, + "Voorz. Incourourant grondst.": {}, + "Voorz.incour. handelsgoed.": {}, + "root_type": "Expense" + }, + "root_type": "Expense" + } }, "VASTE ACTIVA, EIGEN VERMOGEN, LANGLOPEND VREEMD VERMOGEN EN VOORZIENINGEN": { "EIGEN VERMOGEN": { @@ -602,7 +665,7 @@ "account_type": "Equity" } }, - "root_type": "Asset" + "root_type": "Equity" }, "VERKOOPRESULTATEN": { "Diensten fabric. 0% niet-EU": {}, @@ -627,67 +690,6 @@ "Verleende Kredietbep. fabricage": {}, "Verleende Kredietbep. handel": {}, "root_type": "Income" - }, - "VOORRAAD GEREED PRODUCT EN ONDERHANDEN WERK": { - "Betalingskort. crediteuren": {}, - "Garantiekosten": {}, - "Hulpmaterialen": {}, - "Inkomende vrachten": { - "account_type": "Expenses Included In Valuation" - }, - "Inkoop import buiten EU hoog": {}, - "Inkoop import buiten EU laag": {}, - "Inkoop import buiten EU overig": {}, - "Inkoopbonussen": {}, - "Inkoopkosten": {}, - "Inkoopprovisie": {}, - "Inkopen BTW verlegd": {}, - "Inkopen EU hoog tarief": {}, - "Inkopen EU laag tarief": {}, - "Inkopen EU overig": {}, - "Inkopen hoog": {}, - "Inkopen laag": {}, - "Inkopen nul": {}, - "Inkopen overig": {}, - "Invoerkosten": {}, - "Kosten inkoopvereniging": {}, - "Kostprijs omzet grondstoffen": { - "account_type": "Cost of Goods Sold" - }, - "Kostprijs omzet handelsgoederen": {}, - "Onttrekking uitgev.garantie": {}, - "Priv\u00e9-gebruik goederen": {}, - "Stock aanpassing": { - "account_type": "Stock Adjustment" - }, - "Tegenrekening inkoop": {}, - "Toev. Voorz. incour. grondst.": {}, - "Toevoeging garantieverpl.": {}, - "Toevoeging voorz. incour. handelsgoed.": {}, - "Uitbesteed werk": {}, - "Voorz. Incourourant grondst.": {}, - "Voorz.incour. handelsgoed.": {}, - "root_type": "Expense" - }, - "VOORRAAD GRONDSTOFFEN, HULPMATERIALEN EN HANDELSGOEDEREN": { - "Emballage": {}, - "Gereed product 1": {}, - "Gereed product 2": {}, - "Goederen 1": {}, - "Goederen 2": {}, - "Goederen in consignatie": {}, - "Goederen onderweg": {}, - "Grondstoffen 1": {}, - "Grondstoffen 2": {}, - "Halffabrikaten 1": {}, - "Halffabrikaten 2": {}, - "Hulpstoffen 1": {}, - "Hulpstoffen 2": {}, - "Kantoorbenodigdheden": {}, - "Onderhanden werk": {}, - "Verpakkingsmateriaal": {}, - "Zegels": {}, - "root_type": "Asset" } } } \ No newline at end of file diff --git a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py index 81ff6a52db..15c84d462f 100644 --- a/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py +++ b/erpnext/accounts/doctype/accounting_dimension/accounting_dimension.py @@ -271,6 +271,12 @@ def get_dimensions(with_cost_center_and_project=False): as_dict=1, ) + if isinstance(with_cost_center_and_project, str): + if with_cost_center_and_project.lower().strip() == "true": + with_cost_center_and_project = True + else: + with_cost_center_and_project = False + if with_cost_center_and_project: dimension_filters.extend( [ diff --git a/erpnext/accounts/doctype/bank/bank.js b/erpnext/accounts/doctype/bank/bank.js index 35d606ba3a..6667193a54 100644 --- a/erpnext/accounts/doctype/bank/bank.js +++ b/erpnext/accounts/doctype/bank/bank.js @@ -8,9 +8,6 @@ frappe.ui.form.on('Bank', { }, refresh: function(frm) { add_fields_to_mapping_table(frm); - - frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Bank' }; - frm.toggle_display(['address_html','contact_html'], !frm.doc.__islocal); if (frm.doc.__islocal) { diff --git a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js index 0647a5ccf3..d961ead642 100644 --- a/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js +++ b/erpnext/accounts/doctype/bank_reconciliation_tool/bank_reconciliation_tool.js @@ -19,7 +19,7 @@ frappe.ui.form.on("Bank Reconciliation Tool", { onload: function (frm) { // Set default filter dates - today = frappe.datetime.get_today() + let today = frappe.datetime.get_today() frm.doc.bank_statement_from_date = frappe.datetime.add_months(today, -1); frm.doc.bank_statement_to_date = today; frm.trigger('bank_account'); diff --git a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py index 598db642f3..3b5698b118 100644 --- a/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py +++ b/erpnext/accounts/doctype/exchange_rate_revaluation/exchange_rate_revaluation.py @@ -93,6 +93,12 @@ class ExchangeRateRevaluation(Document): return True + def fetch_and_calculate_accounts_data(self): + accounts = self.get_accounts_data() + if accounts: + for acc in accounts: + self.append("accounts", acc) + @frappe.whitelist() def get_accounts_data(self): self.validate_mandatory() @@ -252,8 +258,8 @@ class ExchangeRateRevaluation(Document): new_balance_in_base_currency = 0 new_balance_in_account_currency = 0 - current_exchange_rate = calculate_exchange_rate_using_last_gle( - company, d.account, d.party_type, d.party + current_exchange_rate = ( + calculate_exchange_rate_using_last_gle(company, d.account, d.party_type, d.party) or 0.0 ) gain_loss = new_balance_in_account_currency - ( diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year.js b/erpnext/accounts/doctype/fiscal_year/fiscal_year.js index bc77dac1cd..508b2eaf2a 100644 --- a/erpnext/accounts/doctype/fiscal_year/fiscal_year.js +++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year.js @@ -8,17 +8,6 @@ frappe.ui.form.on('Fiscal Year', { frappe.datetime.add_days(frappe.defaults.get_default("year_end_date"), 1)); } }, - refresh: function (frm) { - if (!frm.doc.__islocal && (frm.doc.name != frappe.sys_defaults.fiscal_year)) { - frm.add_custom_button(__("Set as Default"), () => frm.events.set_as_default(frm)); - frm.set_intro(__("To set this Fiscal Year as Default, click on 'Set as Default'")); - } else { - frm.set_intro(""); - } - }, - set_as_default: function(frm) { - return frm.call('set_as_default'); - }, year_start_date: function(frm) { if (!frm.doc.is_short_year) { let year_end_date = diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year.py b/erpnext/accounts/doctype/fiscal_year/fiscal_year.py index 9d1b99b29b..0dfe569ec9 100644 --- a/erpnext/accounts/doctype/fiscal_year/fiscal_year.py +++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year.py @@ -4,28 +4,12 @@ import frappe from dateutil.relativedelta import relativedelta -from frappe import _, msgprint +from frappe import _ from frappe.model.document import Document from frappe.utils import add_days, add_years, cstr, getdate class FiscalYear(Document): - @frappe.whitelist() - def set_as_default(self): - frappe.db.set_single_value("Global Defaults", "current_fiscal_year", self.name) - global_defaults = frappe.get_doc("Global Defaults") - global_defaults.check_permission("write") - global_defaults.on_update() - - # clear cache - frappe.clear_cache() - - msgprint( - _( - "{0} is now the default Fiscal Year. Please refresh your browser for the change to take effect." - ).format(self.name) - ) - def validate(self): self.validate_dates() self.validate_overlap() @@ -68,13 +52,6 @@ class FiscalYear(Document): frappe.cache().delete_value("fiscal_years") def on_trash(self): - global_defaults = frappe.get_doc("Global Defaults") - if global_defaults.current_fiscal_year == self.name: - frappe.throw( - _( - "You cannot delete Fiscal Year {0}. Fiscal Year {0} is set as default in Global Settings" - ).format(self.name) - ) frappe.cache().delete_value("fiscal_years") def validate_overlap(self): diff --git a/erpnext/accounts/doctype/item_tax_template/item_tax_template.json b/erpnext/accounts/doctype/item_tax_template/item_tax_template.json index b42d712d88..87f0ad1048 100644 --- a/erpnext/accounts/doctype/item_tax_template/item_tax_template.json +++ b/erpnext/accounts/doctype/item_tax_template/item_tax_template.json @@ -35,6 +35,7 @@ { "fieldname": "company", "fieldtype": "Link", + "in_filter": 1, "in_list_view": 1, "label": "Company", "options": "Company", @@ -56,7 +57,7 @@ } ], "links": [], - "modified": "2022-01-18 21:11:23.105589", + "modified": "2023-07-09 18:11:23.105589", "modified_by": "Administrator", "module": "Accounts", "name": "Item Tax Template", @@ -102,4 +103,4 @@ "states": [], "title_field": "title", "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py index 83312dbd22..ea4a2d4b19 100644 --- a/erpnext/accounts/doctype/journal_entry/journal_entry.py +++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py @@ -408,6 +408,15 @@ class JournalEntry(AccountsController): d.idx, d.account ) ) + elif ( + d.party_type + and frappe.db.get_value("Party Type", d.party_type, "account_type") != account_type + ): + frappe.throw( + _("Row {0}: Account {1} and Party Type {2} have different account types").format( + d.idx, d.account, d.party_type + ) + ) def check_credit_limit(self): customers = list( diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py index c85c1aeb3a..79f0419c15 100644 --- a/erpnext/accounts/doctype/payment_entry/payment_entry.py +++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py @@ -8,6 +8,7 @@ from functools import reduce import frappe from frappe import ValidationError, _, qb, scrub, throw from frappe.utils import cint, comma_or, flt, getdate, nowdate +from frappe.utils.data import comma_and, fmt_money import erpnext from erpnext.accounts.doctype.bank_account.bank_account import ( @@ -124,13 +125,16 @@ class PaymentEntry(AccountsController): self.set(self.party_account_field, liability_account) - msg = "Book Advance Payments as Liability option is chosen. Paid From account changed from {0} to {1}.".format( - frappe.bold(self.party_account), - frappe.bold(liability_account), + frappe.msgprint( + _( + "Book Advance Payments as Liability option is chosen. Paid From account changed from {0} to {1}." + ).format( + frappe.bold(self.party_account), + frappe.bold(liability_account), + ), + alert=True, ) - frappe.msgprint(_(msg), alert=True) - def on_cancel(self): self.ignore_linked_doctypes = ( "GL Entry", @@ -230,17 +234,16 @@ class PaymentEntry(AccountsController): # The reference has already been fully paid if not latest: frappe.throw( - _("{0} {1} has already been fully paid.").format(d.reference_doctype, d.reference_name) + _("{0} {1} has already been fully paid.").format(_(d.reference_doctype), d.reference_name) ) # The reference has already been partly paid - elif ( - latest.outstanding_amount < latest.invoice_amount - and d.outstanding_amount != latest.outstanding_amount - ): + elif latest.outstanding_amount < latest.invoice_amount and flt( + d.outstanding_amount, d.precision("outstanding_amount") + ) != flt(latest.outstanding_amount, d.precision("outstanding_amount")): frappe.throw( _( "{0} {1} has already been partly paid. Please use the 'Get Outstanding Invoice' or the 'Get Outstanding Orders' button to get the latest outstanding amounts." - ).format(d.reference_doctype, d.reference_name) + ).format(_(d.reference_doctype), d.reference_name) ) fail_message = _("Row #{0}: Allocated Amount cannot be greater than outstanding amount.") @@ -342,7 +345,7 @@ class PaymentEntry(AccountsController): def validate_party_details(self): if self.party: if not frappe.db.exists(self.party_type, self.party): - frappe.throw(_("Invalid {0}: {1}").format(self.party_type, self.party)) + frappe.throw(_("{0} {1} does not exist").format(_(self.party_type), self.party)) def set_exchange_rate(self, ref_doc=None): self.set_source_exchange_rate(ref_doc) @@ -391,7 +394,9 @@ class PaymentEntry(AccountsController): continue if d.reference_doctype not in valid_reference_doctypes: frappe.throw( - _("Reference Doctype must be one of {0}").format(comma_or(valid_reference_doctypes)) + _("Reference Doctype must be one of {0}").format( + comma_or((_(d) for d in valid_reference_doctypes)) + ) ) elif d.reference_name: @@ -404,7 +409,7 @@ class PaymentEntry(AccountsController): if self.party != ref_doc.get(scrub(self.party_type)): frappe.throw( _("{0} {1} is not associated with {2} {3}").format( - d.reference_doctype, d.reference_name, self.party_type, self.party + _(d.reference_doctype), d.reference_name, _(self.party_type), self.party ) ) else: @@ -426,18 +431,18 @@ class PaymentEntry(AccountsController): ): frappe.throw( _("{0} {1} is associated with {2}, but Party Account is {3}").format( - d.reference_doctype, d.reference_name, ref_party_account, self.party_account + _(d.reference_doctype), d.reference_name, ref_party_account, self.party_account ) ) if ref_doc.doctype == "Purchase Invoice" and ref_doc.get("on_hold"): frappe.throw( - _("{0} {1} is on hold").format(d.reference_doctype, d.reference_name), - title=_("Invalid Invoice"), + _("{0} {1} is on hold").format(_(d.reference_doctype), d.reference_name), + title=_("Invalid Purchase Invoice"), ) if ref_doc.docstatus != 1: - frappe.throw(_("{0} {1} must be submitted").format(d.reference_doctype, d.reference_name)) + frappe.throw(_("{0} {1} must be submitted").format(_(d.reference_doctype), d.reference_name)) def get_valid_reference_doctypes(self): if self.party_type == "Customer": @@ -463,14 +468,13 @@ class PaymentEntry(AccountsController): if outstanding_amount <= 0 and not is_return: no_oustanding_refs.setdefault(d.reference_doctype, []).append(d) - for k, v in no_oustanding_refs.items(): + for reference_doctype, references in no_oustanding_refs.items(): frappe.msgprint( _( - "{} - {} now has {} as it had no outstanding amount left before submitting the Payment Entry." + "References {0} of type {1} had no outstanding amount left before submitting the Payment Entry. Now they have a negative outstanding amount." ).format( - _(k), - frappe.bold(", ".join(d.reference_name for d in v)), - frappe.bold(_("negative outstanding amount")), + frappe.bold(comma_and((d.reference_name for d in references))), + _(reference_doctype), ) + "

" + _("If this is undesirable please cancel the corresponding Payment Entry."), @@ -505,7 +509,7 @@ class PaymentEntry(AccountsController): if not valid: frappe.throw( _("Against Journal Entry {0} does not have any unmatched {1} entry").format( - d.reference_name, dr_or_cr + d.reference_name, _(dr_or_cr) ) ) @@ -572,7 +576,7 @@ class PaymentEntry(AccountsController): if allocated_amount > outstanding: frappe.throw( _("Row #{0}: Cannot allocate more than {1} against payment term {2}").format( - idx, outstanding, key[0] + idx, fmt_money(outstanding), key[0] ) ) @@ -876,7 +880,7 @@ class PaymentEntry(AccountsController): elif paid_amount - additional_charges > total_negative_outstanding: frappe.throw( _("Paid Amount cannot be greater than total negative outstanding amount {0}").format( - total_negative_outstanding + fmt_money(total_negative_outstanding) ), InvalidPaymentEntry, ) @@ -1428,6 +1432,9 @@ def get_outstanding_reference_documents(args, validate=False): if args.get("party_type") == "Member": return + if not args.get("get_outstanding_invoices") and not args.get("get_orders_to_be_billed"): + args["get_outstanding_invoices"] = True + ple = qb.DocType("Payment Ledger Entry") common_filter = [] accounting_dimensions_filter = [] @@ -1546,7 +1553,7 @@ def get_outstanding_reference_documents(args, validate=False): _( "No outstanding {0} found for the {1} {2} which qualify the filters you have specified." ).format( - ref_document_type, _(args.get("party_type")).lower(), frappe.bold(args.get("party")) + _(ref_document_type), _(args.get("party_type")).lower(), frappe.bold(args.get("party")) ) ) @@ -1622,60 +1629,59 @@ def get_orders_to_be_billed( cost_center=None, filters=None, ): + voucher_type = None if party_type == "Customer": voucher_type = "Sales Order" elif party_type == "Supplier": voucher_type = "Purchase Order" - elif party_type == "Employee": - voucher_type = None + + if not voucher_type: + return [] # Add cost center condition - if voucher_type: - doc = frappe.get_doc({"doctype": voucher_type}) - condition = "" - if doc and hasattr(doc, "cost_center") and doc.cost_center: - condition = " and cost_center='%s'" % cost_center + doc = frappe.get_doc({"doctype": voucher_type}) + condition = "" + if doc and hasattr(doc, "cost_center") and doc.cost_center: + condition = " and cost_center='%s'" % cost_center - orders = [] - if voucher_type: - if party_account_currency == company_currency: - grand_total_field = "base_grand_total" - rounded_total_field = "base_rounded_total" - else: - grand_total_field = "grand_total" - rounded_total_field = "rounded_total" + if party_account_currency == company_currency: + grand_total_field = "base_grand_total" + rounded_total_field = "base_rounded_total" + else: + grand_total_field = "grand_total" + rounded_total_field = "rounded_total" - orders = frappe.db.sql( - """ - select - name as voucher_no, - if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) as invoice_amount, - (if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) - advance_paid) as outstanding_amount, - transaction_date as posting_date - from - `tab{voucher_type}` - where - {party_type} = %s - and docstatus = 1 - and company = %s - and ifnull(status, "") != "Closed" - and if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) > advance_paid - and abs(100 - per_billed) > 0.01 - {condition} - order by - transaction_date, name - """.format( - **{ - "rounded_total_field": rounded_total_field, - "grand_total_field": grand_total_field, - "voucher_type": voucher_type, - "party_type": scrub(party_type), - "condition": condition, - } - ), - (party, company), - as_dict=True, - ) + orders = frappe.db.sql( + """ + select + name as voucher_no, + if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) as invoice_amount, + (if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) - advance_paid) as outstanding_amount, + transaction_date as posting_date + from + `tab{voucher_type}` + where + {party_type} = %s + and docstatus = 1 + and company = %s + and ifnull(status, "") != "Closed" + and if({rounded_total_field}, {rounded_total_field}, {grand_total_field}) > advance_paid + and abs(100 - per_billed) > 0.01 + {condition} + order by + transaction_date, name + """.format( + **{ + "rounded_total_field": rounded_total_field, + "grand_total_field": grand_total_field, + "voucher_type": voucher_type, + "party_type": scrub(party_type), + "condition": condition, + } + ), + (party, company), + as_dict=True, + ) order_list = [] for d in orders: @@ -1708,6 +1714,8 @@ def get_negative_outstanding_invoices( cost_center=None, condition=None, ): + if party_type not in ["Customer", "Supplier"]: + return [] voucher_type = "Sales Invoice" if party_type == "Customer" else "Purchase Invoice" account = "debit_to" if voucher_type == "Sales Invoice" else "credit_to" supplier_condition = "" @@ -1758,7 +1766,7 @@ def get_negative_outstanding_invoices( def get_party_details(company, party_type, party, date, cost_center=None): bank_account = "" if not frappe.db.exists(party_type, party): - frappe.throw(_("Invalid {0}: {1}").format(party_type, party)) + frappe.throw(_("{0} {1} does not exist").format(_(party_type), party)) party_account = get_party_account(party_type, party, company) account_currency = get_account_currency(party_account) @@ -1912,7 +1920,7 @@ def get_payment_entry( if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) >= ( 100.0 + over_billing_allowance ): - frappe.throw(_("Can only make payment against unbilled {0}").format(dt)) + frappe.throw(_("Can only make payment against unbilled {0}").format(_(dt))) if not party_type: party_type = set_party_type(dt) diff --git a/erpnext/accounts/doctype/process_deferred_accounting/test_process_deferred_accounting.py b/erpnext/accounts/doctype/process_deferred_accounting/test_process_deferred_accounting.py index 83646c90ba..263621dcf4 100644 --- a/erpnext/accounts/doctype/process_deferred_accounting/test_process_deferred_accounting.py +++ b/erpnext/accounts/doctype/process_deferred_accounting/test_process_deferred_accounting.py @@ -16,8 +16,10 @@ from erpnext.stock.doctype.item.test_item import create_item class TestProcessDeferredAccounting(unittest.TestCase): def test_creation_of_ledger_entry_on_submit(self): """test creation of gl entries on submission of document""" + change_acc_settings(acc_frozen_upto="2023-05-31", book_deferred_entries_based_on="Months") + deferred_account = create_account( - account_name="Deferred Revenue", + account_name="Deferred Revenue for Accounts Frozen", parent_account="Current Liabilities - _TC", company="_Test Company", ) @@ -29,21 +31,21 @@ class TestProcessDeferredAccounting(unittest.TestCase): item.save() si = create_sales_invoice( - item=item.name, update_stock=0, posting_date="2019-01-10", do_not_submit=True + item=item.name, rate=3000, update_stock=0, posting_date="2023-07-01", do_not_submit=True ) si.items[0].enable_deferred_revenue = 1 - si.items[0].service_start_date = "2019-01-10" - si.items[0].service_end_date = "2019-03-15" + si.items[0].service_start_date = "2023-05-01" + si.items[0].service_end_date = "2023-07-31" si.items[0].deferred_revenue_account = deferred_account si.save() si.submit() - process_deferred_accounting = frappe.get_doc( + process_deferred_accounting = doc = frappe.get_doc( dict( doctype="Process Deferred Accounting", - posting_date="2019-01-01", - start_date="2019-01-01", - end_date="2019-01-31", + posting_date="2023-07-01", + start_date="2023-05-01", + end_date="2023-06-30", type="Income", ) ) @@ -52,11 +54,16 @@ class TestProcessDeferredAccounting(unittest.TestCase): process_deferred_accounting.submit() expected_gle = [ - [deferred_account, 33.85, 0.0, "2019-01-31"], - ["Sales - _TC", 0.0, 33.85, "2019-01-31"], + ["Debtors - _TC", 3000, 0.0, "2023-07-01"], + [deferred_account, 0.0, 3000, "2023-07-01"], + ["Sales - _TC", 0.0, 1000, "2023-06-30"], + [deferred_account, 1000, 0.0, "2023-06-30"], + ["Sales - _TC", 0.0, 1000, "2023-06-30"], + [deferred_account, 1000, 0.0, "2023-06-30"], ] - check_gl_entries(self, si.name, expected_gle, "2019-01-31") + check_gl_entries(self, si.name, expected_gle, "2023-07-01") + change_acc_settings() def test_pda_submission_and_cancellation(self): pda = frappe.get_doc( @@ -70,3 +77,10 @@ class TestProcessDeferredAccounting(unittest.TestCase): ) pda.submit() pda.cancel() + + +def change_acc_settings(acc_frozen_upto="", book_deferred_entries_based_on="Days"): + acc_settings = frappe.get_doc("Accounts Settings", "Accounts Settings") + acc_settings.acc_frozen_upto = acc_frozen_upto + acc_settings.book_deferred_entries_based_on = book_deferred_entries_based_on + acc_settings.save() diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json index e247e80253..d8759e95b8 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json @@ -549,6 +549,7 @@ "depends_on": "update_stock", "fieldname": "rejected_warehouse", "fieldtype": "Link", + "ignore_user_permissions": 1, "label": "Rejected Warehouse", "no_copy": 1, "options": "Warehouse", @@ -1576,7 +1577,7 @@ "idx": 204, "is_submittable": 1, "links": [], - "modified": "2023-06-03 16:21:54.637245", + "modified": "2023-07-04 17:22:59.145031", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice", diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json index c5187a2f46..4afc4512ff 100644 --- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json +++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json @@ -423,6 +423,7 @@ { "fieldname": "rejected_warehouse", "fieldtype": "Link", + "ignore_user_permissions": 1, "label": "Rejected Warehouse", "options": "Warehouse" }, @@ -904,7 +905,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2023-07-02 18:39:41.495723", + "modified": "2023-07-04 17:22:21.501152", "modified_by": "Administrator", "module": "Accounts", "name": "Purchase Invoice Item", diff --git a/erpnext/accounts/doctype/shareholder/shareholder.js b/erpnext/accounts/doctype/shareholder/shareholder.js index c6f101e7f3..544d417a0e 100644 --- a/erpnext/accounts/doctype/shareholder/shareholder.js +++ b/erpnext/accounts/doctype/shareholder/shareholder.js @@ -3,8 +3,6 @@ frappe.ui.form.on('Shareholder', { refresh: function(frm) { - frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Shareholder' }; - frm.toggle_display(['contact_html'], !frm.doc.__islocal); if (frm.doc.__islocal) { diff --git a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py index e3127b346f..7e0bdea80c 100644 --- a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py +++ b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py @@ -73,7 +73,7 @@ def get_entries(filters): return sorted( entries, - key=lambda k: k[2] or getdate(nowdate()), + key=lambda k: k[2].strftime("%H%M%S") or getdate(nowdate()), ) diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js index dd965a9813..d58fd95a84 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js @@ -49,7 +49,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "label": __("Start Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "reqd": 1, on_change: () => { frappe.model.with_doc("Fiscal Year", frappe.query_report.get_filter_value('from_fiscal_year'), function(r) { @@ -65,7 +65,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "label": __("End Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "reqd": 1, on_change: () => { frappe.model.with_doc("Fiscal Year", frappe.query_report.get_filter_value('to_fiscal_year'), function(r) { @@ -139,7 +139,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { return value; }, onload: function() { - let fiscal_year = frappe.defaults.get_user_default("fiscal_year") + let fiscal_year = erpnext.utils.get_fiscal_year(frappe.datetime.get_today()); frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) { var fy = frappe.model.get_doc("Fiscal Year", fiscal_year); diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py index a6447549e6..6e39ee9944 100644 --- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py +++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.py @@ -650,7 +650,7 @@ def set_gl_entries_by_account( if filters and filters.get("presentation_currency") != d.default_currency: currency_info["company"] = d.name currency_info["company_currency"] = d.default_currency - convert_to_presentation_currency(gl_entries, currency_info, filters.get("company")) + convert_to_presentation_currency(gl_entries, currency_info) for entry in gl_entries: if entry.account_number: diff --git a/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.js b/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.js index 0056b9e8f5..96e0c844ca 100644 --- a/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.js +++ b/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.js @@ -48,7 +48,7 @@ function get_filters() { "label": __("Start Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "reqd": 1 }, { @@ -56,7 +56,7 @@ function get_filters() { "label": __("End Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "reqd": 1 }, { @@ -100,7 +100,7 @@ frappe.query_reports["Deferred Revenue and Expense"] = { return default_formatter(value, row, column, data); }, onload: function(report){ - let fiscal_year = frappe.defaults.get_user_default("fiscal_year"); + let fiscal_year = erpnext.utils.get_fiscal_year(frappe.datetime.get_today()); frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) { var fy = frappe.model.get_doc("Fiscal Year", fiscal_year); diff --git a/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py b/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py index 3e11643776..cad5325c6e 100644 --- a/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py +++ b/erpnext/accounts/report/deferred_revenue_and_expense/deferred_revenue_and_expense.py @@ -4,9 +4,10 @@ import frappe from frappe import _, qb from frappe.query_builder import Column, functions -from frappe.utils import add_days, date_diff, flt, get_first_day, get_last_day, rounded +from frappe.utils import add_days, date_diff, flt, get_first_day, get_last_day, getdate, rounded from erpnext.accounts.report.financial_statements import get_period_list +from erpnext.accounts.utils import get_fiscal_year class Deferred_Item(object): @@ -226,7 +227,7 @@ class Deferred_Revenue_and_Expense_Report(object): # If no filters are provided, get user defaults if not filters: - fiscal_year = frappe.get_doc("Fiscal Year", frappe.defaults.get_user_default("fiscal_year")) + fiscal_year = frappe.get_doc("Fiscal Year", get_fiscal_year(date=getdate())) self.filters = frappe._dict( { "company": frappe.defaults.get_user_default("Company"), diff --git a/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py b/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py index 023ff225ee..c84b843f1f 100644 --- a/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py +++ b/erpnext/accounts/report/deferred_revenue_and_expense/test_deferred_revenue_and_expense.py @@ -10,6 +10,7 @@ from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sal from erpnext.accounts.report.deferred_revenue_and_expense.deferred_revenue_and_expense import ( Deferred_Revenue_and_Expense_Report, ) +from erpnext.accounts.utils import get_fiscal_year from erpnext.buying.doctype.supplier.test_supplier import create_supplier from erpnext.stock.doctype.item.test_item import create_item @@ -116,7 +117,7 @@ class TestDeferredRevenueAndExpense(unittest.TestCase): pda.submit() # execute report - fiscal_year = frappe.get_doc("Fiscal Year", frappe.defaults.get_user_default("fiscal_year")) + fiscal_year = frappe.get_doc("Fiscal Year", get_fiscal_year(date="2021-05-01")) self.filters = frappe._dict( { "company": frappe.defaults.get_user_default("Company"), @@ -209,7 +210,7 @@ class TestDeferredRevenueAndExpense(unittest.TestCase): pda.submit() # execute report - fiscal_year = frappe.get_doc("Fiscal Year", frappe.defaults.get_user_default("fiscal_year")) + fiscal_year = frappe.get_doc("Fiscal Year", get_fiscal_year(date="2021-05-01")) self.filters = frappe._dict( { "company": frappe.defaults.get_user_default("Company"), @@ -297,7 +298,7 @@ class TestDeferredRevenueAndExpense(unittest.TestCase): pda.submit() # execute report - fiscal_year = frappe.get_doc("Fiscal Year", frappe.defaults.get_user_default("fiscal_year")) + fiscal_year = frappe.get_doc("Fiscal Year", get_fiscal_year(date="2021-05-01")) self.filters = frappe._dict( { "company": frappe.defaults.get_user_default("Company"), diff --git a/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.js b/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.js index ea05a35b25..9d416db4fd 100644 --- a/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.js +++ b/erpnext/accounts/report/dimension_wise_accounts_balance_report/dimension_wise_accounts_balance_report.js @@ -18,7 +18,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "label": __("Fiscal Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "reqd": 1, "on_change": function(query_report) { var fiscal_year = query_report.get_values().fiscal_year; diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py index 8a47e1c011..db9609debe 100644 --- a/erpnext/accounts/report/financial_statements.py +++ b/erpnext/accounts/report/financial_statements.py @@ -416,6 +416,7 @@ def set_gl_entries_by_account( filters, gl_entries_by_account, ignore_closing_entries=False, + ignore_opening_entries=False, ): """Returns a dict like { "account": [gl entries], ... }""" gl_entries = [] @@ -426,7 +427,6 @@ def set_gl_entries_by_account( pluck="name", ) - ignore_opening_entries = False if accounts_list: # For balance sheet if not from_date: @@ -462,7 +462,7 @@ def set_gl_entries_by_account( ) if filters and filters.get("presentation_currency"): - convert_to_presentation_currency(gl_entries, get_currency(filters), filters.get("company")) + convert_to_presentation_currency(gl_entries, get_currency(filters)) for entry in gl_entries: gl_entries_by_account.setdefault(entry.account, []).append(entry) diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py index d47e3da313..d7af167e38 100644 --- a/erpnext/accounts/report/general_ledger/general_ledger.py +++ b/erpnext/accounts/report/general_ledger/general_ledger.py @@ -204,7 +204,7 @@ def get_gl_entries(filters, accounting_dimensions): ) if filters.get("presentation_currency"): - return convert_to_presentation_currency(gl_entries, currency_map, filters.get("company")) + return convert_to_presentation_currency(gl_entries, currency_map) else: return gl_entries diff --git a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.js b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.js index 8dc5ab36dd..92cf36ebc5 100644 --- a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.js +++ b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.js @@ -12,14 +12,6 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { erpnext.financial_statements); frappe.query_reports["Gross and Net Profit Report"]["filters"].push( - { - "fieldname": "project", - "label": __("Project"), - "fieldtype": "MultiSelectList", - get_data: function(txt) { - return frappe.db.get_link_options('Project', txt); - } - }, { "fieldname": "accumulated_values", "label": __("Accumulated Values"), diff --git a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py index 6fdb2f337c..050e6bc5d2 100644 --- a/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py +++ b/erpnext/accounts/report/item_wise_purchase_register/item_wise_purchase_register.py @@ -15,20 +15,21 @@ from erpnext.accounts.report.item_wise_sales_register.item_wise_sales_register i get_group_by_conditions, get_tax_accounts, ) +from erpnext.accounts.report.utils import get_query_columns, get_values_for_columns def execute(filters=None): return _execute(filters) -def _execute(filters=None, additional_table_columns=None, additional_query_columns=None): +def _execute(filters=None, additional_table_columns=None): if not filters: filters = {} columns = get_columns(additional_table_columns, filters) company_currency = erpnext.get_company_currency(filters.company) - item_list = get_items(filters, additional_query_columns) + item_list = get_items(filters, get_query_columns(additional_table_columns)) aii_account_map = get_aii_accounts() if item_list: itemised_tax, tax_columns = get_tax_accounts( @@ -79,28 +80,20 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum "posting_date": d.posting_date, "supplier": d.supplier, "supplier_name": d.supplier_name, + **get_values_for_columns(additional_table_columns, d), + "credit_to": d.credit_to, + "mode_of_payment": d.mode_of_payment, + "project": d.project, + "company": d.company, + "purchase_order": d.purchase_order, + "purchase_receipt": purchase_receipt, + "expense_account": expense_account, + "stock_qty": d.stock_qty, + "stock_uom": d.stock_uom, + "rate": d.base_net_amount / d.stock_qty if d.stock_qty else d.base_net_amount, + "amount": d.base_net_amount, } - if additional_query_columns: - for col in additional_query_columns: - row.update({col: d.get(col)}) - - row.update( - { - "credit_to": d.credit_to, - "mode_of_payment": d.mode_of_payment, - "project": d.project, - "company": d.company, - "purchase_order": d.purchase_order, - "purchase_receipt": purchase_receipt, - "expense_account": expense_account, - "stock_qty": d.stock_qty, - "stock_uom": d.stock_uom, - "rate": d.base_net_amount / d.stock_qty if d.stock_qty else d.base_net_amount, - "amount": d.base_net_amount, - } - ) - total_tax = 0 for tax in tax_columns: item_tax = itemised_tax.get(d.name, {}).get(tax, {}) @@ -317,11 +310,6 @@ def get_conditions(filters): def get_items(filters, additional_query_columns): conditions = get_conditions(filters) - if additional_query_columns: - additional_query_columns = ", " + ", ".join(additional_query_columns) - else: - additional_query_columns = "" - return frappe.db.sql( """ select @@ -340,11 +328,10 @@ def get_items(filters, additional_query_columns): from `tabPurchase Invoice`, `tabPurchase Invoice Item`, `tabItem` where `tabPurchase Invoice`.name = `tabPurchase Invoice Item`.`parent` and `tabItem`.name = `tabPurchase Invoice Item`.`item_code` and - `tabPurchase Invoice`.docstatus = 1 %s + `tabPurchase Invoice`.docstatus = 1 {1} """.format( - additional_query_columns - ) - % (conditions), + additional_query_columns, conditions + ), filters, as_dict=1, ) diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py index bd7d02e043..4d24dd9076 100644 --- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py +++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py @@ -9,6 +9,7 @@ from frappe.utils import cstr, flt from frappe.utils.xlsxutils import handle_html from erpnext.accounts.report.sales_register.sales_register import get_mode_of_payments +from erpnext.accounts.report.utils import get_query_columns, get_values_for_columns from erpnext.selling.report.item_wise_sales_history.item_wise_sales_history import ( get_customer_details, ) @@ -18,19 +19,14 @@ def execute(filters=None): return _execute(filters) -def _execute( - filters=None, - additional_table_columns=None, - additional_query_columns=None, - additional_conditions=None, -): +def _execute(filters=None, additional_table_columns=None, additional_conditions=None): if not filters: filters = {} columns = get_columns(additional_table_columns, filters) company_currency = frappe.get_cached_value("Company", filters.get("company"), "default_currency") - item_list = get_items(filters, additional_query_columns, additional_conditions) + item_list = get_items(filters, get_query_columns(additional_table_columns), additional_conditions) if item_list: itemised_tax, tax_columns = get_tax_accounts(item_list, columns, company_currency) @@ -79,30 +75,22 @@ def _execute( "customer": d.customer, "customer_name": customer_record.customer_name, "customer_group": customer_record.customer_group, + **get_values_for_columns(additional_table_columns, d), + "debit_to": d.debit_to, + "mode_of_payment": ", ".join(mode_of_payments.get(d.parent, [])), + "territory": d.territory, + "project": d.project, + "company": d.company, + "sales_order": d.sales_order, + "delivery_note": d.delivery_note, + "income_account": d.unrealized_profit_loss_account + if d.is_internal_customer == 1 + else d.income_account, + "cost_center": d.cost_center, + "stock_qty": d.stock_qty, + "stock_uom": d.stock_uom, } - if additional_query_columns: - for col in additional_query_columns: - row.update({col: d.get(col)}) - - row.update( - { - "debit_to": d.debit_to, - "mode_of_payment": ", ".join(mode_of_payments.get(d.parent, [])), - "territory": d.territory, - "project": d.project, - "company": d.company, - "sales_order": d.sales_order, - "delivery_note": d.delivery_note, - "income_account": d.unrealized_profit_loss_account - if d.is_internal_customer == 1 - else d.income_account, - "cost_center": d.cost_center, - "stock_qty": d.stock_qty, - "stock_uom": d.stock_uom, - } - ) - if d.stock_uom != d.uom and d.stock_qty: row.update({"rate": (d.base_net_rate * d.qty) / d.stock_qty, "amount": d.base_net_amount}) else: @@ -394,11 +382,6 @@ def get_group_by_conditions(filters, doctype): def get_items(filters, additional_query_columns, additional_conditions=None): conditions = get_conditions(filters, additional_conditions) - if additional_query_columns: - additional_query_columns = ", " + ", ".join(additional_query_columns) - else: - additional_query_columns = "" - return frappe.db.sql( """ select @@ -424,7 +407,7 @@ def get_items(filters, additional_query_columns, additional_conditions=None): `tabItem`.name = `tabSales Invoice Item`.`item_code` and `tabSales Invoice`.docstatus = 1 {1} """.format( - additional_query_columns or "", conditions + additional_query_columns, conditions ), filters, as_dict=1, diff --git a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js index 298d83894c..e794f270c2 100644 --- a/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js +++ b/erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.js @@ -9,16 +9,6 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { erpnext.utils.add_dimensions('Profit and Loss Statement', 10); frappe.query_reports["Profit and Loss Statement"]["filters"].push( - { - "fieldname": "project", - "label": __("Project"), - "fieldtype": "MultiSelectList", - get_data: function(txt) { - return frappe.db.get_link_options('Project', txt, { - company: frappe.query_report.get_filter_value("company") - }); - }, - }, { "fieldname": "include_default_book_entries", "label": __("Include Default Book Entries"), diff --git a/erpnext/accounts/report/profitability_analysis/profitability_analysis.js b/erpnext/accounts/report/profitability_analysis/profitability_analysis.js index 889ede5a82..6caebd34a2 100644 --- a/erpnext/accounts/report/profitability_analysis/profitability_analysis.js +++ b/erpnext/accounts/report/profitability_analysis/profitability_analysis.js @@ -25,7 +25,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "label": __("Fiscal Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "reqd": 1, "on_change": function(query_report) { var fiscal_year = query_report.get_values().fiscal_year; diff --git a/erpnext/accounts/report/purchase_register/purchase_register.py b/erpnext/accounts/report/purchase_register/purchase_register.py index a05d581207..69827aca69 100644 --- a/erpnext/accounts/report/purchase_register/purchase_register.py +++ b/erpnext/accounts/report/purchase_register/purchase_register.py @@ -10,17 +10,18 @@ from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( get_accounting_dimensions, get_dimension_with_children, ) +from erpnext.accounts.report.utils import get_query_columns, get_values_for_columns def execute(filters=None): return _execute(filters) -def _execute(filters=None, additional_table_columns=None, additional_query_columns=None): +def _execute(filters=None, additional_table_columns=None): if not filters: filters = {} - invoice_list = get_invoices(filters, additional_query_columns) + invoice_list = get_invoices(filters, get_query_columns(additional_table_columns)) columns, expense_accounts, tax_accounts, unrealized_profit_loss_accounts = get_columns( invoice_list, additional_table_columns ) @@ -47,13 +48,12 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum purchase_receipt = list(set(invoice_po_pr_map.get(inv.name, {}).get("purchase_receipt", []))) project = list(set(invoice_po_pr_map.get(inv.name, {}).get("project", []))) - row = [inv.name, inv.posting_date, inv.supplier, inv.supplier_name] - - if additional_query_columns: - for col in additional_query_columns: - row.append(inv.get(col)) - - row += [ + row = [ + inv.name, + inv.posting_date, + inv.supplier, + inv.supplier_name, + *get_values_for_columns(additional_table_columns, inv).values(), supplier_details.get(inv.supplier), # supplier_group inv.tax_id, inv.credit_to, @@ -244,9 +244,6 @@ def get_conditions(filters): def get_invoices(filters, additional_query_columns): - if additional_query_columns: - additional_query_columns = ", " + ", ".join(additional_query_columns) - conditions = get_conditions(filters) return frappe.db.sql( """ @@ -255,11 +252,10 @@ def get_invoices(filters, additional_query_columns): remarks, base_net_total, base_grand_total, outstanding_amount, mode_of_payment {0} from `tabPurchase Invoice` - where docstatus = 1 %s + where docstatus = 1 {1} order by posting_date desc, name desc""".format( - additional_query_columns or "" - ) - % conditions, + additional_query_columns, conditions + ), filters, as_dict=1, ) diff --git a/erpnext/accounts/report/sales_register/sales_register.py b/erpnext/accounts/report/sales_register/sales_register.py index b333901d7b..291c7d976e 100644 --- a/erpnext/accounts/report/sales_register/sales_register.py +++ b/erpnext/accounts/report/sales_register/sales_register.py @@ -11,17 +11,18 @@ from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import ( get_accounting_dimensions, get_dimension_with_children, ) +from erpnext.accounts.report.utils import get_query_columns, get_values_for_columns def execute(filters=None): return _execute(filters) -def _execute(filters, additional_table_columns=None, additional_query_columns=None): +def _execute(filters, additional_table_columns=None): if not filters: filters = frappe._dict({}) - invoice_list = get_invoices(filters, additional_query_columns) + invoice_list = get_invoices(filters, get_query_columns(additional_table_columns)) columns, income_accounts, tax_accounts, unrealized_profit_loss_accounts = get_columns( invoice_list, additional_table_columns ) @@ -54,30 +55,22 @@ def _execute(filters, additional_table_columns=None, additional_query_columns=No "posting_date": inv.posting_date, "customer": inv.customer, "customer_name": inv.customer_name, + **get_values_for_columns(additional_table_columns, inv), + "customer_group": inv.get("customer_group"), + "territory": inv.get("territory"), + "tax_id": inv.get("tax_id"), + "receivable_account": inv.debit_to, + "mode_of_payment": ", ".join(mode_of_payments.get(inv.name, [])), + "project": inv.project, + "owner": inv.owner, + "remarks": inv.remarks, + "sales_order": ", ".join(sales_order), + "delivery_note": ", ".join(delivery_note), + "cost_center": ", ".join(cost_center), + "warehouse": ", ".join(warehouse), + "currency": company_currency, } - if additional_query_columns: - for col in additional_query_columns: - row.update({col: inv.get(col)}) - - row.update( - { - "customer_group": inv.get("customer_group"), - "territory": inv.get("territory"), - "tax_id": inv.get("tax_id"), - "receivable_account": inv.debit_to, - "mode_of_payment": ", ".join(mode_of_payments.get(inv.name, [])), - "project": inv.project, - "owner": inv.owner, - "remarks": inv.remarks, - "sales_order": ", ".join(sales_order), - "delivery_note": ", ".join(delivery_note), - "cost_center": ", ".join(cost_center), - "warehouse": ", ".join(warehouse), - "currency": company_currency, - } - ) - # map income values base_net_total = 0 for income_acc in income_accounts: @@ -402,9 +395,6 @@ def get_conditions(filters): def get_invoices(filters, additional_query_columns): - if additional_query_columns: - additional_query_columns = ", " + ", ".join(additional_query_columns) - conditions = get_conditions(filters) return frappe.db.sql( """ @@ -413,10 +403,10 @@ def get_invoices(filters, additional_query_columns): base_net_total, base_grand_total, base_rounded_total, outstanding_amount, is_internal_customer, represents_company, company {0} from `tabSales Invoice` - where docstatus = 1 %s order by posting_date desc, name desc""".format( - additional_query_columns or "" - ) - % conditions, + where docstatus = 1 {1} + order by posting_date desc, name desc""".format( + additional_query_columns, conditions + ), filters, as_dict=1, ) diff --git a/erpnext/accounts/report/share_ledger/share_ledger.py b/erpnext/accounts/report/share_ledger/share_ledger.py index d6c3bd059f..629528e5cc 100644 --- a/erpnext/accounts/report/share_ledger/share_ledger.py +++ b/erpnext/accounts/report/share_ledger/share_ledger.py @@ -67,8 +67,9 @@ def get_all_transfers(date, shareholder): # condition = 'AND company = %(company)s ' return frappe.db.sql( """SELECT * FROM `tabShare Transfer` - WHERE (DATE(date) <= %(date)s AND from_shareholder = %(shareholder)s {condition}) - OR (DATE(date) <= %(date)s AND to_shareholder = %(shareholder)s {condition}) + WHERE ((DATE(date) <= %(date)s AND from_shareholder = %(shareholder)s {condition}) + OR (DATE(date) <= %(date)s AND to_shareholder = %(shareholder)s {condition})) + AND docstatus = 1 ORDER BY date""".format( condition=condition ), diff --git a/erpnext/accounts/report/trial_balance/trial_balance.js b/erpnext/accounts/report/trial_balance/trial_balance.js index 078b06519f..e45c3adcb6 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.js +++ b/erpnext/accounts/report/trial_balance/trial_balance.js @@ -17,7 +17,7 @@ frappe.require("assets/erpnext/js/financial_statements.js", function() { "label": __("Fiscal Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "reqd": 1, "on_change": function(query_report) { var fiscal_year = query_report.get_values().fiscal_year; diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py index 22bebb7d19..5176c31be7 100644 --- a/erpnext/accounts/report/trial_balance/trial_balance.py +++ b/erpnext/accounts/report/trial_balance/trial_balance.py @@ -17,6 +17,7 @@ from erpnext.accounts.report.financial_statements import ( filter_out_zero_value_rows, set_gl_entries_by_account, ) +from erpnext.accounts.report.utils import convert_to_presentation_currency, get_currency value_fields = ( "opening_debit", @@ -116,6 +117,7 @@ def get_data(filters): filters, gl_entries_by_account, ignore_closing_entries=not flt(filters.with_period_closing_entry), + ignore_opening_entries=True, ) calculate_values(accounts, gl_entries_by_account, opening_balances) @@ -158,6 +160,8 @@ def get_rootwise_opening_balances(filters, report_type): accounting_dimensions, period_closing_voucher=last_period_closing_voucher[0].name, ) + + # Report getting generate from the mid of a fiscal year if getdate(last_period_closing_voucher[0].posting_date) < getdate( add_days(filters.from_date, -1) ): @@ -178,8 +182,8 @@ def get_rootwise_opening_balances(filters, report_type): "opening_credit": 0.0, }, ) - opening[d.account]["opening_debit"] += flt(d.opening_debit) - opening[d.account]["opening_credit"] += flt(d.opening_credit) + opening[d.account]["opening_debit"] += flt(d.debit) + opening[d.account]["opening_credit"] += flt(d.credit) return opening @@ -194,8 +198,11 @@ def get_opening_balance( frappe.qb.from_(closing_balance) .select( closing_balance.account, - Sum(closing_balance.debit).as_("opening_debit"), - Sum(closing_balance.credit).as_("opening_credit"), + closing_balance.account_currency, + Sum(closing_balance.debit).as_("debit"), + Sum(closing_balance.credit).as_("credit"), + Sum(closing_balance.debit_in_account_currency).as_("debit_in_account_currency"), + Sum(closing_balance.credit_in_account_currency).as_("credit_in_account_currency"), ) .where( (closing_balance.company == filters.company) @@ -216,7 +223,10 @@ def get_opening_balance( if start_date: opening_balance = opening_balance.where(closing_balance.posting_date >= start_date) opening_balance = opening_balance.where(closing_balance.is_opening == "No") - opening_balance = opening_balance.where(closing_balance.posting_date < filters.from_date) + else: + opening_balance = opening_balance.where( + (closing_balance.posting_date < filters.from_date) | (closing_balance.is_opening == "Yes") + ) if ( not filters.show_unclosed_fy_pl_balances @@ -282,6 +292,9 @@ def get_opening_balance( gle = opening_balance.run(as_dict=1) + if filters and filters.get("presentation_currency"): + convert_to_presentation_currency(gle, get_currency(filters)) + return gle diff --git a/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js b/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js index 0e93035a35..0f7578cdc1 100644 --- a/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js +++ b/erpnext/accounts/report/trial_balance_for_party/trial_balance_for_party.js @@ -16,7 +16,7 @@ frappe.query_reports["Trial Balance for Party"] = { "label": __("Fiscal Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "reqd": 1, "on_change": function(query_report) { var fiscal_year = query_report.get_values().fiscal_year; diff --git a/erpnext/accounts/report/utils.py b/erpnext/accounts/report/utils.py index 97cc1c4a13..7ea1fac105 100644 --- a/erpnext/accounts/report/utils.py +++ b/erpnext/accounts/report/utils.py @@ -1,5 +1,5 @@ import frappe -from frappe.utils import flt, formatdate, get_datetime_str +from frappe.utils import flt, formatdate, get_datetime_str, get_table_name from erpnext import get_company_currency, get_default_company from erpnext.accounts.doctype.fiscal_year.fiscal_year import get_from_and_to_date @@ -78,7 +78,7 @@ def get_rate_as_at(date, from_currency, to_currency): return rate -def convert_to_presentation_currency(gl_entries, currency_info, company): +def convert_to_presentation_currency(gl_entries, currency_info): """ Take a list of GL Entries and change the 'debit' and 'credit' values to currencies in `currency_info`. @@ -93,7 +93,6 @@ def convert_to_presentation_currency(gl_entries, currency_info, company): account_currencies = list(set(entry["account_currency"] for entry in gl_entries)) for entry in gl_entries: - account = entry["account"] debit = flt(entry["debit"]) credit = flt(entry["credit"]) debit_in_account_currency = flt(entry["debit_in_account_currency"]) @@ -151,3 +150,32 @@ def get_invoiced_item_gross_margin( result = sum(d.gross_profit for d in result) return result + + +def get_query_columns(report_columns): + if not report_columns: + return "" + + columns = [] + for column in report_columns: + fieldname = column["fieldname"] + + if doctype := column.get("_doctype"): + columns.append(f"`{get_table_name(doctype)}`.`{fieldname}`") + else: + columns.append(fieldname) + + return ", " + ", ".join(columns) + + +def get_values_for_columns(report_columns, report_row): + values = {} + + if not report_columns: + return values + + for column in report_columns: + fieldname = column["fieldname"] + values[fieldname] = report_row.get(fieldname) + + return values diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 9000b0d32e..4b54483bc0 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -850,7 +850,7 @@ def get_held_invoices(party_type, party): if party_type == "Supplier": held_invoices = frappe.db.sql( - "select name from `tabPurchase Invoice` where release_date IS NOT NULL and release_date > CURDATE()", + "select name from `tabPurchase Invoice` where on_hold = 1 and release_date IS NOT NULL and release_date > CURDATE()", as_dict=1, ) held_invoices = set(d["name"] for d in held_invoices) @@ -1110,6 +1110,11 @@ def get_autoname_with_number(number_value, doc_title, company): return " - ".join(parts) +def parse_naming_series_variable(doc, variable): + if variable == "FY": + return get_fiscal_year(date=doc.get("posting_date"), company=doc.get("company"))[0] + + @frappe.whitelist() def get_coa(doctype, parent, is_root, chart=None): from erpnext.accounts.doctype.account.chart_of_accounts.chart_of_accounts import ( @@ -1408,6 +1413,50 @@ def check_and_delete_linked_reports(report): frappe.delete_doc("Desktop Icon", icon) +def create_err_and_its_journals(companies: list = None) -> None: + if companies: + for company in companies: + err = frappe.new_doc("Exchange Rate Revaluation") + err.company = company.name + err.posting_date = nowdate() + err.rounding_loss_allowance = 0.0 + + err.fetch_and_calculate_accounts_data() + if err.accounts: + err.save().submit() + response = err.make_jv_entries() + + if company.submit_err_jv: + jv = response.get("revaluation_jv", None) + jv and frappe.get_doc("Journal Entry", jv).submit() + jv = response.get("zero_balance_jv", None) + jv and frappe.get_doc("Journal Entry", jv).submit() + + +def auto_create_exchange_rate_revaluation_daily() -> None: + """ + Executed by background job + """ + companies = frappe.db.get_all( + "Company", + filters={"auto_exchange_rate_revaluation": 1, "auto_err_frequency": "Daily"}, + fields=["name", "submit_err_jv"], + ) + create_err_and_its_journals(companies) + + +def auto_create_exchange_rate_revaluation_weekly() -> None: + """ + Executed by background job + """ + companies = frappe.db.get_all( + "Company", + filters={"auto_exchange_rate_revaluation": 1, "auto_err_frequency": "Weekly"}, + fields=["name", "submit_err_jv"], + ) + create_err_and_its_journals(companies) + + def get_payment_ledger_entries(gl_entries, cancel=0): ple_map = [] if gl_entries: diff --git a/erpnext/accounts/workspace/accounting/accounting.json b/erpnext/accounts/workspace/accounting/accounting.json index 2260bcad76..c27ede29d1 100644 --- a/erpnext/accounts/workspace/accounting/accounting.json +++ b/erpnext/accounts/workspace/accounting/accounting.json @@ -5,7 +5,7 @@ "label": "Profit and Loss" } ], - "content": "[{\"id\":\"MmUf9abwxg\",\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Accounts\",\"col\":12}},{\"id\":\"i0EtSjDAXq\",\"type\":\"chart\",\"data\":{\"chart_name\":\"Profit and Loss\",\"col\":12}},{\"id\":\"X78jcbq1u3\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"vikWSkNm6_\",\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"id\":\"pMywM0nhlj\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Chart of Accounts\",\"col\":3}},{\"id\":\"_pRdD6kqUG\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Invoice\",\"col\":3}},{\"id\":\"G984SgVRJN\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Purchase Invoice\",\"col\":3}},{\"id\":\"1ArNvt9qhz\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Journal Entry\",\"col\":3}},{\"id\":\"F9f4I1viNr\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Payment Entry\",\"col\":3}},{\"id\":\"4IBBOIxfqW\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Accounts Receivable\",\"col\":3}},{\"id\":\"El2anpPaFY\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"General Ledger\",\"col\":3}},{\"id\":\"1nwcM9upJo\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Trial Balance\",\"col\":3}},{\"id\":\"OF9WOi1Ppc\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"id\":\"B7-uxs8tkU\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"tHb3yxthkR\",\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"col\":12}},{\"id\":\"DnNtsmxpty\",\"type\":\"card\",\"data\":{\"card_name\":\"Accounting Masters\",\"col\":4}},{\"id\":\"nKKr6fjgjb\",\"type\":\"card\",\"data\":{\"card_name\":\"General Ledger\",\"col\":4}},{\"id\":\"xOHTyD8b5l\",\"type\":\"card\",\"data\":{\"card_name\":\"Accounts Receivable\",\"col\":4}},{\"id\":\"_Cb7C8XdJJ\",\"type\":\"card\",\"data\":{\"card_name\":\"Accounts Payable\",\"col\":4}},{\"id\":\"p7NY6MHe2Y\",\"type\":\"card\",\"data\":{\"card_name\":\"Financial Statements\",\"col\":4}},{\"id\":\"KlqilF5R_V\",\"type\":\"card\",\"data\":{\"card_name\":\"Taxes\",\"col\":4}},{\"id\":\"jTUy8LB0uw\",\"type\":\"card\",\"data\":{\"card_name\":\"Cost Center and Budgeting\",\"col\":4}},{\"id\":\"Wn2lhs7WLn\",\"type\":\"card\",\"data\":{\"card_name\":\"Multi Currency\",\"col\":4}},{\"id\":\"PAQMqqNkBM\",\"type\":\"card\",\"data\":{\"card_name\":\"Bank Statement\",\"col\":4}},{\"id\":\"Q_hBCnSeJY\",\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"id\":\"3AK1Zf0oew\",\"type\":\"card\",\"data\":{\"card_name\":\"Profitability\",\"col\":4}},{\"id\":\"kxhoaiqdLq\",\"type\":\"card\",\"data\":{\"card_name\":\"Opening and Closing\",\"col\":4}},{\"id\":\"q0MAlU2j_Z\",\"type\":\"card\",\"data\":{\"card_name\":\"Subscription Management\",\"col\":4}},{\"id\":\"ptm7T6Hwu-\",\"type\":\"card\",\"data\":{\"card_name\":\"Share Management\",\"col\":4}},{\"id\":\"OX7lZHbiTr\",\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}}]", + "content": "[{\"id\":\"MmUf9abwxg\",\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Accounts\",\"col\":12}},{\"id\":\"i0EtSjDAXq\",\"type\":\"chart\",\"data\":{\"chart_name\":\"Profit and Loss\",\"col\":12}},{\"id\":\"X78jcbq1u3\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"vikWSkNm6_\",\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"id\":\"pMywM0nhlj\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Chart of Accounts\",\"col\":3}},{\"id\":\"_pRdD6kqUG\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Invoice\",\"col\":3}},{\"id\":\"G984SgVRJN\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Purchase Invoice\",\"col\":3}},{\"id\":\"1ArNvt9qhz\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Journal Entry\",\"col\":3}},{\"id\":\"F9f4I1viNr\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Payment Entry\",\"col\":3}},{\"id\":\"4IBBOIxfqW\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Accounts Receivable\",\"col\":3}},{\"id\":\"El2anpPaFY\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"General Ledger\",\"col\":3}},{\"id\":\"1nwcM9upJo\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Trial Balance\",\"col\":3}},{\"id\":\"OF9WOi1Ppc\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"id\":\"iAwpe-Chra\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Learn Accounting\",\"col\":3}},{\"id\":\"B7-uxs8tkU\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"tHb3yxthkR\",\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"col\":12}},{\"id\":\"DnNtsmxpty\",\"type\":\"card\",\"data\":{\"card_name\":\"Accounting Masters\",\"col\":4}},{\"id\":\"nKKr6fjgjb\",\"type\":\"card\",\"data\":{\"card_name\":\"General Ledger\",\"col\":4}},{\"id\":\"xOHTyD8b5l\",\"type\":\"card\",\"data\":{\"card_name\":\"Accounts Receivable\",\"col\":4}},{\"id\":\"_Cb7C8XdJJ\",\"type\":\"card\",\"data\":{\"card_name\":\"Accounts Payable\",\"col\":4}},{\"id\":\"p7NY6MHe2Y\",\"type\":\"card\",\"data\":{\"card_name\":\"Financial Statements\",\"col\":4}},{\"id\":\"KlqilF5R_V\",\"type\":\"card\",\"data\":{\"card_name\":\"Taxes\",\"col\":4}},{\"id\":\"jTUy8LB0uw\",\"type\":\"card\",\"data\":{\"card_name\":\"Cost Center and Budgeting\",\"col\":4}},{\"id\":\"Wn2lhs7WLn\",\"type\":\"card\",\"data\":{\"card_name\":\"Multi Currency\",\"col\":4}},{\"id\":\"PAQMqqNkBM\",\"type\":\"card\",\"data\":{\"card_name\":\"Bank Statement\",\"col\":4}},{\"id\":\"Q_hBCnSeJY\",\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"id\":\"3AK1Zf0oew\",\"type\":\"card\",\"data\":{\"card_name\":\"Profitability\",\"col\":4}},{\"id\":\"kxhoaiqdLq\",\"type\":\"card\",\"data\":{\"card_name\":\"Opening and Closing\",\"col\":4}},{\"id\":\"q0MAlU2j_Z\",\"type\":\"card\",\"data\":{\"card_name\":\"Subscription Management\",\"col\":4}},{\"id\":\"ptm7T6Hwu-\",\"type\":\"card\",\"data\":{\"card_name\":\"Share Management\",\"col\":4}},{\"id\":\"OX7lZHbiTr\",\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}}]", "creation": "2020-03-02 15:41:59.515192", "custom_blocks": [], "docstatus": 0, @@ -1061,7 +1061,7 @@ "type": "Link" } ], - "modified": "2023-05-30 13:23:29.316711", + "modified": "2023-07-04 14:32:15.842044", "modified_by": "Administrator", "module": "Accounts", "name": "Accounting", @@ -1074,6 +1074,13 @@ "roles": [], "sequence_id": 2.0, "shortcuts": [ + { + "color": "Grey", + "doc_view": "List", + "label": "Learn Accounting", + "type": "URL", + "url": "https://frappe.school/courses/erpnext-accounting?utm_source=in_app" + }, { "label": "Chart of Accounts", "link_to": "Account", diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py index 259568a24b..e1431eae17 100644 --- a/erpnext/assets/doctype/asset/depreciation.py +++ b/erpnext/assets/doctype/asset/depreciation.py @@ -40,6 +40,7 @@ def post_depreciation_entries(date=None): date = today() failed_asset_names = [] + error_log_names = [] for asset_name in get_depreciable_assets(date): asset_doc = frappe.get_doc("Asset", asset_name) @@ -50,10 +51,12 @@ def post_depreciation_entries(date=None): except Exception as e: frappe.db.rollback() failed_asset_names.append(asset_name) + error_log = frappe.log_error(e) + error_log_names.append(error_log.name) if failed_asset_names: set_depr_entry_posting_status_for_failed_assets(failed_asset_names) - notify_depr_entry_posting_error(failed_asset_names) + notify_depr_entry_posting_error(failed_asset_names, error_log_names) frappe.db.commit() @@ -239,7 +242,7 @@ def set_depr_entry_posting_status_for_failed_assets(failed_asset_names): frappe.db.set_value("Asset", asset_name, "depr_entry_posting_status", "Failed") -def notify_depr_entry_posting_error(failed_asset_names): +def notify_depr_entry_posting_error(failed_asset_names, error_log_names): recipients = get_users_with_role("Accounts Manager") if not recipients: @@ -247,7 +250,8 @@ def notify_depr_entry_posting_error(failed_asset_names): subject = _("Error while posting depreciation entries") - asset_links = get_comma_separated_asset_links(failed_asset_names) + asset_links = get_comma_separated_links(failed_asset_names, "Asset") + error_log_links = get_comma_separated_links(error_log_names, "Error Log") message = ( _("Hello,") @@ -257,23 +261,26 @@ def notify_depr_entry_posting_error(failed_asset_names): ) + "." + "

" - + _( - "Please raise a support ticket and share this email, or forward this email to your development team so that they can find the issue in the developer console by manually creating the depreciation entry via the asset's depreciation schedule table." + + _("Here are the error logs for the aforementioned failed depreciation entries: {0}").format( + error_log_links ) + + "." + + "

" + + _("Please share this email with your support team so that they can find and fix the issue.") ) frappe.sendmail(recipients=recipients, subject=subject, message=message) -def get_comma_separated_asset_links(asset_names): - asset_links = [] +def get_comma_separated_links(names, doctype): + links = [] - for asset_name in asset_names: - asset_links.append(get_link_to_form("Asset", asset_name)) + for name in names: + links.append(get_link_to_form(doctype, name)) - asset_links = ", ".join(asset_links) + links = ", ".join(links) - return asset_links + return links @frappe.whitelist() diff --git a/erpnext/assets/doctype/asset_movement/asset_movement.js b/erpnext/assets/doctype/asset_movement/asset_movement.js index f9c600731b..4ccc3f8013 100644 --- a/erpnext/assets/doctype/asset_movement/asset_movement.js +++ b/erpnext/assets/doctype/asset_movement/asset_movement.js @@ -63,7 +63,7 @@ frappe.ui.form.on('Asset Movement', { fieldnames_to_be_altered = { target_location: { read_only: 0, reqd: 1 }, source_location: { read_only: 1, reqd: 0 }, - from_employee: { read_only: 0, reqd: 1 }, + from_employee: { read_only: 0, reqd: 0 }, to_employee: { read_only: 1, reqd: 0 } }; } diff --git a/erpnext/assets/doctype/asset_movement/asset_movement.py b/erpnext/assets/doctype/asset_movement/asset_movement.py index b58ca10482..22055dcb73 100644 --- a/erpnext/assets/doctype/asset_movement/asset_movement.py +++ b/erpnext/assets/doctype/asset_movement/asset_movement.py @@ -62,29 +62,20 @@ class AssetMovement(Document): frappe.throw(_("Source and Target Location cannot be same")) if self.purpose == "Receipt": - # only when asset is bought and first entry is made - if not d.source_location and not (d.target_location or d.to_employee): + if not (d.source_location or d.from_employee) and not (d.target_location or d.to_employee): frappe.throw( _("Target Location or To Employee is required while receiving Asset {0}").format(d.asset) ) - elif d.source_location: - # when asset is received from an employee - if d.target_location and not d.from_employee: - frappe.throw( - _("From employee is required while receiving Asset {0} to a target location").format( - d.asset - ) - ) - if d.from_employee and not d.target_location: - frappe.throw( - _("Target Location is required while receiving Asset {0} from an employee").format(d.asset) - ) - if d.to_employee and d.target_location: - frappe.throw( - _( - "Asset {0} cannot be received at a location and given to employee in a single movement" - ).format(d.asset) - ) + elif d.from_employee and not d.target_location: + frappe.throw( + _("Target Location is required while receiving Asset {0} from an employee").format(d.asset) + ) + elif d.to_employee and d.target_location: + frappe.throw( + _( + "Asset {0} cannot be received at a location and given to an employee in a single movement" + ).format(d.asset) + ) def validate_employee(self): for d in self.assets: diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js index b788a32d6a..48b17f58fb 100644 --- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js +++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js @@ -82,7 +82,7 @@ frappe.query_reports["Fixed Asset Register"] = { "label": __("Start Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "depends_on": "eval: doc.filter_based_on == 'Fiscal Year'", }, { @@ -90,7 +90,7 @@ frappe.query_reports["Fixed Asset Register"] = { "label": __("End Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "depends_on": "eval: doc.filter_based_on == 'Fiscal Year'", }, { diff --git a/erpnext/buying/doctype/supplier/supplier.js b/erpnext/buying/doctype/supplier/supplier.js index 5b95d0fde3..372ca56b86 100644 --- a/erpnext/buying/doctype/supplier/supplier.js +++ b/erpnext/buying/doctype/supplier/supplier.js @@ -66,8 +66,6 @@ frappe.ui.form.on("Supplier", { }, refresh: function (frm) { - frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Supplier' } - if (frappe.defaults.get_default("supp_master_name") != "Naming Series") { frm.toggle_display("naming_series", false); } else { diff --git a/erpnext/buying/workspace/buying/buying.json b/erpnext/buying/workspace/buying/buying.json index 58c8f74710..1394fc48d5 100644 --- a/erpnext/buying/workspace/buying/buying.json +++ b/erpnext/buying/workspace/buying/buying.json @@ -5,7 +5,7 @@ "label": "Purchase Order Trends" } ], - "content": "[{\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Buying\",\"col\":12}},{\"type\":\"chart\",\"data\":{\"chart_name\":\"Purchase Order Trends\",\"col\":12}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Item\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Material Request\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Purchase Order\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Purchase Analytics\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Purchase Order Analysis\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Buying\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Items & Pricing\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Supplier\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Supplier Scorecard\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Key Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Other Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Regional\",\"col\":4}}]", + "content": "[{\"id\":\"I3JijHOxil\",\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Buying\",\"col\":12}},{\"id\":\"j3dJGo8Ok6\",\"type\":\"chart\",\"data\":{\"chart_name\":\"Purchase Order Trends\",\"col\":12}},{\"id\":\"oN7lXSwQji\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"Ivw1PI_wEJ\",\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"id\":\"RrWFEi4kCf\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Item\",\"col\":3}},{\"id\":\"RFIakryyJP\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Material Request\",\"col\":3}},{\"id\":\"bM10abFmf6\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Purchase Order\",\"col\":3}},{\"id\":\"lR0Hw_37Pu\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Purchase Analytics\",\"col\":3}},{\"id\":\"_HN0Ljw1lX\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Purchase Order Analysis\",\"col\":3}},{\"id\":\"kuLuiMRdnX\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"id\":\"tQFeiKptW2\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Learn Procurement\",\"col\":3}},{\"id\":\"0NiuFE_EGS\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"Xe2GVLOq8J\",\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"col\":12}},{\"id\":\"QwqyG6XuUt\",\"type\":\"card\",\"data\":{\"card_name\":\"Buying\",\"col\":4}},{\"id\":\"bTPjOxC_N_\",\"type\":\"card\",\"data\":{\"card_name\":\"Items & Pricing\",\"col\":4}},{\"id\":\"87ht0HIneb\",\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}},{\"id\":\"EDOsBOmwgw\",\"type\":\"card\",\"data\":{\"card_name\":\"Supplier\",\"col\":4}},{\"id\":\"oWNNIiNb2i\",\"type\":\"card\",\"data\":{\"card_name\":\"Supplier Scorecard\",\"col\":4}},{\"id\":\"7F_13-ihHB\",\"type\":\"card\",\"data\":{\"card_name\":\"Key Reports\",\"col\":4}},{\"id\":\"pfwiLvionl\",\"type\":\"card\",\"data\":{\"card_name\":\"Other Reports\",\"col\":4}},{\"id\":\"8ySDy6s4qn\",\"type\":\"card\",\"data\":{\"card_name\":\"Regional\",\"col\":4}}]", "creation": "2020-01-28 11:50:26.195467", "custom_blocks": [], "docstatus": 0, @@ -511,7 +511,7 @@ "type": "Link" } ], - "modified": "2023-05-24 14:47:20.535772", + "modified": "2023-07-04 14:43:30.387683", "modified_by": "Administrator", "module": "Buying", "name": "Buying", @@ -532,6 +532,13 @@ "stats_filter": "{\n \"disabled\": 0\n}", "type": "DocType" }, + { + "color": "Grey", + "doc_view": "List", + "label": "Learn Procurement", + "type": "URL", + "url": "https://frappe.school/courses/procurement?utm_source=in_app" + }, { "color": "Yellow", "format": "{} Pending", diff --git a/erpnext/communication/doctype/communication_medium/communication_medium.json b/erpnext/communication/doctype/communication_medium/communication_medium.json index 1e1fe3bf49..b6b9c7e434 100644 --- a/erpnext/communication/doctype/communication_medium/communication_medium.json +++ b/erpnext/communication/doctype/communication_medium/communication_medium.json @@ -61,7 +61,7 @@ "fieldname": "communication_channel", "fieldtype": "Select", "label": "Communication Channel", - "options": "\nExotel" + "options": "" } ], "links": [], diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py index fec494a84c..7b7c53ecfe 100644 --- a/erpnext/controllers/buying_controller.py +++ b/erpnext/controllers/buying_controller.py @@ -437,18 +437,23 @@ class BuyingController(SubcontractingController): # validate rate with ref PR def validate_rejected_warehouse(self): - for d in self.get("items"): - if flt(d.rejected_qty) and not d.rejected_warehouse: + for item in self.get("items"): + if flt(item.rejected_qty) and not item.rejected_warehouse: if self.rejected_warehouse: - d.rejected_warehouse = self.rejected_warehouse + item.rejected_warehouse = self.rejected_warehouse - if not d.rejected_warehouse: + if not item.rejected_warehouse: frappe.throw( - _("Row #{0}: Rejected Warehouse is mandatory against rejected Item {1}").format( - d.idx, d.item_code + _("Row #{0}: Rejected Warehouse is mandatory for the rejected Item {1}").format( + item.idx, item.item_code ) ) + if item.get("rejected_warehouse") and (item.get("rejected_warehouse") == item.get("warehouse")): + frappe.throw( + _("Row #{0}: Accepted Warehouse and Rejected Warehouse cannot be same").format(item.idx) + ) + # validate accepted and rejected qty def validate_accepted_rejected_qty(self): for d in self.get("items"): diff --git a/erpnext/controllers/sales_and_purchase_return.py b/erpnext/controllers/sales_and_purchase_return.py index 954668055e..173e812dbd 100644 --- a/erpnext/controllers/sales_and_purchase_return.py +++ b/erpnext/controllers/sales_and_purchase_return.py @@ -669,7 +669,11 @@ def get_filters( if reference_voucher_detail_no: filters["voucher_detail_no"] = reference_voucher_detail_no - if item_row and item_row.get("warehouse"): + if ( + voucher_type in ["Purchase Receipt", "Purchase Invoice"] + and item_row + and item_row.get("warehouse") + ): filters["warehouse"] = item_row.get("warehouse") return filters diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py index 5137e03058..caf4b6f18b 100644 --- a/erpnext/controllers/stock_controller.py +++ b/erpnext/controllers/stock_controller.py @@ -201,6 +201,12 @@ class StockController(AccountsController): warehouse_asset_account = warehouse_account[item_row.get("warehouse")]["account"] expense_account = frappe.get_cached_value("Company", self.company, "default_expense_account") + if not expense_account: + frappe.throw( + _( + "Please set default cost of goods sold account in company {0} for booking rounding gain and loss during stock transfer" + ).format(frappe.bold(self.company)) + ) gl_list.append( self.get_gl_dict( diff --git a/erpnext/crm/doctype/lead/lead.js b/erpnext/crm/doctype/lead/lead.js index b98a27ede8..9ac54183a2 100644 --- a/erpnext/crm/doctype/lead/lead.js +++ b/erpnext/crm/doctype/lead/lead.js @@ -30,11 +30,6 @@ erpnext.LeadController = class LeadController extends frappe.ui.form.Controller var me = this; let doc = this.frm.doc; erpnext.toggle_naming_series(); - frappe.dynamic_link = { - doc: doc, - fieldname: 'name', - doctype: 'Lead' - }; if (!this.frm.is_new() && doc.__onload && !doc.__onload.is_customer) { this.frm.add_custom_button(__("Customer"), this.make_customer, __("Create")); diff --git a/erpnext/crm/doctype/prospect/prospect.js b/erpnext/crm/doctype/prospect/prospect.js index 495ed291ae..c1a7ff576c 100644 --- a/erpnext/crm/doctype/prospect/prospect.js +++ b/erpnext/crm/doctype/prospect/prospect.js @@ -3,8 +3,6 @@ frappe.ui.form.on('Prospect', { refresh (frm) { - frappe.dynamic_link = { doc: frm.doc, fieldname: "name", doctype: frm.doctype }; - if (!frm.is_new() && frappe.boot.user.can_create.includes("Customer")) { frm.add_custom_button(__("Customer"), function() { frappe.model.open_mapped_doc({ diff --git a/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.json b/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.json deleted file mode 100644 index 0d42ca8c85..0000000000 --- a/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.json +++ /dev/null @@ -1,89 +0,0 @@ -{ - "actions": [], - "creation": "2019-05-21 07:41:53.536536", - "doctype": "DocType", - "engine": "InnoDB", - "field_order": [ - "enabled", - "section_break_2", - "account_sid", - "api_key", - "api_token", - "section_break_6", - "map_custom_field_to_doctype", - "target_doctype" - ], - "fields": [ - { - "default": "0", - "fieldname": "enabled", - "fieldtype": "Check", - "label": "Enabled" - }, - { - "depends_on": "enabled", - "fieldname": "section_break_2", - "fieldtype": "Section Break", - "label": "Credentials" - }, - { - "fieldname": "account_sid", - "fieldtype": "Data", - "label": "Account SID" - }, - { - "fieldname": "api_token", - "fieldtype": "Data", - "label": "API Token" - }, - { - "fieldname": "api_key", - "fieldtype": "Data", - "label": "API Key" - }, - { - "depends_on": "enabled", - "fieldname": "section_break_6", - "fieldtype": "Section Break", - "label": "Custom Field" - }, - { - "default": "0", - "fieldname": "map_custom_field_to_doctype", - "fieldtype": "Check", - "label": "Map Custom Field to DocType" - }, - { - "depends_on": "map_custom_field_to_doctype", - "fieldname": "target_doctype", - "fieldtype": "Link", - "label": "Target DocType", - "mandatory_depends_on": "map_custom_field_to_doctype", - "options": "DocType" - } - ], - "issingle": 1, - "links": [], - "modified": "2022-12-14 17:24:50.176107", - "modified_by": "Administrator", - "module": "ERPNext Integrations", - "name": "Exotel Settings", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "role": "System Manager", - "share": 1, - "write": 1 - } - ], - "quick_entry": 1, - "sort_field": "modified", - "sort_order": "ASC", - "states": [], - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py b/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py deleted file mode 100644 index 4879cb5623..0000000000 --- a/erpnext/erpnext_integrations/doctype/exotel_settings/exotel_settings.py +++ /dev/null @@ -1,22 +0,0 @@ -# Copyright (c) 2019, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - - -import frappe -import requests -from frappe import _ -from frappe.model.document import Document - - -class ExotelSettings(Document): - def validate(self): - self.verify_credentials() - - def verify_credentials(self): - if self.enabled: - response = requests.get( - "https://api.exotel.com/v1/Accounts/{sid}".format(sid=self.account_sid), - auth=(self.api_key, self.api_token), - ) - if response.status_code != 200: - frappe.throw(_("Invalid credentials")) diff --git a/erpnext/erpnext_integrations/exotel_integration.py b/erpnext/erpnext_integrations/exotel_integration.py deleted file mode 100644 index 0d40667e32..0000000000 --- a/erpnext/erpnext_integrations/exotel_integration.py +++ /dev/null @@ -1,151 +0,0 @@ -import frappe -import requests - -# api/method/erpnext.erpnext_integrations.exotel_integration.handle_incoming_call -# api/method/erpnext.erpnext_integrations.exotel_integration.handle_end_call -# api/method/erpnext.erpnext_integrations.exotel_integration.handle_missed_call - - -@frappe.whitelist(allow_guest=True) -def handle_incoming_call(**kwargs): - try: - exotel_settings = get_exotel_settings() - if not exotel_settings.enabled: - return - - call_payload = kwargs - status = call_payload.get("Status") - if status == "free": - return - - call_log = get_call_log(call_payload) - if not call_log: - create_call_log(call_payload) - else: - update_call_log(call_payload, call_log=call_log) - except Exception as e: - frappe.db.rollback() - exotel_settings.log_error("Error in Exotel incoming call") - frappe.db.commit() - - -@frappe.whitelist(allow_guest=True) -def handle_end_call(**kwargs): - update_call_log(kwargs, "Completed") - - -@frappe.whitelist(allow_guest=True) -def handle_missed_call(**kwargs): - status = "" - call_type = kwargs.get("CallType") - dial_call_status = kwargs.get("DialCallStatus") - - if call_type == "incomplete" and dial_call_status == "no-answer": - status = "No Answer" - elif call_type == "client-hangup" and dial_call_status == "canceled": - status = "Canceled" - elif call_type == "incomplete" and dial_call_status == "failed": - status = "Failed" - - update_call_log(kwargs, status) - - -def update_call_log(call_payload, status="Ringing", call_log=None): - call_log = call_log or get_call_log(call_payload) - - # for a new sid, call_log and get_call_log will be empty so create a new log - if not call_log: - call_log = create_call_log(call_payload) - if call_log: - call_log.status = status - call_log.to = call_payload.get("DialWhomNumber") - call_log.duration = call_payload.get("DialCallDuration") or 0 - call_log.recording_url = call_payload.get("RecordingUrl") - call_log.save(ignore_permissions=True) - frappe.db.commit() - return call_log - - -def get_call_log(call_payload): - call_log_id = call_payload.get("CallSid") - if frappe.db.exists("Call Log", call_log_id): - return frappe.get_doc("Call Log", call_log_id) - - -def map_custom_field(call_payload, call_log): - field_value = call_payload.get("CustomField") - - if not field_value: - return call_log - - settings = get_exotel_settings() - target_doctype = settings.target_doctype - mapping_enabled = settings.map_custom_field_to_doctype - - if not mapping_enabled or not target_doctype: - return call_log - - call_log.append("links", {"link_doctype": target_doctype, "link_name": field_value}) - - return call_log - - -def create_call_log(call_payload): - call_log = frappe.new_doc("Call Log") - call_log.id = call_payload.get("CallSid") - call_log.to = call_payload.get("DialWhomNumber") - call_log.medium = call_payload.get("To") - call_log.status = "Ringing" - setattr(call_log, "from", call_payload.get("CallFrom")) - map_custom_field(call_payload, call_log) - call_log.save(ignore_permissions=True) - frappe.db.commit() - return call_log - - -@frappe.whitelist() -def get_call_status(call_id): - endpoint = get_exotel_endpoint("Calls/{call_id}.json".format(call_id=call_id)) - response = requests.get(endpoint) - status = response.json().get("Call", {}).get("Status") - return status - - -@frappe.whitelist() -def make_a_call(from_number, to_number, caller_id, **kwargs): - endpoint = get_exotel_endpoint("Calls/connect.json?details=true") - response = requests.post( - endpoint, data={"From": from_number, "To": to_number, "CallerId": caller_id, **kwargs} - ) - - return response.json() - - -def get_exotel_settings(): - return frappe.get_single("Exotel Settings") - - -def whitelist_numbers(numbers, caller_id): - endpoint = get_exotel_endpoint("CustomerWhitelist") - response = requests.post( - endpoint, - data={ - "VirtualNumber": caller_id, - "Number": numbers, - }, - ) - - return response - - -def get_all_exophones(): - endpoint = get_exotel_endpoint("IncomingPhoneNumbers") - response = requests.post(endpoint) - return response - - -def get_exotel_endpoint(action): - settings = get_exotel_settings() - return "https://{api_key}:{api_token}@api.exotel.com/v1/Accounts/{sid}/{action}".format( - api_key=settings.api_key, api_token=settings.api_token, sid=settings.account_sid, action=action - ) diff --git a/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json b/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json index ccc46b7a22..5c4be6ffaa 100644 --- a/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json +++ b/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json @@ -230,17 +230,6 @@ "onboard": 0, "type": "Card Break" }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Exotel Settings", - "link_count": 0, - "link_to": "Exotel Settings", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, { "hidden": 0, "is_query_report": 0, @@ -252,7 +241,7 @@ "type": "Link" } ], - "modified": "2023-05-24 14:47:25.984717", + "modified": "2023-05-24 14:47:26.984717", "modified_by": "Administrator", "module": "ERPNext Integrations", "name": "ERPNext Integrations", diff --git a/erpnext/hooks.py b/erpnext/hooks.py index 68af89bbc5..b68318ac30 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -83,7 +83,7 @@ update_website_context = [ my_account_context = "erpnext.e_commerce.shopping_cart.utils.update_my_account_context" webform_list_context = "erpnext.controllers.website_list_for_contact.get_webform_list_context" -calendars = ["Task", "Work Order", "Leave Application", "Sales Order", "Holiday List", "ToDo"] +calendars = ["Task", "Work Order", "Sales Order", "Holiday List", "ToDo"] website_generators = ["Item Group", "Website Item", "BOM", "Sales Partner"] @@ -355,6 +355,11 @@ doc_events = { }, } +# function should expect the variable and doc as arguments +naming_series_variables = { + "FY": "erpnext.accounts.utils.parse_naming_series_variable", +} + # On cancel event Payment Entry will be exempted and all linked submittable doctype will get cancelled. # to maintain data integrity we exempted payment entry. it will un-link when sales invoice get cancelled. # if payment entry not in auto cancel exempted doctypes it will cancel payment entry. @@ -416,6 +421,10 @@ scheduler_events = { "erpnext.selling.doctype.quotation.quotation.set_expired_status", "erpnext.buying.doctype.supplier_quotation.supplier_quotation.set_expired_status", "erpnext.accounts.doctype.process_statement_of_accounts.process_statement_of_accounts.send_auto_email", + "erpnext.accounts.utils.auto_create_exchange_rate_revaluation_daily", + ], + "weekly": [ + "erpnext.accounts.utils.auto_create_exchange_rate_revaluation_weekly", ], "daily_long": [ "erpnext.setup.doctype.email_digest.email_digest.send", @@ -608,3 +617,8 @@ global_search_doctypes = { additional_timeline_content = { "*": ["erpnext.telephony.doctype.call_log.call_log.get_linked_call_logs"] } + + +extend_bootinfo = [ + "erpnext.support.doctype.service_level_agreement.service_level_agreement.add_sla_doctypes", +] diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py index 6dc1ff6a49..a988badd74 100644 --- a/erpnext/manufacturing/doctype/production_plan/production_plan.py +++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py @@ -621,7 +621,7 @@ class ProductionPlan(Document): def create_work_order(self, item): from erpnext.manufacturing.doctype.work_order.work_order import OverProductionError - if item.get("qty") <= 0: + if flt(item.get("qty")) <= 0: return wo = frappe.new_doc("Work Order") @@ -697,10 +697,9 @@ class ProductionPlan(Document): material_request.flags.ignore_permissions = 1 material_request.run_method("set_missing_values") + material_request.save() if self.get("submit_material_request"): material_request.submit() - else: - material_request.save() frappe.flags.mute_messages = False diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py index 79b1e798ed..7c15bf9234 100644 --- a/erpnext/manufacturing/doctype/work_order/work_order.py +++ b/erpnext/manufacturing/doctype/work_order/work_order.py @@ -1026,7 +1026,7 @@ class WorkOrder(Document): consumed_qty = frappe.db.sql( """ SELECT - SUM(qty) + SUM(detail.qty) FROM `tabStock Entry` entry, `tabStock Entry Detail` detail diff --git a/erpnext/manufacturing/report/job_card_summary/job_card_summary.js b/erpnext/manufacturing/report/job_card_summary/job_card_summary.js index 782ce8110a..a874f22482 100644 --- a/erpnext/manufacturing/report/job_card_summary/job_card_summary.js +++ b/erpnext/manufacturing/report/job_card_summary/job_card_summary.js @@ -17,7 +17,7 @@ frappe.query_reports["Job Card Summary"] = { label: __("Fiscal Year"), fieldtype: "Link", options: "Fiscal Year", - default: frappe.defaults.get_user_default("fiscal_year"), + default: erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), reqd: 1, on_change: function(query_report) { var fiscal_year = query_report.get_values().fiscal_year; diff --git a/erpnext/manufacturing/workspace/manufacturing/manufacturing.json b/erpnext/manufacturing/workspace/manufacturing/manufacturing.json index d862c349e3..518ae14659 100644 --- a/erpnext/manufacturing/workspace/manufacturing/manufacturing.json +++ b/erpnext/manufacturing/workspace/manufacturing/manufacturing.json @@ -1,6 +1,6 @@ { "charts": [], - "content": "[{\"id\":\"csBCiDglCE\",\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"id\":\"xit0dg7KvY\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"BOM\",\"col\":3}},{\"id\":\"LRhGV9GAov\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Production Plan\",\"col\":3}},{\"id\":\"69KKosI6Hg\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Work Order\",\"col\":3}},{\"id\":\"PwndxuIpB3\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Job Card\",\"col\":3}},{\"id\":\"OaiDqTT03Y\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Forecasting\",\"col\":3}},{\"id\":\"OtMcArFRa5\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"BOM Stock Report\",\"col\":3}},{\"id\":\"76yYsI5imF\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Production Planning Report\",\"col\":3}},{\"id\":\"bN_6tHS-Ct\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"yVEFZMqVwd\",\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"col\":12}},{\"id\":\"rwrmsTI58-\",\"type\":\"card\",\"data\":{\"card_name\":\"Production\",\"col\":4}},{\"id\":\"6dnsyX-siZ\",\"type\":\"card\",\"data\":{\"card_name\":\"Bill of Materials\",\"col\":4}},{\"id\":\"CIq-v5f5KC\",\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"id\":\"8RRiQeYr0G\",\"type\":\"card\",\"data\":{\"card_name\":\"Tools\",\"col\":4}},{\"id\":\"Pu8z7-82rT\",\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}}]", + "content": "[{\"id\":\"csBCiDglCE\",\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"id\":\"xit0dg7KvY\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"BOM\",\"col\":3}},{\"id\":\"LRhGV9GAov\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Production Plan\",\"col\":3}},{\"id\":\"69KKosI6Hg\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Work Order\",\"col\":3}},{\"id\":\"PwndxuIpB3\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Job Card\",\"col\":3}},{\"id\":\"OaiDqTT03Y\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Forecasting\",\"col\":3}},{\"id\":\"OtMcArFRa5\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"BOM Stock Report\",\"col\":3}},{\"id\":\"76yYsI5imF\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Production Planning Report\",\"col\":3}},{\"id\":\"PIQJYZOMnD\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Learn Manufacturing\",\"col\":3}},{\"id\":\"bN_6tHS-Ct\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"yVEFZMqVwd\",\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"col\":12}},{\"id\":\"rwrmsTI58-\",\"type\":\"card\",\"data\":{\"card_name\":\"Production\",\"col\":4}},{\"id\":\"6dnsyX-siZ\",\"type\":\"card\",\"data\":{\"card_name\":\"Bill of Materials\",\"col\":4}},{\"id\":\"CIq-v5f5KC\",\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"id\":\"8RRiQeYr0G\",\"type\":\"card\",\"data\":{\"card_name\":\"Tools\",\"col\":4}},{\"id\":\"Pu8z7-82rT\",\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}}]", "creation": "2020-03-02 17:11:37.032604", "custom_blocks": [], "docstatus": 0, @@ -316,7 +316,7 @@ "type": "Link" } ], - "modified": "2023-05-27 16:41:04.776115", + "modified": "2023-07-04 14:40:47.281125", "modified_by": "Administrator", "module": "Manufacturing", "name": "Manufacturing", @@ -329,6 +329,13 @@ "roles": [], "sequence_id": 8.0, "shortcuts": [ + { + "color": "Grey", + "doc_view": "List", + "label": "Learn Manufacturing", + "type": "URL", + "url": "https://frappe.school/courses/manufacturing?utm_source=in_app" + }, { "color": "Grey", "doc_view": "List", diff --git a/erpnext/patches.txt b/erpnext/patches.txt index 4536abf811..fb59a7de62 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -317,7 +317,7 @@ erpnext.patches.v13_0.update_docs_link erpnext.patches.v15_0.update_asset_value_for_manual_depr_entries erpnext.patches.v15_0.update_gpa_and_ndb_for_assdeprsch erpnext.patches.v14_0.create_accounting_dimensions_for_closing_balance -erpnext.patches.v14_0.update_closing_balances #17-05-2023 +erpnext.patches.v14_0.update_closing_balances #14-07-2023 execute:frappe.db.set_single_value("Accounts Settings", "merge_similar_account_heads", 0) # below migration patches should always run last erpnext.patches.v14_0.migrate_gl_to_payment_ledger @@ -334,4 +334,6 @@ erpnext.patches.v14_0.cleanup_workspaces erpnext.patches.v15_0.remove_loan_management_module #2023-07-03 erpnext.patches.v14_0.set_report_in_process_SOA erpnext.buying.doctype.supplier.patches.migrate_supplier_portal_users +execute:frappe.defaults.clear_default("fiscal_year") +erpnext.patches.v15_0.remove_exotel_integration erpnext.patches.v14_0.single_to_multi_dunning diff --git a/erpnext/patches/v14_0/update_closing_balances.py b/erpnext/patches/v14_0/update_closing_balances.py index d66467775c..2947b98740 100644 --- a/erpnext/patches/v14_0/update_closing_balances.py +++ b/erpnext/patches/v14_0/update_closing_balances.py @@ -13,56 +13,63 @@ from erpnext.accounts.utils import get_fiscal_year def execute(): frappe.db.truncate("Account Closing Balance") - i = 0 - company_wise_order = {} - for pcv in frappe.db.get_all( - "Period Closing Voucher", - fields=["company", "posting_date", "name"], - filters={"docstatus": 1}, - order_by="posting_date", - ): + for company in frappe.get_all("Company", pluck="name"): + i = 0 + company_wise_order = {} + for pcv in frappe.db.get_all( + "Period Closing Voucher", + fields=["company", "posting_date", "name"], + filters={"docstatus": 1, "company": company}, + order_by="posting_date", + ): - company_wise_order.setdefault(pcv.company, []) - if pcv.posting_date not in company_wise_order[pcv.company]: - pcv_doc = frappe.get_doc("Period Closing Voucher", pcv.name) - pcv_doc.year_start_date = get_fiscal_year( - pcv.posting_date, pcv.fiscal_year, company=pcv.company - )[1] + company_wise_order.setdefault(pcv.company, []) + if pcv.posting_date not in company_wise_order[pcv.company]: + pcv_doc = frappe.get_doc("Period Closing Voucher", pcv.name) + pcv_doc.year_start_date = get_fiscal_year( + pcv.posting_date, pcv.fiscal_year, company=pcv.company + )[1] - # get gl entries against pcv - gl_entries = frappe.db.get_all( - "GL Entry", filters={"voucher_no": pcv.name, "is_cancelled": 0}, fields=["*"] - ) - for entry in gl_entries: - entry["is_period_closing_voucher_entry"] = 1 - entry["closing_date"] = pcv_doc.posting_date - entry["period_closing_voucher"] = pcv_doc.name - - # get all gl entries for the year - closing_entries = frappe.db.get_all( - "GL Entry", - filters={ - "is_cancelled": 0, - "voucher_no": ["!=", pcv.name], - "posting_date": ["between", [pcv_doc.year_start_date, pcv.posting_date]], - "is_opening": "No", - }, - fields=["*"], - ) - - if i == 0: - # add opening entries only for the first pcv - closing_entries += frappe.db.get_all( - "GL Entry", - filters={"is_cancelled": 0, "is_opening": "Yes"}, - fields=["*"], + # get gl entries against pcv + gl_entries = frappe.db.get_all( + "GL Entry", filters={"voucher_no": pcv.name, "is_cancelled": 0}, fields=["*"] ) + for entry in gl_entries: + entry["is_period_closing_voucher_entry"] = 1 + entry["closing_date"] = pcv_doc.posting_date + entry["period_closing_voucher"] = pcv_doc.name - for entry in closing_entries: - entry["closing_date"] = pcv_doc.posting_date - entry["period_closing_voucher"] = pcv_doc.name + closing_entries = [] - make_closing_entries(gl_entries + closing_entries, voucher_name=pcv.name) - company_wise_order[pcv.company].append(pcv.posting_date) + if pcv.posting_date not in company_wise_order[pcv.company]: + # get all gl entries for the year + closing_entries = frappe.db.get_all( + "GL Entry", + filters={ + "is_cancelled": 0, + "voucher_no": ["!=", pcv.name], + "posting_date": ["between", [pcv_doc.year_start_date, pcv.posting_date]], + "is_opening": "No", + "company": company, + }, + fields=["*"], + ) - i += 1 + if i == 0: + # add opening entries only for the first pcv + closing_entries += frappe.db.get_all( + "GL Entry", + filters={"is_cancelled": 0, "is_opening": "Yes", "company": company}, + fields=["*"], + ) + + for entry in closing_entries: + entry["closing_date"] = pcv_doc.posting_date + entry["period_closing_voucher"] = pcv_doc.name + + entries = gl_entries + closing_entries + + if entries: + make_closing_entries(entries, voucher_name=pcv.name) + i += 1 + company_wise_order[pcv.company].append(pcv.posting_date) diff --git a/erpnext/patches/v15_0/remove_exotel_integration.py b/erpnext/patches/v15_0/remove_exotel_integration.py new file mode 100644 index 0000000000..a37773f337 --- /dev/null +++ b/erpnext/patches/v15_0/remove_exotel_integration.py @@ -0,0 +1,37 @@ +from contextlib import suppress + +import click +import frappe +from frappe import _ +from frappe.desk.doctype.notification_log.notification_log import make_notification_logs +from frappe.utils.user import get_system_managers + +SETTINGS_DOCTYPE = "Exotel Settings" + + +def execute(): + if "exotel_integration" in frappe.get_installed_apps(): + return + + with suppress(Exception): + exotel = frappe.get_doc(SETTINGS_DOCTYPE) + if exotel.enabled: + notify_existing_users() + + frappe.delete_doc("DocType", SETTINGS_DOCTYPE) + + +def notify_existing_users(): + click.secho( + "Exotel integration is moved to a separate app and will be removed from ERPNext in version-15.\n" + "Please install the app to continue using the integration: https://github.com/frappe/exotel_integration", + fg="yellow", + ) + + notification = { + "subject": _( + "WARNING: Exotel app has been separated from ERPNext, please install the app to continue using Exotel integration." + ), + "type": "Alert", + } + make_notification_logs(notification, get_system_managers(only_name=True)) diff --git a/erpnext/projects/workspace/projects/projects.json b/erpnext/projects/workspace/projects/projects.json index 50730bac0d..94ae9c04a4 100644 --- a/erpnext/projects/workspace/projects/projects.json +++ b/erpnext/projects/workspace/projects/projects.json @@ -5,7 +5,7 @@ "label": "Open Projects" } ], - "content": "[{\"type\":\"chart\",\"data\":{\"chart_name\":\"Open Projects\",\"col\":12}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Task\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Project\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Timesheet\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Project Billing Summary\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Projects\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Time Tracking\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}}]", + "content": "[{\"id\":\"VDMms0hapk\",\"type\":\"chart\",\"data\":{\"chart_name\":\"Open Projects\",\"col\":12}},{\"id\":\"7Mbx6I5JUf\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"nyuMo9byw7\",\"type\":\"header\",\"data\":{\"text\":\"Your Shortcuts\",\"col\":12}},{\"id\":\"dILbX_r0ve\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Task\",\"col\":3}},{\"id\":\"JT8ntrqRiJ\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Project\",\"col\":3}},{\"id\":\"RsafDhm1MS\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Timesheet\",\"col\":3}},{\"id\":\"cVJH-gD0CR\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Project Billing Summary\",\"col\":3}},{\"id\":\"DbctrdmAy1\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"id\":\"jx5aPK9aXN\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Learn Project Management\",\"col\":3}},{\"id\":\"ncIHWGQQvX\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"oGhjvYjfv-\",\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"col\":12}},{\"id\":\"TdsgJyG3EI\",\"type\":\"card\",\"data\":{\"card_name\":\"Projects\",\"col\":4}},{\"id\":\"nIc0iyvf1T\",\"type\":\"card\",\"data\":{\"card_name\":\"Time Tracking\",\"col\":4}},{\"id\":\"8G1if4jsQ7\",\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"id\":\"o7qTNRXZI8\",\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}}]", "creation": "2020-03-02 15:46:04.874669", "custom_blocks": [], "docstatus": 0, @@ -192,7 +192,7 @@ "type": "Link" } ], - "modified": "2023-05-24 14:47:23.179860", + "modified": "2023-07-04 14:39:08.935853", "modified_by": "Administrator", "module": "Projects", "name": "Projects", @@ -205,6 +205,13 @@ "roles": [], "sequence_id": 11.0, "shortcuts": [ + { + "color": "Grey", + "doc_view": "List", + "label": "Learn Project Management", + "type": "URL", + "url": "https://frappe.school/courses/project-management?utm_source=in_app" + }, { "color": "Blue", "format": "{} Assigned", diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js index 543d0e9790..6410333f0c 100644 --- a/erpnext/public/js/controllers/transaction.js +++ b/erpnext/public/js/controllers/transaction.js @@ -358,12 +358,14 @@ erpnext.TransactionController = class TransactionController extends erpnext.taxe } refresh() { + erpnext.toggle_naming_series(); erpnext.hide_company(); this.set_dynamic_labels(); this.setup_sms(); this.setup_quality_inspection(); this.validate_has_items(); + erpnext.utils.view_serial_batch_nos(this.frm); } scan_barcode() { diff --git a/erpnext/public/js/financial_statements.js b/erpnext/public/js/financial_statements.js index b0082bdb28..959cf507d5 100644 --- a/erpnext/public/js/financial_statements.js +++ b/erpnext/public/js/financial_statements.js @@ -56,7 +56,7 @@ erpnext.financial_statements = { // dropdown for links to other financial statements erpnext.financial_statements.filters = get_filters() - let fiscal_year = frappe.defaults.get_user_default("fiscal_year") + let fiscal_year = erpnext.utils.get_fiscal_year(frappe.datetime.get_today()); frappe.model.with_doc("Fiscal Year", fiscal_year, function(r) { var fy = frappe.model.get_doc("Fiscal Year", fiscal_year); @@ -137,7 +137,7 @@ function get_filters() { "label": __("Start Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "reqd": 1, "depends_on": "eval:doc.filter_based_on == 'Fiscal Year'" }, @@ -146,7 +146,7 @@ function get_filters() { "label": __("End Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "reqd": 1, "depends_on": "eval:doc.filter_based_on == 'Fiscal Year'" }, @@ -182,6 +182,16 @@ function get_filters() { company: frappe.query_report.get_filter_value("company") }); } + }, + { + "fieldname": "project", + "label": __("Project"), + "fieldtype": "MultiSelectList", + get_data: function(txt) { + return frappe.db.get_link_options('Project', txt, { + company: frappe.query_report.get_filter_value("company") + }); + }, } ] diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js index a859a671b0..497f8d2674 100755 --- a/erpnext/public/js/utils.js +++ b/erpnext/public/js/utils.js @@ -113,6 +113,23 @@ $.extend(erpnext.utils, { } }, + view_serial_batch_nos: function(frm) { + let bundle_ids = frm.doc.items.filter(d => d.serial_and_batch_bundle); + + if (bundle_ids?.length) { + frm.add_custom_button(__('Serial / Batch Nos'), () => { + frappe.route_options = { + "voucher_no": frm.doc.name, + "voucher_type": frm.doc.doctype, + "from_date": frm.doc.posting_date || frm.doc.transaction_date, + "to_date": frm.doc.posting_date || frm.doc.transaction_date, + "company": frm.doc.company, + }; + frappe.set_route("query-report", "Serial and Batch Summary"); + }, __('View')); + } + }, + add_indicator_for_multicompany: function(frm, info) { frm.dashboard.stats_area.show(); frm.dashboard.stats_area_row.addClass('flex'); @@ -381,6 +398,23 @@ $.extend(erpnext.utils, { }); }); }); + }, + + get_fiscal_year: function(date) { + let fiscal_year = ''; + frappe.call({ + method: "erpnext.accounts.utils.get_fiscal_year", + args: { + date: date + }, + async: false, + callback: function(r) { + if (r.message) { + fiscal_year = r.message[0]; + } + } + }); + return fiscal_year; } }); @@ -632,7 +666,6 @@ erpnext.utils.update_child_items = function(opts) { fields.splice(3, 0, { fieldtype: 'Float', fieldname: "conversion_factor", - in_list_view: 1, label: __("Conversion Factor"), precision: get_precision('conversion_factor') }) @@ -640,6 +673,7 @@ erpnext.utils.update_child_items = function(opts) { new frappe.ui.Dialog({ title: __("Update Items"), + size: "extra-large", fields: [ { fieldname: "trans_items", @@ -854,95 +888,87 @@ $(document).on('app_ready', function() { // Show SLA dashboard $(document).on('app_ready', function() { - frappe.call({ - method: 'erpnext.support.doctype.service_level_agreement.service_level_agreement.get_sla_doctypes', - callback: function(r) { - if (!r.message) - return; + $.each(frappe.boot.service_level_agreement_doctypes, function(_i, d) { + frappe.ui.form.on(d, { + onload: function(frm) { + if (!frm.doc.service_level_agreement) + return; - $.each(r.message, function(_i, d) { - frappe.ui.form.on(d, { - onload: function(frm) { - if (!frm.doc.service_level_agreement) - return; - - frappe.call({ - method: 'erpnext.support.doctype.service_level_agreement.service_level_agreement.get_service_level_agreement_filters', - args: { - doctype: frm.doc.doctype, - name: frm.doc.service_level_agreement, - customer: frm.doc.customer - }, - callback: function (r) { - if (r && r.message) { - frm.set_query('priority', function() { - return { - filters: { - 'name': ['in', r.message.priority], - } - }; - }); - frm.set_query('service_level_agreement', function() { - return { - filters: { - 'name': ['in', r.message.service_level_agreements], - } - }; - }); - } - } - }); + frappe.call({ + method: 'erpnext.support.doctype.service_level_agreement.service_level_agreement.get_service_level_agreement_filters', + args: { + doctype: frm.doc.doctype, + name: frm.doc.service_level_agreement, + customer: frm.doc.customer }, - - refresh: function(frm) { - if (frm.doc.status !== 'Closed' && frm.doc.service_level_agreement - && ['First Response Due', 'Resolution Due'].includes(frm.doc.agreement_status)) { - frappe.call({ - 'method': 'frappe.client.get', - args: { - doctype: 'Service Level Agreement', - name: frm.doc.service_level_agreement - }, - callback: function(data) { - let statuses = data.message.pause_sla_on; - const hold_statuses = []; - $.each(statuses, (_i, entry) => { - hold_statuses.push(entry.status); - }); - if (hold_statuses.includes(frm.doc.status)) { - frm.dashboard.clear_headline(); - let message = {'indicator': 'orange', 'msg': __('SLA is on hold since {0}', [moment(frm.doc.on_hold_since).fromNow(true)])}; - frm.dashboard.set_headline_alert( - '
' + - '
' + - ''+ message.msg +' ' + - '
' + - '
' - ); - } else { - set_time_to_resolve_and_response(frm, data.message.apply_sla_for_resolution); + callback: function (r) { + if (r && r.message) { + frm.set_query('priority', function() { + return { + filters: { + 'name': ['in', r.message.priority], } - } + }; + }); + frm.set_query('service_level_agreement', function() { + return { + filters: { + 'name': ['in', r.message.service_level_agreements], + } + }; }); - } else if (frm.doc.service_level_agreement) { - frm.dashboard.clear_headline(); - - let agreement_status = (frm.doc.agreement_status == 'Fulfilled') ? - {'indicator': 'green', 'msg': 'Service Level Agreement has been fulfilled'} : - {'indicator': 'red', 'msg': 'Service Level Agreement Failed'}; - - frm.dashboard.set_headline_alert( - '
' + - '
' + - ' ' + - '
' + - '
' - ); } - }, + } }); - }); - } + }, + + refresh: function(frm) { + if (frm.doc.status !== 'Closed' && frm.doc.service_level_agreement + && ['First Response Due', 'Resolution Due'].includes(frm.doc.agreement_status)) { + frappe.call({ + 'method': 'frappe.client.get', + args: { + doctype: 'Service Level Agreement', + name: frm.doc.service_level_agreement + }, + callback: function(data) { + let statuses = data.message.pause_sla_on; + const hold_statuses = []; + $.each(statuses, (_i, entry) => { + hold_statuses.push(entry.status); + }); + if (hold_statuses.includes(frm.doc.status)) { + frm.dashboard.clear_headline(); + let message = {'indicator': 'orange', 'msg': __('SLA is on hold since {0}', [moment(frm.doc.on_hold_since).fromNow(true)])}; + frm.dashboard.set_headline_alert( + '
' + + '
' + + ''+ message.msg +' ' + + '
' + + '
' + ); + } else { + set_time_to_resolve_and_response(frm, data.message.apply_sla_for_resolution); + } + } + }); + } else if (frm.doc.service_level_agreement) { + frm.dashboard.clear_headline(); + + let agreement_status = (frm.doc.agreement_status == 'Fulfilled') ? + {'indicator': 'green', 'msg': 'Service Level Agreement has been fulfilled'} : + {'indicator': 'red', 'msg': 'Service Level Agreement Failed'}; + + frm.dashboard.set_headline_alert( + '
' + + '
' + + ' ' + + '
' + + '
' + ); + } + }, + }); }); }); @@ -1011,4 +1037,4 @@ function attach_selector_button(inner_text, append_loction, context, grid_row) { $btn.on("click", function() { context.show_serial_batch_selector(grid_row.frm, grid_row.doc, "", "", true); }); -} +} \ No newline at end of file diff --git a/erpnext/regional/report/fichier_des_ecritures_comptables_[fec]/fichier_des_ecritures_comptables_[fec].js b/erpnext/regional/report/fichier_des_ecritures_comptables_[fec]/fichier_des_ecritures_comptables_[fec].js index a4c7640c81..b85b58f636 100644 --- a/erpnext/regional/report/fichier_des_ecritures_comptables_[fec]/fichier_des_ecritures_comptables_[fec].js +++ b/erpnext/regional/report/fichier_des_ecritures_comptables_[fec]/fichier_des_ecritures_comptables_[fec].js @@ -16,7 +16,7 @@ frappe.query_reports["Fichier des Ecritures Comptables [FEC]"] = { "label": __("Fiscal Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "reqd": 1 } ], diff --git a/erpnext/regional/report/irs_1099/irs_1099.js b/erpnext/regional/report/irs_1099/irs_1099.js index 070ff43f78..b3508e40a9 100644 --- a/erpnext/regional/report/irs_1099/irs_1099.js +++ b/erpnext/regional/report/irs_1099/irs_1099.js @@ -17,7 +17,7 @@ frappe.query_reports["IRS 1099"] = { "label": __("Fiscal Year"), "fieldtype": "Link", "options": "Fiscal Year", - "default": frappe.defaults.get_user_default("fiscal_year"), + "default": erpnext.utils.get_fiscal_year(frappe.datetime.get_today()), "reqd": 1, "width": 80, }, diff --git a/erpnext/selling/doctype/customer/customer.js b/erpnext/selling/doctype/customer/customer.js index 540e767d32..60f0941559 100644 --- a/erpnext/selling/doctype/customer/customer.js +++ b/erpnext/selling/doctype/customer/customer.js @@ -131,8 +131,6 @@ frappe.ui.form.on("Customer", { erpnext.toggle_naming_series(); } - frappe.dynamic_link = {doc: frm.doc, fieldname: 'name', doctype: 'Customer'} - if(!frm.doc.__islocal) { frappe.contacts.render_address_and_contact(frm); diff --git a/erpnext/selling/workspace/selling/selling.json b/erpnext/selling/workspace/selling/selling.json index f498223aa8..e13bdec11f 100644 --- a/erpnext/selling/workspace/selling/selling.json +++ b/erpnext/selling/workspace/selling/selling.json @@ -5,7 +5,7 @@ "label": "Sales Order Trends" } ], - "content": "[{\"id\":\"ow595dYDrI\",\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Selling\",\"col\":12}},{\"id\":\"vBSf8Vi9U8\",\"type\":\"chart\",\"data\":{\"chart_name\":\"Sales Order Trends\",\"col\":12}},{\"id\":\"aW2i5R5GRP\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"1it3dCOnm6\",\"type\":\"header\",\"data\":{\"text\":\"Quick Access\",\"col\":12}},{\"id\":\"x7pLl-spS4\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Item\",\"col\":3}},{\"id\":\"SSGrXWmY-H\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Order\",\"col\":3}},{\"id\":\"-5J_yLxDaS\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Analytics\",\"col\":3}},{\"id\":\"6YEYpnIBKV\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Point of Sale\",\"col\":3}},{\"id\":\"c_GjZuZ2oN\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"id\":\"oNjjNbnUHp\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"0BcePLg0g1\",\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"col\":12}},{\"id\":\"uze5dJ1ipL\",\"type\":\"card\",\"data\":{\"card_name\":\"Selling\",\"col\":4}},{\"id\":\"3j2fYwMAkq\",\"type\":\"card\",\"data\":{\"card_name\":\"Point of Sale\",\"col\":4}},{\"id\":\"xImm8NepFt\",\"type\":\"card\",\"data\":{\"card_name\":\"Items and Pricing\",\"col\":4}},{\"id\":\"6MjIe7KCQo\",\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}},{\"id\":\"lBu2EKgmJF\",\"type\":\"card\",\"data\":{\"card_name\":\"Key Reports\",\"col\":4}},{\"id\":\"1ARHrjg4kI\",\"type\":\"card\",\"data\":{\"card_name\":\"Other Reports\",\"col\":4}}]", + "content": "[{\"id\":\"ow595dYDrI\",\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Selling\",\"col\":12}},{\"id\":\"vBSf8Vi9U8\",\"type\":\"chart\",\"data\":{\"chart_name\":\"Sales Order Trends\",\"col\":12}},{\"id\":\"aW2i5R5GRP\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"1it3dCOnm6\",\"type\":\"header\",\"data\":{\"text\":\"Quick Access\",\"col\":12}},{\"id\":\"x7pLl-spS4\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Item\",\"col\":3}},{\"id\":\"SSGrXWmY-H\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Order\",\"col\":3}},{\"id\":\"-5J_yLxDaS\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Analytics\",\"col\":3}},{\"id\":\"6YEYpnIBKV\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Point of Sale\",\"col\":3}},{\"id\":\"c_GjZuZ2oN\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"id\":\"mX-9DJSyT2\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Learn Sales Management\",\"col\":3}},{\"id\":\"oNjjNbnUHp\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"0BcePLg0g1\",\"type\":\"header\",\"data\":{\"text\":\"Reports & Masters\",\"col\":12}},{\"id\":\"uze5dJ1ipL\",\"type\":\"card\",\"data\":{\"card_name\":\"Selling\",\"col\":4}},{\"id\":\"3j2fYwMAkq\",\"type\":\"card\",\"data\":{\"card_name\":\"Point of Sale\",\"col\":4}},{\"id\":\"xImm8NepFt\",\"type\":\"card\",\"data\":{\"card_name\":\"Items and Pricing\",\"col\":4}},{\"id\":\"6MjIe7KCQo\",\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}},{\"id\":\"lBu2EKgmJF\",\"type\":\"card\",\"data\":{\"card_name\":\"Key Reports\",\"col\":4}},{\"id\":\"1ARHrjg4kI\",\"type\":\"card\",\"data\":{\"card_name\":\"Other Reports\",\"col\":4}}]", "creation": "2020-01-28 11:49:12.092882", "custom_blocks": [], "docstatus": 0, @@ -621,7 +621,7 @@ "type": "Link" } ], - "modified": "2023-05-26 16:31:53.634851", + "modified": "2023-07-04 14:35:58.204465", "modified_by": "Administrator", "module": "Selling", "name": "Selling", @@ -634,6 +634,13 @@ "roles": [], "sequence_id": 6.0, "shortcuts": [ + { + "color": "Grey", + "doc_view": "List", + "label": "Learn Sales Management", + "type": "URL", + "url": "https://frappe.school/courses/sales-management-course?utm_source=in_app" + }, { "label": "Point of Sale", "link_to": "point-of-sale", diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js index 333538722e..f4682c1b80 100644 --- a/erpnext/setup/doctype/company/company.js +++ b/erpnext/setup/doctype/company/company.js @@ -81,8 +81,6 @@ frappe.ui.form.on("Company", { disbale_coa_fields(frm); frappe.contacts.render_address_and_contact(frm); - frappe.dynamic_link = {doc: frm.doc, fieldname: 'name', doctype: 'Company'} - if (frappe.perm.has_perm("Cost Center", 0, 'read')) { frm.add_custom_button(__('Cost Centers'), function() { frappe.set_route('Tree', 'Cost Center', {'company': frm.doc.name}); diff --git a/erpnext/setup/doctype/company/company.json b/erpnext/setup/doctype/company/company.json index 6292ad7349..ed2852e87a 100644 --- a/erpnext/setup/doctype/company/company.json +++ b/erpnext/setup/doctype/company/company.json @@ -95,6 +95,10 @@ "depreciation_cost_center", "capital_work_in_progress_account", "asset_received_but_not_billed", + "exchange_rate_revaluation_settings_section", + "auto_exchange_rate_revaluation", + "auto_err_frequency", + "submit_err_jv", "budget_detail", "exception_budget_approver_role", "registration_info", @@ -731,6 +735,29 @@ "fieldname": "book_advance_payments_in_separate_party_account", "fieldtype": "Check", "label": "Book Advance Payments in Separate Party Account" + }, + { + "fieldname": "exchange_rate_revaluation_settings_section", + "fieldtype": "Section Break", + "label": "Exchange Rate Revaluation Settings" + }, + { + "default": "0", + "fieldname": "auto_exchange_rate_revaluation", + "fieldtype": "Check", + "label": "Auto Create Exchange Rate Revaluation" + }, + { + "fieldname": "auto_err_frequency", + "fieldtype": "Select", + "label": "Frequency", + "options": "Daily\nWeekly" + }, + { + "default": "0", + "fieldname": "submit_err_jv", + "fieldtype": "Check", + "label": "Submit ERR Journals?" } ], "icon": "fa fa-building", @@ -738,7 +765,7 @@ "image_field": "company_logo", "is_tree": 1, "links": [], - "modified": "2023-06-23 18:22:27.219706", + "modified": "2023-07-07 05:41:41.537256", "modified_by": "Administrator", "module": "Setup", "name": "Company", diff --git a/erpnext/setup/doctype/global_defaults/global_defaults.json b/erpnext/setup/doctype/global_defaults/global_defaults.json index bafb97a5d8..823d2ba7d7 100644 --- a/erpnext/setup/doctype/global_defaults/global_defaults.json +++ b/erpnext/setup/doctype/global_defaults/global_defaults.json @@ -1,352 +1,99 @@ { - "allow_copy": 1, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2013-05-02 17:53:24", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "editable_grid": 0, + "actions": [], + "allow_copy": 1, + "creation": "2013-05-02 17:53:24", + "doctype": "DocType", + "engine": "InnoDB", + "field_order": [ + "default_company", + "country", + "default_distance_unit", + "column_break_8", + "default_currency", + "hide_currency_symbol", + "disable_rounded_total", + "disable_in_words" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "default_company", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Default Company", - "length": 0, - "no_copy": 0, - "options": "Company", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "default_company", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "label": "Default Company", + "options": "Company" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "current_fiscal_year", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Current Fiscal Year", - "length": 0, - "no_copy": 0, - "options": "Fiscal Year", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "country", + "fieldtype": "Link", + "label": "Country", + "options": "Country" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "country", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Country", - "length": 0, - "no_copy": 0, - "options": "Country", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "default_distance_unit", + "fieldtype": "Link", + "label": "Default Distance Unit", + "options": "UOM" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "", - "fieldname": "default_distance_unit", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Default Distance Unit", - "length": 0, - "no_copy": 0, - "options": "UOM", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_8", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_8", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "INR", + "fieldname": "default_currency", + "fieldtype": "Link", + "ignore_user_permissions": 1, + "in_list_view": 1, + "label": "Default Currency", + "options": "Currency", + "reqd": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "INR", - "fieldname": "default_currency", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 1, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Default Currency", - "length": 0, - "no_copy": 0, - "options": "Currency", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "description": "Do not show any symbol like $ etc next to currencies.", + "fieldname": "hide_currency_symbol", + "fieldtype": "Select", + "in_list_view": 1, + "label": "Hide Currency Symbol", + "options": "\nNo\nYes" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "Do not show any symbol like $ etc next to currencies.", - "fieldname": "hide_currency_symbol", - "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Hide Currency Symbol", - "length": 0, - "no_copy": 0, - "options": "\nNo\nYes", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "description": "If disable, 'Rounded Total' field will not be visible in any transaction", + "fieldname": "disable_rounded_total", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Disable Rounded Total" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "If disable, 'Rounded Total' field will not be visible in any transaction", - "fieldname": "disable_rounded_total", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Disable Rounded Total", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "description": "If disable, 'In Words' field will not be visible in any transaction", - "fieldname": "disable_in_words", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Disable In Words", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "default": "0", + "description": "If disable, 'In Words' field will not be visible in any transaction", + "fieldname": "disable_in_words", + "fieldtype": "Check", + "in_list_view": 1, + "label": "Disable In Words" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "fa fa-cog", - "idx": 1, - "image_view": 0, - "in_create": 1, - "is_submittable": 0, - "issingle": 1, - "istable": 0, - "max_attachments": 0, - "menu_index": 0, - "modified": "2018-10-15 03:08:19.886212", - "modified_by": "Administrator", - "module": "Setup", - "name": "Global Defaults", - "owner": "Administrator", + ], + "icon": "fa fa-cog", + "idx": 1, + "in_create": 1, + "issingle": 1, + "links": [], + "modified": "2023-07-01 19:45:00.323953", + "modified_by": "Administrator", + "module": "Setup", + "name": "Global Defaults", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 0, - "email": 0, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 0, - "read": 1, - "report": 0, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "read": 1, + "role": "System Manager", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 1, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + ], + "read_only": 1, + "sort_field": "modified", + "sort_order": "DESC", + "states": [] } \ No newline at end of file diff --git a/erpnext/setup/doctype/global_defaults/global_defaults.py b/erpnext/setup/doctype/global_defaults/global_defaults.py index 16e94343a3..fc80483e8e 100644 --- a/erpnext/setup/doctype/global_defaults/global_defaults.py +++ b/erpnext/setup/doctype/global_defaults/global_defaults.py @@ -10,7 +10,6 @@ from frappe.utils import cint keydict = { # "key in defaults": "key in Global Defaults" - "fiscal_year": "current_fiscal_year", "company": "default_company", "currency": "default_currency", "country": "country", @@ -29,22 +28,6 @@ class GlobalDefaults(Document): for key in keydict: frappe.db.set_default(key, self.get(keydict[key], "")) - # update year start date and year end date from fiscal_year - if self.current_fiscal_year: - if fiscal_year := frappe.get_all( - "Fiscal Year", - filters={"name": self.current_fiscal_year}, - fields=["year_start_date", "year_end_date"], - limit=1, - order_by=None, - ): - ysd = fiscal_year[0].year_start_date or "" - yed = fiscal_year[0].year_end_date or "" - - if ysd and yed: - frappe.db.set_default("year_start_date", ysd.strftime("%Y-%m-%d")) - frappe.db.set_default("year_end_date", yed.strftime("%Y-%m-%d")) - # enable default currency if self.default_currency: frappe.db.set_value("Currency", self.default_currency, "enabled", 1) diff --git a/erpnext/setup/doctype/holiday_list/holiday_list.js b/erpnext/setup/doctype/holiday_list/holiday_list.js index ea033c7ed9..90d9f1b6f5 100644 --- a/erpnext/setup/doctype/holiday_list/holiday_list.js +++ b/erpnext/setup/doctype/holiday_list/holiday_list.js @@ -6,13 +6,41 @@ frappe.ui.form.on("Holiday List", { if (frm.doc.holidays) { frm.set_value("total_holidays", frm.doc.holidays.length); } + + frm.call("get_supported_countries").then(r => { + frm.subdivisions_by_country = r.message.subdivisions_by_country; + frm.fields_dict.country.set_data( + r.message.countries.sort((a, b) => a.label.localeCompare(b.label)) + ); + + if (frm.doc.country) { + frm.trigger("set_subdivisions"); + } + }); }, from_date: function(frm) { if (frm.doc.from_date && !frm.doc.to_date) { var a_year_from_start = frappe.datetime.add_months(frm.doc.from_date, 12); frm.set_value("to_date", frappe.datetime.add_days(a_year_from_start, -1)); } - } + }, + country: function(frm) { + frm.set_value("subdivision", ""); + + if (frm.doc.country) { + frm.trigger("set_subdivisions"); + } + }, + set_subdivisions: function(frm) { + const subdivisions = [...frm.subdivisions_by_country[frm.doc.country]]; + if (subdivisions && subdivisions.length > 0) { + frm.fields_dict.subdivision.set_data(subdivisions); + frm.set_df_property("subdivision", "hidden", 0); + } else { + frm.fields_dict.subdivision.set_data([]); + frm.set_df_property("subdivision", "hidden", 1); + } + }, }); frappe.tour["Holiday List"] = [ diff --git a/erpnext/setup/doctype/holiday_list/holiday_list.json b/erpnext/setup/doctype/holiday_list/holiday_list.json index 4bbe6a6cb2..45671d181b 100644 --- a/erpnext/setup/doctype/holiday_list/holiday_list.json +++ b/erpnext/setup/doctype/holiday_list/holiday_list.json @@ -1,480 +1,166 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, + "actions": [], "allow_import": 1, "allow_rename": 1, "autoname": "field:holiday_list_name", - "beta": 0, "creation": "2013-01-10 16:34:14", - "custom": 0, - "docstatus": 0, "doctype": "DocType", "document_type": "Setup", - "editable_grid": 0, "engine": "InnoDB", + "field_order": [ + "holiday_list_name", + "from_date", + "to_date", + "column_break_4", + "total_holidays", + "add_weekly_holidays", + "weekly_off", + "get_weekly_off_dates", + "add_local_holidays", + "country", + "subdivision", + "get_local_holidays", + "holidays_section", + "holidays", + "clear_table", + "section_break_9", + "color" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "holiday_list_name", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Holiday List Name", - "length": 0, - "no_copy": 0, "oldfieldname": "holiday_list_name", "oldfieldtype": "Data", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, "unique": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "from_date", "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "From Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "to_date", "fieldtype": "Date", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "To Date", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "reqd": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "column_break_4", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Column Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "total_holidays", "fieldtype": "Int", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Total Holidays", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "read_only": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, "collapsible": 1, - "columns": 0, + "depends_on": "eval: doc.from_date && doc.to_date", "fieldname": "add_weekly_holidays", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Add Weekly Holidays", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Add Weekly Holidays" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "weekly_off", "fieldtype": "Select", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, "in_standard_filter": 1, "label": "Weekly Off", - "length": 0, "no_copy": 1, "options": "\nSunday\nMonday\nTuesday\nWednesday\nThursday\nFriday\nSaturday", - "permlevel": 0, "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "report_hide": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "get_weekly_off_dates", "fieldtype": "Button", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Add to Holidays", - "length": 0, - "no_copy": 0, - "options": "get_weekly_off_dates", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "get_weekly_off_dates" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "holidays_section", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Holidays", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "label": "Holidays" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "holidays", "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Holidays", - "length": 0, - "no_copy": 0, "oldfieldname": "holiday_list_details", "oldfieldtype": "Table", - "options": "Holiday", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "Holiday" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "clear_table", "fieldtype": "Button", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Clear Table", - "length": 0, - "no_copy": 0, - "options": "clear_table", - "permlevel": 0, - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "options": "clear_table" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "section_break_9", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldtype": "Section Break" }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "color", "fieldtype": "Color", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Color", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "print_hide": 1 + }, + { + "fieldname": "country", + "fieldtype": "Autocomplete", + "label": "Country" + }, + { + "depends_on": "country", + "fieldname": "subdivision", + "fieldtype": "Autocomplete", + "label": "Subdivision" + }, + { + "collapsible": 1, + "depends_on": "eval: doc.from_date && doc.to_date", + "fieldname": "add_local_holidays", + "fieldtype": "Section Break", + "label": "Add Local Holidays" + }, + { + "fieldname": "get_local_holidays", + "fieldtype": "Button", + "label": "Add to Holidays", + "options": "get_local_holidays" } ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, "icon": "fa fa-calendar", "idx": 1, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-07-03 07:22:46.474096", + "links": [], + "modified": "2023-07-14 13:28:53.156421", "modified_by": "Administrator", "module": "Setup", "name": "Holiday List", + "naming_rule": "By fieldname", "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, "create": 1, "delete": 1, "email": 1, - "export": 0, - "if_owner": 0, - "import": 0, - "permlevel": 0, "print": 1, "read": 1, "report": 1, "role": "HR Manager", - "set_user_permissions": 0, "share": 1, - "submit": 0, "write": 1 } ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, "sort_field": "modified", "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + "states": [] } \ No newline at end of file diff --git a/erpnext/setup/doctype/holiday_list/holiday_list.py b/erpnext/setup/doctype/holiday_list/holiday_list.py index 84d0d35287..2ef4e655b2 100644 --- a/erpnext/setup/doctype/holiday_list/holiday_list.py +++ b/erpnext/setup/doctype/holiday_list/holiday_list.py @@ -3,11 +3,15 @@ import json +from datetime import date import frappe +from babel import Locale from frappe import _, throw from frappe.model.document import Document -from frappe.utils import cint, formatdate, getdate, today +from frappe.utils import formatdate, getdate, today +from holidays import country_holidays +from holidays.utils import list_supported_countries class OverlapError(frappe.ValidationError): @@ -21,25 +25,66 @@ class HolidayList(Document): @frappe.whitelist() def get_weekly_off_dates(self): - self.validate_values() - date_list = self.get_weekly_off_date_list(self.from_date, self.to_date) - last_idx = max( - [cint(d.idx) for d in self.get("holidays")] - or [ - 0, - ] - ) - for i, d in enumerate(date_list): - ch = self.append("holidays", {}) - ch.description = _(self.weekly_off) - ch.holiday_date = d - ch.weekly_off = 1 - ch.idx = last_idx + i + 1 - - def validate_values(self): if not self.weekly_off: throw(_("Please select weekly off day")) + existing_holidays = self.get_holidays() + + for d in self.get_weekly_off_date_list(self.from_date, self.to_date): + if d in existing_holidays: + continue + + self.append("holidays", {"description": _(self.weekly_off), "holiday_date": d, "weekly_off": 1}) + + self.sort_holidays() + + @frappe.whitelist() + def get_supported_countries(self): + subdivisions_by_country = list_supported_countries() + countries = [ + {"value": country, "label": local_country_name(country)} + for country in subdivisions_by_country.keys() + ] + return { + "countries": countries, + "subdivisions_by_country": subdivisions_by_country, + } + + @frappe.whitelist() + def get_local_holidays(self): + if not self.country: + throw(_("Please select a country")) + + existing_holidays = self.get_holidays() + from_date = getdate(self.from_date) + to_date = getdate(self.to_date) + + for holiday_date, holiday_name in country_holidays( + self.country, + subdiv=self.subdivision, + years=[from_date.year, to_date.year], + language=frappe.local.lang, + ).items(): + if holiday_date in existing_holidays: + continue + + if holiday_date < from_date or holiday_date > to_date: + continue + + self.append( + "holidays", {"description": holiday_name, "holiday_date": holiday_date, "weekly_off": 0} + ) + + self.sort_holidays() + + def sort_holidays(self): + self.holidays.sort(key=lambda x: getdate(x.holiday_date)) + for i in range(len(self.holidays)): + self.holidays[i].idx = i + 1 + + def get_holidays(self) -> list[date]: + return [getdate(holiday.holiday_date) for holiday in self.holidays] + def validate_days(self): if getdate(self.from_date) > getdate(self.to_date): throw(_("To Date cannot be before From Date")) @@ -120,3 +165,8 @@ def is_holiday(holiday_list, date=None): ) else: return False + + +def local_country_name(country_code: str) -> str: + """Return the localized country name for the given country code.""" + return Locale.parse(frappe.local.lang).territories.get(country_code, country_code) diff --git a/erpnext/setup/doctype/holiday_list/test_holiday_list.py b/erpnext/setup/doctype/holiday_list/test_holiday_list.py index d32cfe8265..23b08fd117 100644 --- a/erpnext/setup/doctype/holiday_list/test_holiday_list.py +++ b/erpnext/setup/doctype/holiday_list/test_holiday_list.py @@ -3,7 +3,7 @@ import unittest from contextlib import contextmanager -from datetime import timedelta +from datetime import date, timedelta import frappe from frappe.utils import getdate @@ -23,6 +23,41 @@ class TestHolidayList(unittest.TestCase): fetched_holiday_list = frappe.get_value("Holiday List", holiday_list.name) self.assertEqual(holiday_list.name, fetched_holiday_list) + def test_weekly_off(self): + holiday_list = frappe.new_doc("Holiday List") + holiday_list.from_date = "2023-01-01" + holiday_list.to_date = "2023-02-28" + holiday_list.weekly_off = "Sunday" + holiday_list.get_weekly_off_dates() + + holidays = [holiday.holiday_date for holiday in holiday_list.holidays] + + self.assertNotIn(date(2022, 12, 25), holidays) + self.assertIn(date(2023, 1, 1), holidays) + self.assertIn(date(2023, 1, 8), holidays) + self.assertIn(date(2023, 1, 15), holidays) + self.assertIn(date(2023, 1, 22), holidays) + self.assertIn(date(2023, 1, 29), holidays) + self.assertIn(date(2023, 2, 5), holidays) + self.assertIn(date(2023, 2, 12), holidays) + self.assertIn(date(2023, 2, 19), holidays) + self.assertIn(date(2023, 2, 26), holidays) + self.assertNotIn(date(2023, 3, 5), holidays) + + def test_local_holidays(self): + holiday_list = frappe.new_doc("Holiday List") + holiday_list.from_date = "2023-04-01" + holiday_list.to_date = "2023-04-30" + holiday_list.country = "DE" + holiday_list.subdivision = "SN" + holiday_list.get_local_holidays() + + holidays = [holiday.holiday_date for holiday in holiday_list.holidays] + self.assertNotIn(date(2023, 1, 1), holidays) + self.assertIn(date(2023, 4, 7), holidays) + self.assertIn(date(2023, 4, 10), holidays) + self.assertNotIn(date(2023, 5, 1), holidays) + def make_holiday_list( name, from_date=getdate() - timedelta(days=10), to_date=getdate(), holiday_dates=None diff --git a/erpnext/setup/doctype/sales_partner/sales_partner.js b/erpnext/setup/doctype/sales_partner/sales_partner.js index 5656d43e85..f9e3770560 100644 --- a/erpnext/setup/doctype/sales_partner/sales_partner.js +++ b/erpnext/setup/doctype/sales_partner/sales_partner.js @@ -3,8 +3,6 @@ frappe.ui.form.on('Sales Partner', { refresh: function(frm) { - frappe.dynamic_link = {doc: frm.doc, fieldname: 'name', doctype: 'Sales Partner'} - if(frm.doc.__islocal){ hide_field(['address_html', 'contact_html', 'address_contacts']); frappe.contacts.clear_address_and_contact(frm); diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py index 013d945c22..85eaf5fa92 100644 --- a/erpnext/setup/install.py +++ b/erpnext/setup/install.py @@ -169,7 +169,7 @@ def add_standard_navbar_items(): { "item_label": "Documentation", "item_type": "Route", - "route": "https://docs.erpnext.com/docs/v14/user/manual/en/introduction", + "route": "https://docs.erpnext.com/", "is_standard": 1, }, { @@ -178,6 +178,12 @@ def add_standard_navbar_items(): "route": "https://discuss.frappe.io", "is_standard": 1, }, + { + "item_label": "Frappe School", + "item_type": "Route", + "route": "https://frappe.school?utm_source=in_app", + "is_standard": 1, + }, { "item_label": "Report an Issue", "item_type": "Route", diff --git a/erpnext/setup/setup_wizard/operations/install_fixtures.py b/erpnext/setup/setup_wizard/operations/install_fixtures.py index 8e61fe2872..535c87d652 100644 --- a/erpnext/setup/setup_wizard/operations/install_fixtures.py +++ b/erpnext/setup/setup_wizard/operations/install_fixtures.py @@ -462,11 +462,9 @@ def install_defaults(args=None): # nosemgrep def set_global_defaults(args): global_defaults = frappe.get_doc("Global Defaults", "Global Defaults") - current_fiscal_year = frappe.get_all("Fiscal Year")[0] global_defaults.update( { - "current_fiscal_year": current_fiscal_year.name, "default_currency": args.get("currency"), "default_company": args.get("company_name"), "country": args.get("country"), diff --git a/erpnext/stock/doctype/batch/batch.js b/erpnext/stock/doctype/batch/batch.js index fa8b2bee55..3b07e4e80c 100644 --- a/erpnext/stock/doctype/batch/batch.js +++ b/erpnext/stock/doctype/batch/batch.js @@ -47,8 +47,6 @@ frappe.ui.form.on('Batch', { return; } - debugger - const section = frm.dashboard.add_section('', __("Stock Levels")); // sort by qty diff --git a/erpnext/stock/doctype/batch/test_batch.py b/erpnext/stock/doctype/batch/test_batch.py index 0e4132db8e..7fb672c0cb 100644 --- a/erpnext/stock/doctype/batch/test_batch.py +++ b/erpnext/stock/doctype/batch/test_batch.py @@ -59,6 +59,73 @@ class TestBatch(FrappeTestCase): return receipt + def test_batch_stock_levels(self, batch_qty=100): + """Test automated batch creation from Purchase Receipt""" + self.make_batch_item("ITEM-BATCH-1") + + receipt = frappe.get_doc( + dict( + doctype="Purchase Receipt", + supplier="_Test Supplier", + company="_Test Company", + items=[dict(item_code="ITEM-BATCH-1", qty=10, rate=10, warehouse="Stores - _TC")], + ) + ).insert() + receipt.submit() + + receipt.load_from_db() + batch_no = get_batch_from_bundle(receipt.items[0].serial_and_batch_bundle) + + bundle_id = ( + SerialBatchCreation( + { + "item_code": "ITEM-BATCH-1", + "warehouse": "_Test Warehouse - _TC", + "actual_qty": 20, + "voucher_type": "Purchase Receipt", + "batches": frappe._dict({batch_no: 20}), + "type_of_transaction": "Inward", + "company": receipt.company, + } + ) + .make_serial_and_batch_bundle() + .name + ) + + receipt2 = frappe.get_doc( + dict( + doctype="Purchase Receipt", + supplier="_Test Supplier", + company="_Test Company", + items=[ + dict( + item_code="ITEM-BATCH-1", + qty=20, + rate=10, + warehouse="_Test Warehouse - _TC", + serial_and_batch_bundle=bundle_id, + ) + ], + ) + ).insert() + receipt2.submit() + + receipt.load_from_db() + receipt2.load_from_db() + + self.assertTrue(receipt.items[0].serial_and_batch_bundle) + self.assertTrue(receipt2.items[0].serial_and_batch_bundle) + + batchwise_qty = frappe._dict({}) + for receipt in [receipt, receipt2]: + batch_no = get_batch_from_bundle(receipt.items[0].serial_and_batch_bundle) + key = (batch_no, receipt.items[0].warehouse) + batchwise_qty[key] = receipt.items[0].qty + + batches = get_batch_qty(batch_no) + for d in batches: + self.assertEqual(d.qty, batchwise_qty[(d.batch_no, d.warehouse)]) + def test_stock_entry_incoming(self): """Test batch creation via Stock Entry (Work Order)""" diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py index 8baae8a19c..0ef3027bce 100644 --- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py +++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py @@ -318,6 +318,37 @@ class TestDeliveryNote(FrappeTestCase): self.assertEqual(dn.per_returned, 100) self.assertEqual(dn.status, "Return Issued") + def test_delivery_note_return_valuation_on_different_warehuose(self): + from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse + + company = frappe.db.get_value("Warehouse", "Stores - TCP1", "company") + item_code = "Test Return Valuation For DN" + make_item("Test Return Valuation For DN", {"is_stock_item": 1}) + return_warehouse = create_warehouse("Returned Test Warehouse", company=company) + + make_stock_entry(item_code=item_code, target="Stores - TCP1", qty=5, basic_rate=150) + + dn = create_delivery_note( + item_code=item_code, + qty=5, + rate=500, + warehouse="Stores - TCP1", + company=company, + expense_account="Cost of Goods Sold - TCP1", + cost_center="Main - TCP1", + ) + + dn.submit() + self.assertEqual(dn.items[0].incoming_rate, 150) + + from erpnext.controllers.sales_and_purchase_return import make_return_doc + + return_dn = make_return_doc(dn.doctype, dn.name) + return_dn.items[0].warehouse = return_warehouse + return_dn.save().submit() + + self.assertEqual(return_dn.items[0].incoming_rate, 150) + def test_return_single_item_from_bundled_items(self): company = frappe.db.get_value("Warehouse", "Stores - TCP1", "company") diff --git a/erpnext/stock/doctype/item/item.json b/erpnext/stock/doctype/item/item.json index 34adbebc07..87c2a7ea69 100644 --- a/erpnext/stock/doctype/item/item.json +++ b/erpnext/stock/doctype/item/item.json @@ -194,7 +194,8 @@ "default": "0", "fieldname": "disabled", "fieldtype": "Check", - "label": "Disabled" + "label": "Disabled", + "search_index": 1 }, { "default": "0", @@ -911,7 +912,7 @@ "index_web_pages_for_search": 1, "links": [], "make_attachments_public": 1, - "modified": "2023-02-14 04:48:26.343620", + "modified": "2023-07-14 17:18:18.658942", "modified_by": "Administrator", "module": "Stock", "name": "Item", diff --git a/erpnext/stock/doctype/item/item.py b/erpnext/stock/doctype/item/item.py index 93d799a395..ef4155e48a 100644 --- a/erpnext/stock/doctype/item/item.py +++ b/erpnext/stock/doctype/item/item.py @@ -773,7 +773,7 @@ class Item(Document): rows = "" for docname, attr_list in not_included.items(): - link = "{0}".format(frappe.bold(_(docname))) + link = f"{frappe.bold(docname)}" rows += table_row(link, body(attr_list)) error_description = _( diff --git a/erpnext/stock/doctype/item_variant_attribute/item_variant_attribute.json b/erpnext/stock/doctype/item_variant_attribute/item_variant_attribute.json index 6d02ea9db0..9699ecbb3d 100644 --- a/erpnext/stock/doctype/item_variant_attribute/item_variant_attribute.json +++ b/erpnext/stock/doctype/item_variant_attribute/item_variant_attribute.json @@ -1,370 +1,90 @@ { - "allow_copy": 0, - "allow_events_in_timeline": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "autoname": "", - "beta": 0, - "creation": "2015-05-19 05:12:30.344797", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "Other", - "editable_grid": 1, + "actions": [], + "creation": "2015-05-19 05:12:30.344797", + "doctype": "DocType", + "document_type": "Other", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "variant_of", + "attribute", + "column_break_2", + "attribute_value", + "numeric_values", + "section_break_4", + "from_range", + "increment", + "column_break_8", + "to_range" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "variant_of", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Variant Of", - "length": 0, - "no_copy": 0, - "options": "Item", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "variant_of", + "fieldtype": "Link", + "label": "Variant Of", + "options": "Item", + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "attribute", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Attribute", - "length": 0, - "no_copy": 0, - "options": "Item Attribute", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "attribute", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Attribute", + "options": "Item Attribute", + "reqd": 1, + "search_index": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_2", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_2", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "attribute_value", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Attribute Value", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "attribute_value", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Attribute Value" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "has_variants", - "fieldname": "numeric_values", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Numeric Values", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "default": "0", + "depends_on": "has_variants", + "fieldname": "numeric_values", + "fieldtype": "Check", + "label": "Numeric Values" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "numeric_values", - "fieldname": "section_break_4", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "depends_on": "numeric_values", + "fieldname": "section_break_4", + "fieldtype": "Section Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "from_range", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "From Range", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "from_range", + "fieldtype": "Float", + "label": "From Range" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "increment", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Increment", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "increment", + "fieldtype": "Float", + "label": "Increment" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_8", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "fieldname": "column_break_8", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "to_range", - "fieldtype": "Float", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "To Range", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "to_range", + "fieldtype": "Float", + "label": "To Range" } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "icon": "", - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2019-01-03 15:36:59.129006", - "modified_by": "Administrator", - "module": "Stock", - "name": "Item Variant Attribute", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0, - "track_views": 0 + ], + "istable": 1, + "links": [], + "modified": "2023-07-14 17:15:19.112119", + "modified_by": "Administrator", + "module": "Stock", + "name": "Item Variant Attribute", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] } \ No newline at end of file diff --git a/erpnext/stock/doctype/manufacturer/manufacturer.js b/erpnext/stock/doctype/manufacturer/manufacturer.js index bb7e314e14..5b4990f08b 100644 --- a/erpnext/stock/doctype/manufacturer/manufacturer.js +++ b/erpnext/stock/doctype/manufacturer/manufacturer.js @@ -3,7 +3,6 @@ frappe.ui.form.on('Manufacturer', { refresh: function(frm) { - frappe.dynamic_link = { doc: frm.doc, fieldname: 'name', doctype: 'Manufacturer' }; if (frm.doc.__islocal) { hide_field(['address_html','contact_html']); frappe.contacts.clear_address_and_contact(frm); diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json index b41e971c8a..912b9086dd 100755 --- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json +++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.json @@ -438,6 +438,7 @@ { "fieldname": "rejected_warehouse", "fieldtype": "Link", + "ignore_user_permissions": 1, "label": "Rejected Warehouse", "no_copy": 1, "oldfieldname": "rejected_warehouse", @@ -1240,7 +1241,7 @@ "idx": 261, "is_submittable": 1, "links": [], - "modified": "2023-06-03 16:23:20.781368", + "modified": "2023-07-04 17:23:17.025390", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt", diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py index 07d6e86795..6134bfa1f2 100644 --- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py +++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py @@ -350,6 +350,15 @@ class TestPurchaseReceipt(FrappeTestCase): pr.cancel() self.assertFalse(frappe.db.get_value("Serial No", pr_row_1_serial_no, "warehouse")) + def test_rejected_warehouse_filter(self): + pr = frappe.copy_doc(test_records[0]) + pr.get("items")[0].item_code = "_Test Serialized Item With Series" + pr.get("items")[0].qty = 3 + pr.get("items")[0].rejected_qty = 2 + pr.get("items")[0].received_qty = 5 + pr.get("items")[0].rejected_warehouse = pr.get("items")[0].warehouse + self.assertRaises(frappe.ValidationError, pr.save) + def test_rejected_serial_no(self): pr = frappe.copy_doc(test_records[0]) pr.get("items")[0].item_code = "_Test Serialized Item With Series" @@ -1956,6 +1965,32 @@ class TestPurchaseReceipt(FrappeTestCase): ste5.reload() self.assertEqual(ste5.items[0].valuation_rate, 275.00) + ste6 = make_stock_entry( + purpose="Material Transfer", + posting_date=add_days(today(), -3), + source=warehouse1, + target=warehouse, + item_code=item_code, + qty=20, + company=pr.company, + ) + + ste6.reload() + self.assertEqual(ste6.items[0].valuation_rate, 275.00) + + ste7 = make_stock_entry( + purpose="Material Transfer", + posting_date=add_days(today(), -3), + source=warehouse, + target=warehouse1, + item_code=item_code, + qty=20, + company=pr.company, + ) + + ste7.reload() + self.assertEqual(ste7.items[0].valuation_rate, 275.00) + create_landed_cost_voucher("Purchase Receipt", pr.name, pr.company, charges=2500 * -1) pr.reload() @@ -1976,6 +2011,12 @@ class TestPurchaseReceipt(FrappeTestCase): ste5.reload() self.assertEqual(ste5.items[0].valuation_rate, valuation_rate) + ste6.reload() + self.assertEqual(ste6.items[0].valuation_rate, valuation_rate) + + ste7.reload() + self.assertEqual(ste7.items[0].valuation_rate, valuation_rate) + def prepare_data_for_internal_transfer(): from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json index 3929616f7c..bc5e8a0f37 100644 --- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json +++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json @@ -502,6 +502,7 @@ { "fieldname": "rejected_warehouse", "fieldtype": "Link", + "ignore_user_permissions": 1, "label": "Rejected Warehouse", "no_copy": 1, "oldfieldname": "rejected_warehouse", @@ -1058,7 +1059,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2023-07-02 18:40:48.152637", + "modified": "2023-07-04 17:22:02.830029", "modified_by": "Administrator", "module": "Stock", "name": "Purchase Receipt Item", diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.json b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.json index 6955c761e1..c5b96ff0fe 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.json +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.json @@ -193,7 +193,7 @@ "fieldname": "naming_series", "fieldtype": "Select", "label": "Naming Series", - "options": "SBB-.####" + "options": "SABB-.########" }, { "default": "0", @@ -244,7 +244,7 @@ "index_web_pages_for_search": 1, "is_submittable": 1, "links": [], - "modified": "2023-04-10 20:02:42.964309", + "modified": "2023-07-16 10:53:04.045605", "modified_by": "Administrator", "module": "Stock", "name": "Serial and Batch Bundle", diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py index 75b6ec7ef8..43bd7ac78c 100644 --- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py +++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py @@ -889,13 +889,16 @@ def item_query(doctype, txt, searchfield, start, page_len, filters, as_dict=Fals @frappe.whitelist() -def get_serial_batch_ledgers(item_code, docstatus=None, voucher_no=None, name=None): - filters = get_filters_for_bundle(item_code, docstatus=docstatus, voucher_no=voucher_no, name=name) +def get_serial_batch_ledgers(item_code=None, docstatus=None, voucher_no=None, name=None): + filters = get_filters_for_bundle( + item_code=item_code, docstatus=docstatus, voucher_no=voucher_no, name=name + ) return frappe.get_all( "Serial and Batch Bundle", fields=[ "`tabSerial and Batch Bundle`.`name`", + "`tabSerial and Batch Bundle`.`item_code`", "`tabSerial and Batch Entry`.`qty`", "`tabSerial and Batch Entry`.`warehouse`", "`tabSerial and Batch Entry`.`batch_no`", @@ -906,12 +909,14 @@ def get_serial_batch_ledgers(item_code, docstatus=None, voucher_no=None, name=No ) -def get_filters_for_bundle(item_code, docstatus=None, voucher_no=None, name=None): +def get_filters_for_bundle(item_code=None, docstatus=None, voucher_no=None, name=None): filters = [ - ["Serial and Batch Bundle", "item_code", "=", item_code], ["Serial and Batch Bundle", "is_cancelled", "=", 0], ] + if item_code: + filters.append(["Serial and Batch Bundle", "item_code", "=", item_code]) + if not docstatus: docstatus = [0, 1] @@ -1272,24 +1277,29 @@ def get_reserved_batches_for_pos(kwargs): if ids: for d in get_serial_batch_ledgers(kwargs.item_code, docstatus=1, name=ids): - if d.batch_no not in pos_batches: - pos_batches[d.batch_no] = frappe._dict( + key = (d.batch_no, d.warehouse) + if key not in pos_batches: + pos_batches[key] = frappe._dict( { "qty": d.qty, "warehouse": d.warehouse, } ) else: - pos_batches[d.batch_no].qty += d.qty + pos_batches[key].qty += d.qty for row in pos_invoices: if not row.batch_no: continue - if row.batch_no in pos_batches: - pos_batches[row.batch_no] -= row.qty * -1 if row.is_return else row.qty + if kwargs.get("batch_no") and row.batch_no != kwargs.get("batch_no"): + continue + + key = (row.batch_no, row.warehouse) + if key in pos_batches: + pos_batches[key] -= row.qty * -1 if row.is_return else row.qty else: - pos_batches[row.batch_no] = frappe._dict( + pos_batches[key] = frappe._dict( { "qty": (row.qty * -1 if row.is_return else row.qty), "warehouse": row.warehouse, @@ -1309,6 +1319,7 @@ def get_auto_batch_nos(kwargs): update_available_batches(available_batches, stock_ledgers_batches, pos_invoice_batches) available_batches = list(filter(lambda x: x.qty > 0, available_batches)) + if not qty: return available_batches @@ -1351,10 +1362,11 @@ def get_qty_based_available_batches(available_batches, qty): def update_available_batches(available_batches, reserved_batches=None, pos_invoice_batches=None): for batches in [reserved_batches, pos_invoice_batches]: if batches: - for batch_no, data in batches.items(): + for key, data in batches.items(): + batch_no, warehouse = key batch_not_exists = True for batch in available_batches: - if batch.batch_no == batch_no and batch.warehouse == data.warehouse: + if batch.batch_no == batch_no and batch.warehouse == warehouse: batch.qty += data.qty batch_not_exists = False @@ -1563,7 +1575,7 @@ def get_stock_ledgers_batches(kwargs): .groupby(stock_ledger_entry.batch_no, stock_ledger_entry.warehouse) ) - for field in ["warehouse", "item_code"]: + for field in ["warehouse", "item_code", "batch_no"]: if not kwargs.get(field): continue @@ -1582,6 +1594,10 @@ def get_stock_ledgers_batches(kwargs): data = query.run(as_dict=True) batches = {} for d in data: - batches[d.batch_no] = d + key = (d.batch_no, d.warehouse) + if key not in batches: + batches[key] = d + else: + batches[key].qty += d.qty return batches diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js index 403e04ae60..3e83fafcad 100644 --- a/erpnext/stock/doctype/stock_entry/stock_entry.js +++ b/erpnext/stock/doctype/stock_entry/stock_entry.js @@ -925,6 +925,7 @@ erpnext.stock.StockEntry = class StockEntry extends erpnext.stock.StockControlle this.toggle_related_fields(this.frm.doc); this.toggle_enable_bom(); this.show_stock_ledger(); + erpnext.utils.view_serial_batch_nos(this.frm); if (this.frm.doc.docstatus===1 && erpnext.is_perpetual_inventory_enabled(this.frm.doc.company)) { this.show_general_ledger(); } diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js index 0664c2929c..cb2adf1682 100644 --- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js +++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.js @@ -337,6 +337,7 @@ erpnext.stock.StockReconciliation = class StockReconciliation extends erpnext.st refresh() { if(this.frm.doc.docstatus > 0) { this.show_stock_ledger(); + erpnext.utils.view_serial_batch_nos(this.frm); if (erpnext.is_perpetual_inventory_enabled(this.frm.doc.company)) { this.show_general_ledger(); } diff --git a/erpnext/stock/doctype/warehouse/warehouse.js b/erpnext/stock/doctype/warehouse/warehouse.js index 746a1cbaf1..3819c0b24a 100644 --- a/erpnext/stock/doctype/warehouse/warehouse.js +++ b/erpnext/stock/doctype/warehouse/warehouse.js @@ -83,12 +83,6 @@ frappe.ui.form.on("Warehouse", { } frm.toggle_enable(["is_group", "company"], false); - - frappe.dynamic_link = { - doc: frm.doc, - fieldname: "name", - doctype: "Warehouse", - }; }, }); diff --git a/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.js b/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.js index a7d7149c38..48a72a2bfe 100644 --- a/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.js +++ b/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.js @@ -9,13 +9,27 @@ frappe.query_reports["Batch Item Expiry Status"] = { "fieldtype": "Date", "width": "80", "default": frappe.sys_defaults.year_start_date, + "reqd": 1, }, { "fieldname":"to_date", "label": __("To Date"), "fieldtype": "Date", "width": "80", - "default": frappe.datetime.get_today() + "default": frappe.datetime.get_today(), + "reqd": 1, + }, + { + "fieldname":"item", + "label": __("Item"), + "fieldtype": "Link", + "options": "Item", + "width": "100", + "get_query": function () { + return { + filters: {"has_batch_no": 1} + } + } } ] } diff --git a/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py b/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py index ef7d6e6816..5661e8b260 100644 --- a/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py +++ b/erpnext/stock/report/batch_item_expiry_status/batch_item_expiry_status.py @@ -4,113 +4,86 @@ import frappe from frappe import _ -from frappe.query_builder.functions import IfNull -from frappe.utils import cint, getdate +from frappe.query_builder.functions import Date def execute(filters=None): - if not filters: - filters = {} + validate_filters(filters) - float_precision = cint(frappe.db.get_default("float_precision")) or 3 - - columns = get_columns(filters) - item_map = get_item_details(filters) - iwb_map = get_item_warehouse_batch_map(filters, float_precision) - - data = [] - for item in sorted(iwb_map): - for wh in sorted(iwb_map[item]): - for batch in sorted(iwb_map[item][wh]): - qty_dict = iwb_map[item][wh][batch] - - data.append( - [ - item, - item_map[item]["item_name"], - item_map[item]["description"], - wh, - batch, - frappe.db.get_value("Batch", batch, "expiry_date"), - qty_dict.expiry_status, - ] - ) + columns = get_columns() + data = get_data(filters) return columns, data -def get_columns(filters): - """return columns based on filters""" +def validate_filters(filters): + if not filters: + frappe.throw(_("Please select the required filters")) - columns = ( - [_("Item") + ":Link/Item:100"] - + [_("Item Name") + "::150"] - + [_("Description") + "::150"] - + [_("Warehouse") + ":Link/Warehouse:100"] - + [_("Batch") + ":Link/Batch:100"] - + [_("Expires On") + ":Date:90"] - + [_("Expiry (In Days)") + ":Int:120"] - ) - - return columns - - -def get_stock_ledger_entries(filters): if not filters.get("from_date"): frappe.throw(_("'From Date' is required")) if not filters.get("to_date"): frappe.throw(_("'To Date' is required")) - sle = frappe.qb.DocType("Stock Ledger Entry") - query = ( - frappe.qb.from_(sle) - .select(sle.item_code, sle.batch_no, sle.warehouse, sle.posting_date, sle.actual_qty) - .where( - (sle.is_cancelled == 0) - & (sle.docstatus < 2) - & (IfNull(sle.batch_no, "") != "") - & (sle.posting_date <= filters["to_date"]) - ) - .orderby(sle.item_code, sle.warehouse) + +def get_columns(): + return ( + [_("Item") + ":Link/Item:150"] + + [_("Item Name") + "::150"] + + [_("Batch") + ":Link/Batch:150"] + + [_("Stock UOM") + ":Link/UOM:100"] + + [_("Quantity") + ":Float:100"] + + [_("Expires On") + ":Date:100"] + + [_("Expiry (In Days)") + ":Int:130"] ) - return query.run(as_dict=True) +def get_data(filters): + data = [] -def get_item_warehouse_batch_map(filters, float_precision): - sle = get_stock_ledger_entries(filters) - iwb_map = {} - - from_date = getdate(filters["from_date"]) - to_date = getdate(filters["to_date"]) - - for d in sle: - iwb_map.setdefault(d.item_code, {}).setdefault(d.warehouse, {}).setdefault( - d.batch_no, frappe._dict({"expires_on": None, "expiry_status": None}) + for batch in get_batch_details(filters): + data.append( + [ + batch.item, + batch.item_name, + batch.name, + batch.stock_uom, + batch.batch_qty, + batch.expiry_date, + max((batch.expiry_date - frappe.utils.datetime.date.today()).days, 0) + if batch.expiry_date + else None, + ] ) - qty_dict = iwb_map[d.item_code][d.warehouse][d.batch_no] - - expiry_date_unicode = frappe.db.get_value("Batch", d.batch_no, "expiry_date") - qty_dict.expires_on = expiry_date_unicode - - exp_date = frappe.utils.data.getdate(expiry_date_unicode) - qty_dict.expires_on = exp_date - - expires_in_days = (exp_date - frappe.utils.datetime.date.today()).days - - if expires_in_days > 0: - qty_dict.expiry_status = expires_in_days - else: - qty_dict.expiry_status = 0 - - return iwb_map + return data -def get_item_details(filters): - item_map = {} - for d in (frappe.qb.from_("Item").select("name", "item_name", "description")).run(as_dict=True): - item_map.setdefault(d.name, d) +def get_batch_details(filters): + batch = frappe.qb.DocType("Batch") + query = ( + frappe.qb.from_(batch) + .select( + batch.name, + batch.creation, + batch.expiry_date, + batch.item, + batch.item_name, + batch.stock_uom, + batch.batch_qty, + ) + .where( + (batch.disabled == 0) + & (batch.batch_qty > 0) + & ( + (Date(batch.creation) >= filters["from_date"]) & (Date(batch.creation) <= filters["to_date"]) + ) + ) + .orderby(batch.creation) + ) - return item_map + if filters.get("item"): + query = query.where(batch.item == filters["item"]) + + return query.run(as_dict=True) diff --git a/erpnext/erpnext_integrations/doctype/exotel_settings/__init__.py b/erpnext/stock/report/serial_and_batch_summary/__init__.py similarity index 100% rename from erpnext/erpnext_integrations/doctype/exotel_settings/__init__.py rename to erpnext/stock/report/serial_and_batch_summary/__init__.py diff --git a/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.js b/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.js new file mode 100644 index 0000000000..10e5925ff4 --- /dev/null +++ b/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.js @@ -0,0 +1,95 @@ +// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors +// For license information, please see license.txt +/* eslint-disable */ + +frappe.query_reports["Serial and Batch Summary"] = { + "filters": [ + { + "fieldname":"company", + "label": __("Company"), + "fieldtype": "Link", + "options": "Company", + "default": frappe.defaults.get_user_default("Company"), + }, + { + "fieldname":"from_date", + "label": __("From Date"), + "fieldtype": "Date", + "default": frappe.datetime.add_months(frappe.datetime.get_today(), -1), + }, + { + "fieldname":"to_date", + "label": __("To Date"), + "fieldtype": "Date", + "default": frappe.datetime.get_today() + }, + { + "fieldname":"item_code", + "label": __("Item"), + "fieldtype": "Link", + "options": "Item", + }, + { + "fieldname":"warehouse", + "label": __("Warehouse"), + "fieldtype": "Link", + "options": "Warehouse", + }, + { + "fieldname":"voucher_type", + "label": __("Voucher Type"), + "fieldtype": "Link", + "options": "DocType", + get_query: function() { + return { + query: "erpnext.stock.report.serial_and_batch_summary.serial_and_batch_summary.get_voucher_type", + } + } + }, + { + "fieldname":"voucher_no", + "label": __("Voucher No"), + "fieldtype": "MultiSelectList", + get_data: function(txt) { + if (!frappe.query_report.filters) return; + + let voucher_type = frappe.query_report.get_filter_value('voucher_type'); + if (!voucher_type) return; + + return frappe.db.get_link_options(voucher_type, txt); + }, + }, + { + "fieldname":"serial_no", + "label": __("Serial No"), + "fieldtype": "Link", + "options": "Serial No", + get_query: function() { + return { + query: "erpnext.stock.report.serial_and_batch_summary.serial_and_batch_summary.get_serial_nos", + filters: { + "item_code": frappe.query_report.get_filter_value('item_code'), + "voucher_type": frappe.query_report.get_filter_value('voucher_type'), + "voucher_no": frappe.query_report.get_filter_value('voucher_no'), + } + } + } + }, + { + "fieldname":"batch_no", + "label": __("Batch No"), + "fieldtype": "Link", + "options": "Batch", + get_query: function() { + return { + query: "erpnext.stock.report.serial_and_batch_summary.serial_and_batch_summary.get_batch_nos", + filters: { + "item_code": frappe.query_report.get_filter_value('item_code'), + "voucher_type": frappe.query_report.get_filter_value('voucher_type'), + "voucher_no": frappe.query_report.get_filter_value('voucher_no'), + } + } + } + } + ] +}; diff --git a/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.json b/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.json new file mode 100644 index 0000000000..7511e3a198 --- /dev/null +++ b/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.json @@ -0,0 +1,38 @@ +{ + "add_total_row": 0, + "columns": [], + "creation": "2023-07-13 16:53:27.735091", + "disabled": 0, + "docstatus": 0, + "doctype": "Report", + "filters": [], + "idx": 0, + "is_standard": "Yes", + "json": "{}", + "modified": "2023-07-13 16:53:33.204591", + "modified_by": "Administrator", + "module": "Stock", + "name": "Serial and Batch Summary", + "owner": "Administrator", + "prepared_report": 0, + "ref_doctype": "Serial and Batch Bundle", + "report_name": "Serial and Batch Summary", + "report_type": "Script Report", + "roles": [ + { + "role": "System Manager" + }, + { + "role": "Sales User" + }, + { + "role": "Purchase User" + }, + { + "role": "Stock User" + }, + { + "role": "Maintenance User" + } + ] +} \ No newline at end of file diff --git a/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.py b/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.py new file mode 100644 index 0000000000..3ea5e8278d --- /dev/null +++ b/erpnext/stock/report/serial_and_batch_summary/serial_and_batch_summary.py @@ -0,0 +1,245 @@ +# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +import frappe +from frappe import _ + + +def execute(filters=None): + data = get_data(filters) + columns = get_columns(filters, data) + + return columns, data + + +def get_data(filters): + filter_conditions = get_filter_conditions(filters) + + return frappe.get_all( + "Serial and Batch Bundle", + fields=[ + "`tabSerial and Batch Bundle`.`voucher_type`", + "`tabSerial and Batch Bundle`.`posting_date`", + "`tabSerial and Batch Bundle`.`name`", + "`tabSerial and Batch Bundle`.`company`", + "`tabSerial and Batch Bundle`.`voucher_no`", + "`tabSerial and Batch Bundle`.`item_code`", + "`tabSerial and Batch Bundle`.`item_name`", + "`tabSerial and Batch Entry`.`serial_no`", + "`tabSerial and Batch Entry`.`batch_no`", + "`tabSerial and Batch Entry`.`warehouse`", + "`tabSerial and Batch Entry`.`incoming_rate`", + "`tabSerial and Batch Entry`.`stock_value_difference`", + "`tabSerial and Batch Entry`.`qty`", + ], + filters=filter_conditions, + order_by="posting_date", + ) + + +def get_filter_conditions(filters): + filter_conditions = [ + ["Serial and Batch Bundle", "docstatus", "=", 1], + ["Serial and Batch Bundle", "is_cancelled", "=", 0], + ] + + for field in ["voucher_type", "voucher_no", "item_code", "warehouse", "company"]: + if filters.get(field): + if field == "voucher_no": + filter_conditions.append(["Serial and Batch Bundle", field, "in", filters.get(field)]) + else: + filter_conditions.append(["Serial and Batch Bundle", field, "=", filters.get(field)]) + + if filters.get("from_date") and filters.get("to_date"): + filter_conditions.append( + [ + "Serial and Batch Bundle", + "posting_date", + "between", + [filters.get("from_date"), filters.get("to_date")], + ] + ) + + for field in ["serial_no", "batch_no"]: + if filters.get(field): + filter_conditions.append(["Serial and Batch Entry", field, "=", filters.get(field)]) + + return filter_conditions + + +def get_columns(filters, data): + columns = [ + { + "label": _("Company"), + "fieldname": "company", + "fieldtype": "Link", + "options": "Company", + "width": 120, + }, + { + "label": _("Serial and Batch Bundle"), + "fieldname": "name", + "fieldtype": "Link", + "options": "Serial and Batch Bundle", + "width": 110, + }, + {"label": _("Posting Date"), "fieldname": "posting_date", "fieldtype": "Date", "width": 100}, + ] + + item_details = {} + + item_codes = [] + if filters.get("voucher_type"): + item_codes = [d.item_code for d in data] + + if filters.get("item_code") or (item_codes and len(list(set(item_codes))) == 1): + item_details = frappe.get_cached_value( + "Item", + filters.get("item_code") or item_codes[0], + ["has_serial_no", "has_batch_no"], + as_dict=True, + ) + + if not filters.get("voucher_no"): + columns.extend( + [ + { + "label": _("Voucher Type"), + "fieldname": "voucher_type", + "fieldtype": "Link", + "options": "DocType", + "width": 120, + }, + { + "label": _("Voucher No"), + "fieldname": "voucher_no", + "fieldtype": "Dynamic Link", + "options": "voucher_type", + "width": 160, + }, + ] + ) + + if not filters.get("item_code"): + columns.extend( + [ + { + "label": _("Item Code"), + "fieldname": "item_code", + "fieldtype": "Link", + "options": "Item", + "width": 120, + }, + {"label": _("Item Name"), "fieldname": "item_name", "fieldtype": "Data", "width": 120}, + ] + ) + + if not filters.get("warehouse"): + columns.append( + { + "label": _("Warehouse"), + "fieldname": "warehouse", + "fieldtype": "Link", + "options": "Warehouse", + "width": 120, + } + ) + + if not item_details or item_details.get("has_serial_no"): + columns.append( + {"label": _("Serial No"), "fieldname": "serial_no", "fieldtype": "Data", "width": 120} + ) + + if not item_details or item_details.get("has_batch_no"): + columns.extend( + [ + {"label": _("Batch No"), "fieldname": "batch_no", "fieldtype": "Data", "width": 120}, + {"label": _("Batch Qty"), "fieldname": "qty", "fieldtype": "Float", "width": 120}, + ] + ) + + columns.extend( + [ + {"label": _("Incoming Rate"), "fieldname": "incoming_rate", "fieldtype": "Float", "width": 120}, + { + "label": _("Change in Stock Value"), + "fieldname": "stock_value_difference", + "fieldtype": "Float", + "width": 120, + }, + ] + ) + + return columns + + +@frappe.whitelist() +@frappe.validate_and_sanitize_search_inputs +def get_voucher_type(doctype, txt, searchfield, start, page_len, filters): + child_doctypes = frappe.get_all( + "DocField", + filters={"fieldname": "serial_and_batch_bundle"}, + fields=["distinct parent as parent"], + ) + + query_filters = {"options": ["in", [d.parent for d in child_doctypes]]} + if txt: + query_filters["parent"] = ["like", "%{}%".format(txt)] + + return frappe.get_all("DocField", filters=query_filters, fields=["distinct parent"], as_list=True) + + +@frappe.whitelist() +@frappe.validate_and_sanitize_search_inputs +def get_serial_nos(doctype, txt, searchfield, start, page_len, filters): + query_filters = {} + + if txt: + query_filters["serial_no"] = ["like", f"%{txt}%"] + + if filters.get("voucher_no"): + serial_batch_bundle = frappe.get_cached_value( + "Serial and Batch Bundle", + {"voucher_no": ("in", filters.get("voucher_no")), "docstatus": 1, "is_cancelled": 0}, + "name", + ) + + query_filters["parent"] = serial_batch_bundle + if not txt: + query_filters["serial_no"] = ("is", "set") + + return frappe.get_all( + "Serial and Batch Entry", filters=query_filters, fields=["serial_no"], as_list=True + ) + + else: + query_filters["item_code"] = filters.get("item_code") + return frappe.get_all("Serial No", filters=query_filters, as_list=True) + + +@frappe.whitelist() +@frappe.validate_and_sanitize_search_inputs +def get_batch_nos(doctype, txt, searchfield, start, page_len, filters): + query_filters = {} + + if txt: + query_filters["batch_no"] = ["like", f"%{txt}%"] + + if filters.get("voucher_no"): + serial_batch_bundle = frappe.get_cached_value( + "Serial and Batch Bundle", + {"voucher_no": ("in", filters.get("voucher_no")), "docstatus": 1, "is_cancelled": 0}, + "name", + ) + + query_filters["parent"] = serial_batch_bundle + if not txt: + query_filters["batch_no"] = ("is", "set") + + return frappe.get_all( + "Serial and Batch Entry", filters=query_filters, fields=["batch_no"], as_list=True + ) + + else: + query_filters["item"] = filters.get("item_code") + return frappe.get_all("Batch", filters=query_filters, as_list=True) diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py index 7b1eae5545..5abb8e827f 100644 --- a/erpnext/stock/stock_ledger.py +++ b/erpnext/stock/stock_ledger.py @@ -645,7 +645,7 @@ class update_entries_after(object): def update_distinct_item_warehouses(self, dependant_sle): key = (dependant_sle.item_code, dependant_sle.warehouse) - val = frappe._dict({"sle": dependant_sle}) + val = frappe._dict({"sle": dependant_sle, "dependent_voucher_detail_nos": []}) if key not in self.distinct_item_warehouses: self.distinct_item_warehouses[key] = val @@ -654,13 +654,26 @@ class update_entries_after(object): existing_sle_posting_date = ( self.distinct_item_warehouses[key].get("sle", {}).get("posting_date") ) + + dependent_voucher_detail_nos = self.get_dependent_voucher_detail_nos(key) + if getdate(dependant_sle.posting_date) < getdate(existing_sle_posting_date): val.sle_changed = True self.distinct_item_warehouses[key] = val self.new_items_found = True - elif self.distinct_item_warehouses[key].get("reposting_status"): - self.distinct_item_warehouses[key] = val + elif dependant_sle.voucher_detail_no not in set(dependent_voucher_detail_nos): + # Future dependent voucher needs to be repost to get the correct stock value + # If dependent voucher has not reposted, then add it to the list + dependent_voucher_detail_nos.append(dependant_sle.voucher_detail_no) self.new_items_found = True + val.dependent_voucher_detail_nos = dependent_voucher_detail_nos + self.distinct_item_warehouses[key] = val + + def get_dependent_voucher_detail_nos(self, key): + if "dependent_voucher_detail_nos" not in self.distinct_item_warehouses[key]: + self.distinct_item_warehouses[key].dependent_voucher_detail_nos = [] + + return self.distinct_item_warehouses[key].dependent_voucher_detail_nos def process_sle(self, sle): # previous sle data for this warehouse @@ -1370,6 +1383,7 @@ def get_sle_by_voucher_detail_no(voucher_detail_no, excluded_sle=None): "qty_after_transaction", "posting_date", "posting_time", + "voucher_detail_no", "timestamp(posting_date, posting_time) as timestamp", ], as_dict=1, diff --git a/erpnext/stock/workspace/stock/stock.json b/erpnext/stock/workspace/stock/stock.json index 502afde2f4..1a683e2b3d 100644 --- a/erpnext/stock/workspace/stock/stock.json +++ b/erpnext/stock/workspace/stock/stock.json @@ -5,7 +5,7 @@ "label": "Warehouse wise Stock Value" } ], - "content": "[{\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Stock\",\"col\":12}},{\"type\":\"chart\",\"data\":{\"chart_name\":\"Warehouse wise Stock Value\",\"col\":12}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Quick Access\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Item\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Material Request\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Stock Entry\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Purchase Receipt\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Delivery Note\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Stock Ledger\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Stock Balance\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"Masters & Reports\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Items and Pricing\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Stock Transactions\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Stock Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Serial No and Batch\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Tools\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Key Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Other Reports\",\"col\":4}}]", + "content": "[{\"id\":\"BJTnTemGjc\",\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Stock\",\"col\":12}},{\"id\":\"WKeeHLcyXI\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"Total Stock Value\",\"col\":4}},{\"id\":\"6nVoOHuy5w\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"Total Warehouses\",\"col\":4}},{\"id\":\"OUex5VED7d\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"Total Active Items\",\"col\":4}},{\"id\":\"A3svBa974t\",\"type\":\"chart\",\"data\":{\"chart_name\":\"Warehouse wise Stock Value\",\"col\":12}},{\"id\":\"wwAoBx30p3\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"LkqrpJHM9X\",\"type\":\"header\",\"data\":{\"text\":\"Quick Access\",\"col\":12}},{\"id\":\"OR8PYiYspy\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Item\",\"col\":3}},{\"id\":\"KP1A22WjDl\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Material Request\",\"col\":3}},{\"id\":\"0EYKOrx6U1\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Stock Entry\",\"col\":3}},{\"id\":\"cqotiphmhZ\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Purchase Receipt\",\"col\":3}},{\"id\":\"Xhjqnm-JxZ\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Delivery Note\",\"col\":3}},{\"id\":\"yxCx6Tay4Z\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Stock Ledger\",\"col\":3}},{\"id\":\"o3sdEnNy34\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Stock Balance\",\"col\":3}},{\"id\":\"m9O0HUUDS5\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"id\":\"NwWcNC_xNj\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Learn Inventory Management\",\"col\":3}},{\"id\":\"9AmAh9LnPI\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"3SmmwBbOER\",\"type\":\"header\",\"data\":{\"text\":\"Masters & Reports\",\"col\":12}},{\"id\":\"OAGNH9njt7\",\"type\":\"card\",\"data\":{\"card_name\":\"Items Catalogue\",\"col\":4}},{\"id\":\"jF9eKz0qr0\",\"type\":\"card\",\"data\":{\"card_name\":\"Stock Transactions\",\"col\":4}},{\"id\":\"tyTnQo-MIS\",\"type\":\"card\",\"data\":{\"card_name\":\"Stock Reports\",\"col\":4}},{\"id\":\"dJaJw6YNPU\",\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}},{\"id\":\"rQf5vK4N_T\",\"type\":\"card\",\"data\":{\"card_name\":\"Serial No and Batch\",\"col\":4}},{\"id\":\"7oM7hFL4v8\",\"type\":\"card\",\"data\":{\"card_name\":\"Tools\",\"col\":4}},{\"id\":\"ve3L6ZifkB\",\"type\":\"card\",\"data\":{\"card_name\":\"Key Reports\",\"col\":4}},{\"id\":\"8Kfvu3umw7\",\"type\":\"card\",\"data\":{\"card_name\":\"Other Reports\",\"col\":4}}]", "creation": "2020-03-02 15:43:10.096528", "custom_blocks": [], "docstatus": 0, @@ -17,14 +17,6 @@ "is_hidden": 0, "label": "Stock", "links": [ - { - "hidden": 0, - "is_query_report": 0, - "label": "Items and Pricing", - "link_count": 0, - "onboard": 0, - "type": "Card Break" - }, { "dependencies": "", "hidden": 0, @@ -442,113 +434,6 @@ "onboard": 0, "type": "Link" }, - { - "hidden": 0, - "is_query_report": 0, - "label": "Key Reports", - "link_count": 0, - "onboard": 0, - "type": "Card Break" - }, - { - "dependencies": "Item Price", - "hidden": 0, - "is_query_report": 0, - "label": "Item-wise Price List Rate", - "link_count": 0, - "link_to": "Item-wise Price List Rate", - "link_type": "Report", - "onboard": 1, - "type": "Link" - }, - { - "dependencies": "Stock Entry", - "hidden": 0, - "is_query_report": 1, - "label": "Stock Analytics", - "link_count": 0, - "link_to": "Stock Analytics", - "link_type": "Report", - "onboard": 1, - "type": "Link" - }, - { - "dependencies": "Item", - "hidden": 0, - "is_query_report": 1, - "label": "Stock Qty vs Serial No Count", - "link_count": 0, - "link_to": "Stock Qty vs Serial No Count", - "link_type": "Report", - "onboard": 1, - "type": "Link" - }, - { - "dependencies": "Delivery Note", - "hidden": 0, - "is_query_report": 1, - "label": "Delivery Note Trends", - "link_count": 0, - "link_to": "Delivery Note Trends", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Purchase Receipt", - "hidden": 0, - "is_query_report": 1, - "label": "Purchase Receipt Trends", - "link_count": 0, - "link_to": "Purchase Receipt Trends", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Sales Order", - "hidden": 0, - "is_query_report": 1, - "label": "Sales Order Analysis", - "link_count": 0, - "link_to": "Sales Order Analysis", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Purchase Order", - "hidden": 0, - "is_query_report": 1, - "label": "Purchase Order Analysis", - "link_count": 0, - "link_to": "Purchase Order Analysis", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Bin", - "hidden": 0, - "is_query_report": 1, - "label": "Item Shortage Report", - "link_count": 0, - "link_to": "Item Shortage Report", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, - { - "dependencies": "Batch", - "hidden": 0, - "is_query_report": 1, - "label": "Batch-Wise Balance History", - "link_count": 0, - "link_to": "Batch-Wise Balance History", - "link_type": "Report", - "onboard": 0, - "type": "Link" - }, { "hidden": 0, "is_query_report": 0, @@ -717,13 +602,185 @@ "link_type": "Report", "onboard": 0, "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Items Catalogue", + "link_count": 6, + "onboard": 0, + "type": "Card Break" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Item", + "link_count": 0, + "link_to": "Item", + "link_type": "DocType", + "onboard": 1, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Item Group", + "link_count": 0, + "link_to": "Item Group", + "link_type": "DocType", + "onboard": 1, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Product Bundle", + "link_count": 0, + "link_to": "Product Bundle", + "link_type": "DocType", + "onboard": 1, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Shipping Rule", + "link_count": 0, + "link_to": "Shipping Rule", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Item Alternative", + "link_count": 0, + "link_to": "Item Alternative", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "", + "hidden": 0, + "is_query_report": 0, + "label": "Item Manufacturer", + "link_count": 0, + "link_to": "Item Manufacturer", + "link_type": "DocType", + "onboard": 0, + "type": "Link" + }, + { + "hidden": 0, + "is_query_report": 0, + "label": "Key Reports", + "link_count": 7, + "onboard": 0, + "type": "Card Break" + }, + { + "dependencies": "Stock Entry", + "hidden": 0, + "is_query_report": 1, + "label": "Stock Analytics", + "link_count": 0, + "link_to": "Stock Analytics", + "link_type": "Report", + "onboard": 1, + "type": "Link" + }, + { + "dependencies": "Delivery Note", + "hidden": 0, + "is_query_report": 1, + "label": "Delivery Note Trends", + "link_count": 0, + "link_to": "Delivery Note Trends", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "Purchase Receipt", + "hidden": 0, + "is_query_report": 1, + "label": "Purchase Receipt Trends", + "link_count": 0, + "link_to": "Purchase Receipt Trends", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "Sales Order", + "hidden": 0, + "is_query_report": 1, + "label": "Sales Order Analysis", + "link_count": 0, + "link_to": "Sales Order Analysis", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "Purchase Order", + "hidden": 0, + "is_query_report": 1, + "label": "Purchase Order Analysis", + "link_count": 0, + "link_to": "Purchase Order Analysis", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "Bin", + "hidden": 0, + "is_query_report": 1, + "label": "Item Shortage Report", + "link_count": 0, + "link_to": "Item Shortage Report", + "link_type": "Report", + "onboard": 0, + "type": "Link" + }, + { + "dependencies": "Batch", + "hidden": 0, + "is_query_report": 1, + "label": "Batch-Wise Balance History", + "link_count": 0, + "link_to": "Batch-Wise Balance History", + "link_type": "Report", + "onboard": 0, + "type": "Link" } ], - "modified": "2023-05-24 14:47:21.707580", + "modified": "2023-07-04 14:38:14.988756", "modified_by": "Administrator", "module": "Stock", "name": "Stock", - "number_cards": [], + "number_cards": [ + { + "label": "Total Warehouses", + "number_card_name": "Total Warehouses" + }, + { + "label": "Total Stock Value", + "number_card_name": "Total Stock Value" + }, + { + "label": "Total Active Items", + "number_card_name": "Total Active Items" + } + ], "owner": "Administrator", "parent_page": "", "public": 1, @@ -740,6 +797,13 @@ "stats_filter": "{\n \"disabled\" : 0\n}", "type": "DocType" }, + { + "color": "Grey", + "doc_view": "List", + "label": "Learn Inventory Management", + "type": "URL", + "url": "https://frappe.school/courses/inventory-management?utm_source=in_app" + }, { "color": "Yellow", "format": "{} Pending", diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json index 9dee3aae46..4b3cc8365c 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json @@ -251,6 +251,7 @@ "description": "Sets 'Rejected Warehouse' in each row of the Items table.", "fieldname": "rejected_warehouse", "fieldtype": "Link", + "ignore_user_permissions": 1, "label": "Rejected Warehouse", "no_copy": 1, "options": "Warehouse", @@ -630,7 +631,7 @@ "in_create": 1, "is_submittable": 1, "links": [], - "modified": "2023-06-03 16:18:39.088518", + "modified": "2023-07-06 18:43:16.171842", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Receipt", diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py index 4af38e516f..60746d95f3 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py +++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py @@ -192,13 +192,23 @@ class SubcontractingReceipt(SubcontractingController): self.total = total_amount def validate_rejected_warehouse(self): - if not self.rejected_warehouse: - for item in self.items: - if item.rejected_qty: + for item in self.items: + if flt(item.rejected_qty) and not item.rejected_warehouse: + if self.rejected_warehouse: + item.rejected_warehouse = self.rejected_warehouse + + if not item.rejected_warehouse: frappe.throw( - _("Rejected Warehouse is mandatory against rejected Item {0}").format(item.item_code) + _("Row #{0}: Rejected Warehouse is mandatory for the rejected Item {1}").format( + item.idx, item.item_code + ) ) + if item.get("rejected_warehouse") and (item.get("rejected_warehouse") == item.get("warehouse")): + frappe.throw( + _("Row #{0}: Accepted Warehouse and Rejected Warehouse cannot be same").format(item.idx) + ) + def validate_available_qty_for_consumption(self): for item in self.get("supplied_items"): precision = item.precision("consumed_qty") diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json index d550b75839..d72878061c 100644 --- a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json +++ b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json @@ -254,6 +254,7 @@ "depends_on": "eval: !parent.is_return", "fieldname": "rejected_warehouse", "fieldtype": "Link", + "ignore_user_permissions": 1, "label": "Rejected Warehouse", "no_copy": 1, "options": "Warehouse", @@ -494,7 +495,7 @@ "idx": 1, "istable": 1, "links": [], - "modified": "2023-03-12 14:00:41.418681", + "modified": "2023-07-06 18:43:45.599761", "modified_by": "Administrator", "module": "Subcontracting", "name": "Subcontracting Receipt Item", diff --git a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py index 2a078c4395..6c9bc54f7e 100644 --- a/erpnext/support/doctype/service_level_agreement/service_level_agreement.py +++ b/erpnext/support/doctype/service_level_agreement/service_level_agreement.py @@ -21,6 +21,7 @@ from frappe.utils import ( time_diff_in_seconds, to_timedelta, ) +from frappe.utils.caching import redis_cache from frappe.utils.nestedset import get_ancestors_of from frappe.utils.safe_exec import get_safe_globals @@ -209,6 +210,10 @@ class ServiceLevelAgreement(Document): def on_update(self): set_documents_with_active_service_level_agreement() + def clear_cache(self): + get_sla_doctypes.clear_cache() + return super().clear_cache() + def create_docfields(self, meta, service_level_agreement_fields): last_index = len(meta.fields) @@ -990,6 +995,7 @@ def get_user_time(user, to_string=False): @frappe.whitelist() +@redis_cache() def get_sla_doctypes(): doctypes = [] data = frappe.get_all("Service Level Agreement", {"enabled": 1}, ["document_type"], distinct=1) @@ -998,3 +1004,7 @@ def get_sla_doctypes(): doctypes.append(entry.document_type) return doctypes + + +def add_sla_doctypes(bootinfo): + bootinfo.service_level_agreement_doctypes = get_sla_doctypes() diff --git a/erpnext/telephony/doctype/call_log/call_log.py b/erpnext/telephony/doctype/call_log/call_log.py index 7725e71f19..1d6839c1e6 100644 --- a/erpnext/telephony/doctype/call_log/call_log.py +++ b/erpnext/telephony/doctype/call_log/call_log.py @@ -24,12 +24,10 @@ class CallLog(Document): lead_number = self.get("from") if self.is_incoming_call() else self.get("to") lead_number = strip_number(lead_number) - contact = get_contact_with_phone_number(strip_number(lead_number)) - if contact: + if contact := get_contact_with_phone_number(strip_number(lead_number)): self.add_link(link_type="Contact", link_name=contact) - lead = get_lead_with_phone_number(lead_number) - if lead: + if lead := get_lead_with_phone_number(lead_number): self.add_link(link_type="Lead", link_name=lead) # Add Employee Name @@ -70,28 +68,30 @@ class CallLog(Document): self.append("links", {"link_doctype": link_type, "link_name": link_name}) def trigger_call_popup(self): - if self.is_incoming_call(): - scheduled_employees = get_scheduled_employees_for_popup(self.medium) - employees = get_employees_with_number(self.to) - employee_emails = [employee.get("user_id") for employee in employees] + if not self.is_incoming_call(): + return - # check if employees with matched number are scheduled to receive popup - emails = set(scheduled_employees).intersection(employee_emails) + scheduled_employees = get_scheduled_employees_for_popup(self.medium) + employees = get_employees_with_number(self.to) + employee_emails = [employee.get("user_id") for employee in employees] - if frappe.conf.developer_mode: - self.add_comment( - text=f""" + # check if employees with matched number are scheduled to receive popup + emails = set(scheduled_employees).intersection(employee_emails) + + if frappe.conf.developer_mode: + self.add_comment( + text=f""" Scheduled Employees: {scheduled_employees} Matching Employee: {employee_emails} Show Popup To: {emails} """ - ) + ) - if employee_emails and not emails: - self.add_comment(text=_("No employee was scheduled for call popup")) + if employee_emails and not emails: + self.add_comment(text=_("No employee was scheduled for call popup")) - for email in emails: - frappe.publish_realtime("show_call_popup", self, user=email) + for email in emails: + frappe.publish_realtime("show_call_popup", self, user=email) def update_received_by(self): if employees := get_employees_with_number(self.get("to")): @@ -154,8 +154,8 @@ def link_existing_conversations(doc, state): ELSE 0 END )=0 - """, - dict(phone_number="%{}".format(number), docname=doc.name, doctype=doc.doctype), + """, + dict(phone_number=f"%{number}", docname=doc.name, doctype=doc.doctype), ) for log in logs: @@ -175,7 +175,7 @@ def get_linked_call_logs(doctype, docname): filters={"parenttype": "Call Log", "link_doctype": doctype, "link_name": docname}, ) - logs = set([log.parent for log in logs]) + logs = {log.parent for log in logs} logs = frappe.get_all("Call Log", fields=["*"], filters={"name": ["in", logs]}) diff --git a/erpnext/tests/exotel_test_data.py b/erpnext/tests/exotel_test_data.py deleted file mode 100644 index 3ad2575c23..0000000000 --- a/erpnext/tests/exotel_test_data.py +++ /dev/null @@ -1,122 +0,0 @@ -import frappe - -call_initiation_data = frappe._dict( - { - "CallSid": "23c162077629863c1a2d7f29263a162m", - "CallFrom": "09999999991", - "CallTo": "09999999980", - "Direction": "incoming", - "Created": "Wed, 23 Feb 2022 12:31:59", - "From": "09999999991", - "To": "09999999988", - "CurrentTime": "2022-02-23 12:32:02", - "DialWhomNumber": "09999999999", - "Status": "busy", - "EventType": "Dial", - "AgentEmail": "test_employee_exotel@company.com", - } -) - -call_end_data = frappe._dict( - { - "CallSid": "23c162077629863c1a2d7f29263a162m", - "CallFrom": "09999999991", - "CallTo": "09999999980", - "Direction": "incoming", - "ForwardedFrom": "null", - "Created": "Wed, 23 Feb 2022 12:31:59", - "DialCallDuration": "17", - "RecordingUrl": "https://s3-ap-southeast-1.amazonaws.com/random.mp3", - "StartTime": "2022-02-23 12:31:58", - "EndTime": "1970-01-01 05:30:00", - "DialCallStatus": "completed", - "CallType": "completed", - "DialWhomNumber": "09999999999", - "ProcessStatus": "null", - "flow_id": "228040", - "tenant_id": "67291", - "From": "09999999991", - "To": "09999999988", - "RecordingAvailableBy": "Wed, 23 Feb 2022 12:37:25", - "CurrentTime": "2022-02-23 12:32:25", - "OutgoingPhoneNumber": "09999999988", - "Legs": [ - { - "Number": "09999999999", - "Type": "single", - "OnCallDuration": "10", - "CallerId": "09999999980", - "CauseCode": "NORMAL_CLEARING", - "Cause": "16", - } - ], - } -) - -call_disconnected_data = frappe._dict( - { - "CallSid": "d96421addce69e24bdc7ce5880d1162l", - "CallFrom": "09999999991", - "CallTo": "09999999980", - "Direction": "incoming", - "ForwardedFrom": "null", - "Created": "Mon, 21 Feb 2022 15:58:12", - "DialCallDuration": "0", - "StartTime": "2022-02-21 15:58:12", - "EndTime": "1970-01-01 05:30:00", - "DialCallStatus": "canceled", - "CallType": "client-hangup", - "DialWhomNumber": "09999999999", - "ProcessStatus": "null", - "flow_id": "228040", - "tenant_id": "67291", - "From": "09999999991", - "To": "09999999988", - "CurrentTime": "2022-02-21 15:58:47", - "OutgoingPhoneNumber": "09999999988", - "Legs": [ - { - "Number": "09999999999", - "Type": "single", - "OnCallDuration": "0", - "CallerId": "09999999980", - "CauseCode": "RING_TIMEOUT", - "Cause": "1003", - } - ], - } -) - -call_not_answered_data = frappe._dict( - { - "CallSid": "fdb67a2b4b2d057b610a52ef43f81622", - "CallFrom": "09999999991", - "CallTo": "09999999980", - "Direction": "incoming", - "ForwardedFrom": "null", - "Created": "Mon, 21 Feb 2022 15:47:02", - "DialCallDuration": "0", - "StartTime": "2022-02-21 15:47:02", - "EndTime": "1970-01-01 05:30:00", - "DialCallStatus": "no-answer", - "CallType": "incomplete", - "DialWhomNumber": "09999999999", - "ProcessStatus": "null", - "flow_id": "228040", - "tenant_id": "67291", - "From": "09999999991", - "To": "09999999988", - "CurrentTime": "2022-02-21 15:47:40", - "OutgoingPhoneNumber": "09999999988", - "Legs": [ - { - "Number": "09999999999", - "Type": "single", - "OnCallDuration": "0", - "CallerId": "09999999980", - "CauseCode": "RING_TIMEOUT", - "Cause": "1003", - } - ], - } -) diff --git a/erpnext/tests/test_exotel.py b/erpnext/tests/test_exotel.py deleted file mode 100644 index 9b91414571..0000000000 --- a/erpnext/tests/test_exotel.py +++ /dev/null @@ -1,68 +0,0 @@ -import frappe -from frappe.contacts.doctype.contact.test_contact import create_contact -from frappe.tests.test_api import FrappeAPITestCase - -from erpnext.setup.doctype.employee.test_employee import make_employee - - -class TestExotel(FrappeAPITestCase): - @classmethod - def setUpClass(cls): - cls.CURRENT_DB_CONNECTION = frappe.db - cls.test_employee_name = make_employee( - user="test_employee_exotel@company.com", cell_number="9999999999" - ) - frappe.db.set_single_value("Exotel Settings", "enabled", 1) - phones = [{"phone": "+91 9999999991", "is_primary_phone": 0, "is_primary_mobile_no": 1}] - create_contact(name="Test Contact", salutation="Mr", phones=phones) - frappe.db.commit() - - def test_for_successful_call(self): - from .exotel_test_data import call_end_data, call_initiation_data - - api_method = "handle_incoming_call" - end_call_api_method = "handle_end_call" - - self.emulate_api_call_from_exotel(api_method, call_initiation_data) - self.emulate_api_call_from_exotel(end_call_api_method, call_end_data) - call_log = frappe.get_doc("Call Log", call_initiation_data.CallSid) - - self.assertEqual(call_log.get("from"), call_initiation_data.CallFrom) - self.assertEqual(call_log.get("to"), call_initiation_data.DialWhomNumber) - self.assertEqual(call_log.get("call_received_by"), self.test_employee_name) - self.assertEqual(call_log.get("status"), "Completed") - - def test_for_disconnected_call(self): - from .exotel_test_data import call_disconnected_data - - api_method = "handle_missed_call" - self.emulate_api_call_from_exotel(api_method, call_disconnected_data) - call_log = frappe.get_doc("Call Log", call_disconnected_data.CallSid) - self.assertEqual(call_log.get("from"), call_disconnected_data.CallFrom) - self.assertEqual(call_log.get("to"), call_disconnected_data.DialWhomNumber) - self.assertEqual(call_log.get("call_received_by"), self.test_employee_name) - self.assertEqual(call_log.get("status"), "Canceled") - - def test_for_call_not_answered(self): - from .exotel_test_data import call_not_answered_data - - api_method = "handle_missed_call" - self.emulate_api_call_from_exotel(api_method, call_not_answered_data) - call_log = frappe.get_doc("Call Log", call_not_answered_data.CallSid) - self.assertEqual(call_log.get("from"), call_not_answered_data.CallFrom) - self.assertEqual(call_log.get("to"), call_not_answered_data.DialWhomNumber) - self.assertEqual(call_log.get("call_received_by"), self.test_employee_name) - self.assertEqual(call_log.get("status"), "No Answer") - - def emulate_api_call_from_exotel(self, api_method, data): - self.post( - f"/api/method/erpnext.erpnext_integrations.exotel_integration.{api_method}", - data=frappe.as_json(data), - content_type="application/json", - ) - # restart db connection to get latest data - frappe.connect() - - @classmethod - def tearDownClass(cls): - frappe.db = cls.CURRENT_DB_CONNECTION diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv index c02aeb2f83..8efa94df5a 100644 --- a/erpnext/translations/de.csv +++ b/erpnext/translations/de.csv @@ -158,7 +158,7 @@ Advertising,Werbung, Aerospace,Luft- und Raumfahrt, Against,Zu, Against Account,Gegenkonto, -Against Journal Entry {0} does not have any unmatched {1} entry,"""Zu Buchungssatz"" {0} hat nur abgeglichene {1} Buchungen", +Against Journal Entry {0} does not have any unmatched {1} entry,Buchungssatz {0} hat keinen offenen Eintrag auf der {1}-Seite, Against Journal Entry {0} is already adjusted against some other voucher,"""Zu Buchungssatz"" {0} ist bereits mit einem anderen Beleg abgeglichen", Against Supplier Invoice {0} dated {1},Zu Eingangsrechnung {0} vom {1}, Against Voucher,Gegenbeleg, @@ -209,10 +209,10 @@ Amount of Integrated Tax,Betrag der integrierten Steuer, Amount of TDS Deducted,Betrag der abgezogenen TDS, Amount should not be less than zero.,Betrag sollte nicht kleiner als Null sein., Amount to Bill,Rechnungsbetrag, -Amount {0} {1} against {2} {3},Menge {0} {1} gegen {2} {3}, -Amount {0} {1} deducted against {2},Menge {0} {1} abgezogen gegen {2}, -Amount {0} {1} transferred from {2} to {3},Menge {0} {1} übertragen von {2} auf {3}, -Amount {0} {1} {2} {3},Menge {0} {1} {2} {3}, +Amount {0} {1} against {2} {3},Betrag {0} {1} gegen {2} {3}, +Amount {0} {1} deducted against {2},Betrag {0} {1} abgezogen gegen {2}, +Amount {0} {1} transferred from {2} to {3},Betrag {0} {1} wurde von {2} zu {3} transferiert, +Amount {0} {1} {2} {3},Betrag {0} {1} {2} {3}, Amt,Menge, "An Item Group exists with same name, please change the item name or rename the item group",Eine Artikelgruppe mit dem gleichen Namen existiert bereits. Bitte den Artikelnamen ändern oder die Artikelgruppe umbenennen, An academic term with this 'Academic Year' {0} and 'Term Name' {1} already exists. Please modify these entries and try again.,"Ein Semester mit ""Semesterjahr""'{0} und ""Semesternamen"" {1} ist bereits vorhanden. Bitte ändern Sie diese entsprechend und versuchen Sie es erneut.", @@ -279,7 +279,7 @@ Asset Name,Name Vermögenswert, Asset Received But Not Billed,"Vermögenswert erhalten, aber nicht in Rechnung gestellt", Asset Value Adjustment,Anpassung Vermögenswert, "Asset cannot be cancelled, as it is already {0}","Vermögenswert kann nicht rückgängig gemacht werden, da es ohnehin schon {0} ist", -Asset scrapped via Journal Entry {0},Vermögenswert über Journaleintrag {0} entsorgt, +Asset scrapped via Journal Entry {0},Vermögenswert über Buchungssatz {0} entsorgt, "Asset {0} cannot be scrapped, as it is already {1}",Anlagewert-{0} ist bereits entsorgt {1}, Asset {0} does not belong to company {1},Vermögenswert {0} gehört nicht zu Unternehmen {1}., Asset {0} must be submitted,Vermögenswert {0} muss eingereicht werden., @@ -440,7 +440,7 @@ Can be approved by {0},Kann von {0} genehmigt werden, "Can not filter based on Account, if grouped by Account","Wenn nach Konto gruppiert wurde, kann nicht auf Grundlage des Kontos gefiltert werden.", "Can not filter based on Voucher No, if grouped by Voucher","Wenn nach Beleg gruppiert wurde, kann nicht auf Grundlage von Belegen gefiltert werden.", "Can not mark Inpatient Record Discharged, there are Unbilled Invoices {0}","Kann den entlassenen Krankenhauskatheter nicht markieren, es gibt keine fakturierten Rechnungen {0}", -Can only make payment against unbilled {0},Zahlung kann nur zu einer noch nicht abgerechneten {0} erstellt werden, +Can only make payment against unbilled {0},Zahlung kann nur zu einem noch nicht abgerechneten Beleg vom Typ {0} erstellt werden, Can refer row only if the charge type is 'On Previous Row Amount' or 'Previous Row Total',"Kann sich nur auf eine Zeile beziehen, wenn die Berechnungsart der Kosten entweder ""auf vorherige Zeilensumme"" oder ""auf vorherigen Zeilenbetrag"" ist", "Can't change valuation method, as there are transactions against some items which does not have it's own valuation method","Kann die Bewertungsmethode nicht ändern, da es Transaktionen gegen einige Posten gibt, für die es keine eigene Bewertungsmethode gibt", Can't create standard criteria. Please rename the criteria,Kann keine Standardkriterien erstellen. Bitte benennen Sie die Kriterien um, @@ -448,7 +448,7 @@ Cancel,Abbrechen, Cancel Material Visit {0} before cancelling this Warranty Claim,Materialkontrolle {0} stornieren vor Abbruch dieses Garantieantrags, Cancel Material Visits {0} before cancelling this Maintenance Visit,Materialkontrolle {0} stornieren vor Abbruch dieses Wartungsbesuchs, Cancel Subscription,Abonnement beenden, -Cancel the journal entry {0} first,Brechen Sie zuerst den Journaleintrag {0} ab, +Cancel the journal entry {0} first,Brechen Sie zuerst den Buchungssatz {0} ab, Canceled,Abgebrochen, "Cannot Submit, Employees left to mark attendance","Kann nicht übergeben werden, Mitarbeiter sind zur Teilnahme zugelassen", Cannot be a fixed asset item as Stock Ledger is created.,"Kann keine Anlageposition sein, wenn das Stock Ledger erstellt wird.", @@ -473,9 +473,11 @@ Cannot deduct when category is for 'Valuation' or 'Vaulation and Total',"Kann ni Cannot enroll more than {0} students for this student group.,Kann nicht mehr als {0} Studenten für diese Studentengruppe einschreiben., Cannot find active Leave Period,Aktive Abwesenheitszeit kann nicht gefunden werden, Cannot produce more Item {0} than Sales Order quantity {1},"Es können nicht mehr Artikel {0} produziert werden, als die über den Auftrag bestellte Stückzahl {1}", +Cannot pay to Customer without any negative outstanding invoice,"Es kann nicht an den Kunden gezahlt werden, ohne dass eine Gutschrift vorhanden ist", Cannot promote Employee with status Left,Mitarbeiter mit Status "Links" kann nicht gefördert werden, +Cannot receive from Supplier without any negative outstanding invoice,"Es kann nicht vom Lieferanten empfangen werden, ohne dass eine Gutschrift vorhanden ist", Cannot refer row number greater than or equal to current row number for this Charge type,"Für diese Berechnungsart kann keine Zeilennummern zugeschrieben werden, die größer oder gleich der aktuellen Zeilennummer ist", -Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row,"Die Berechnungsart kann für die erste Zeile nicht auf ""bezogen auf Menge der vorhergenden Zeile"" oder auf ""bezogen auf Gesamtmenge der vorhergenden Zeile"" gesetzt werden", +Cannot select charge type as 'On Previous Row Amount' or 'On Previous Row Total' for first row,"Die Berechnungsart kann für die erste Zeile nicht auf „Bezogen auf Betrag der vorhergenden Zeile“ oder auf „Bezogen auf Gesamtbetrag der vorhergenden Zeilen“ gesetzt werden", Cannot set as Lost as Sales Order is made.,"Kann nicht als verloren gekennzeichnet werden, da ein Auftrag dazu existiert.", Cannot set authorization on basis of Discount for {0},Genehmigung kann nicht auf der Basis des Rabattes für {0} festgelegt werden, Cannot set multiple Item Defaults for a company.,Es können nicht mehrere Artikelstandards für ein Unternehmen festgelegt werden., @@ -512,7 +514,7 @@ Change Template Code,Vorlagencode ändern, Changing Customer Group for the selected Customer is not allowed.,Die Änderung der Kundengruppe für den ausgewählten Kunden ist nicht zulässig., Chapter,Gruppe, Chapter information.,Gruppeninformation, -Charge of type 'Actual' in row {0} cannot be included in Item Rate,"Kosten für den Typ ""real"" in Zeile {0} können nicht in den Artikelpreis mit eingeschlossen werden", +Charge of type 'Actual' in row {0} cannot be included in Item Rate or Paid Amount,"Kosten für den Typ „Tatsächlich“ in Zeile {0} können nicht in den Artikelpreis oder den bezahlen Betrag einfließen", Chargeble,Belastung, Charges are updated in Purchase Receipt against each item,Kosten werden im Kaufbeleg für jede Position aktualisiert, "Charges will be distributed proportionately based on item qty or amount, as per your selection",Die Kosten werden gemäß Ihrer Wahl anteilig verteilt basierend auf Artikelmenge oder -preis, @@ -655,7 +657,7 @@ Create Inter Company Journal Entry,Erstellen Sie einen Inter Company Journal Ein Create Invoice,Rechnung erstellen, Create Invoices,Rechnungen erstellen, Create Job Card,Jobkarte erstellen, -Create Journal Entry,Journaleintrag erstellen, +Create Journal Entry,Buchungssatz erstellen, Create Lead,Lead erstellen, Create Leads,Leads erstellen, Create Maintenance Visit,Wartungsbesuch anlegen, @@ -835,7 +837,7 @@ Did not find any item called {0},Hat keinen Artikel finden genannt {0}, Diff Qty,Diff Menge, Difference Account,Differenzkonto, "Difference Account must be a Asset/Liability type account, since this Stock Reconciliation is an Opening Entry","Differenzkonto muss ein Vermögens-/Verbindlichkeiten-Konto sein, da dieser Lagerabgleich eine Eröffnungsbuchung ist", -Difference Amount,Differenzmenge, +Difference Amount,Differenzbetrag, Difference Amount must be zero,Differenzbetrag muss Null sein, Different UOM for items will lead to incorrect (Total) Net Weight value. Make sure that Net Weight of each item is in the same UOM.,"Unterschiedliche Maßeinheiten für Artikel führen zu falschen Werten für das (Gesamt-)Nettogewicht. Es muss sicher gestellt sein, dass das Nettogewicht jedes einzelnen Artikels in der gleichen Maßeinheit angegeben ist.", Direct Expenses,Direkte Aufwendungen, @@ -1031,7 +1033,7 @@ Fieldname,Feldname, Fields,Felder, Fill the form and save it,Formular ausfüllen und speichern, Filter Employees By (Optional),Mitarbeiter filtern nach (optional), -"Filter Fields Row #{0}: Fieldname {1} must be of type ""Link"" or ""Table MultiSelect""",Filterfelder Zeile # {0}: Feldname {1} muss vom Typ "Link" oder "Tabelle MultiSelect" sein, +"Filter Fields Row #{0}: Fieldname {1} must be of type ""Link"" or ""Table MultiSelect""",Filterfelder Zeile {0}: Feldname {1} muss vom Typ "Link" oder "Tabelle MultiSelect" sein, Filter Total Zero Qty,Gesamtmenge filtern, Finance Book,Finanzbuch, Financial / accounting year.,Finanz-/Rechnungsjahr, @@ -1217,7 +1219,7 @@ High Sensitivity,Hohe Empfindlichkeit, Hold,Anhalten, Hold Invoice,Rechnung zurückhalten, Holiday,Urlaub, -Holiday List,Urlaubsübersicht, +Holiday List,Feiertagsliste, Hotel Rooms of type {0} are unavailable on {1},Hotelzimmer vom Typ {0} sind auf {1} nicht verfügbar, Hotels,Hotels, Hourly,Stündlich, @@ -1307,6 +1309,7 @@ Invalid GSTIN! A GSTIN must have 15 characters.,Ungültige GSTIN! Eine GSTIN mus Invalid GSTIN! First 2 digits of GSTIN should match with State number {0}.,Ungültige GSTIN! Die ersten beiden Ziffern von GSTIN sollten mit der Statusnummer {0} übereinstimmen., Invalid GSTIN! The input you've entered doesn't match the format of GSTIN.,Ungültige GSTIN! Die von Ihnen eingegebene Eingabe stimmt nicht mit dem Format von GSTIN überein., Invalid Posting Time,Ungültige Buchungszeit, +Invalid Purchase Invoice,Ungültige Einkaufsrechnung, Invalid attribute {0} {1},Ungültiges Attribut {0} {1}, Invalid quantity specified for item {0}. Quantity should be greater than 0.,Ungültzige Anzahl für Artikel {0} angegeben. Anzahl sollte größer als 0 sein., Invalid reference {0} {1},Ungültige Referenz {0} {1}, @@ -1769,7 +1772,7 @@ Odometer,Tacho, Office Equipments,Büroausstattung, Office Maintenance Expenses,Büro-Wartungskosten, Office Rent,Büromiete, -On Hold,In Wartestellung, +On Hold,Auf Eis gelegt, On Net Total,Auf Nettosumme, One customer can be part of only single Loyalty Program.,Ein Kunde kann Teil eines einzigen Treueprogramms sein., Online Auctions,Online-Auktionen, @@ -1859,7 +1862,7 @@ Packing Slip,Packzettel, Packing Slip(s) cancelled,Packzettel storniert, Paid,Bezahlt, Paid Amount,Gezahlter Betrag, -Paid Amount cannot be greater than total negative outstanding amount {0},Gezahlten Betrag kann nicht größer sein als die Gesamt negativ ausstehenden Betrag {0}, +Paid Amount cannot be greater than total negative outstanding amount {0},"Der gezahlte Betrag darf nicht größer sein als der gesamte, negative, ausstehende Betrag {0}", Paid amount + Write Off Amount can not be greater than Grand Total,Summe aus gezahltem Betrag + ausgebuchter Betrag darf nicht größer der Gesamtsumme sein, Paid and Not Delivered,Bezahlt und nicht ausgeliefert, Parameter,Parameter, @@ -1911,7 +1914,7 @@ Payment Terms,Zahlungsbedingungen, Payment Terms Template,Vorlage Zahlungsbedingungen, Payment Terms based on conditions,Zahlungsbedingungen basieren auf Bedingungen, Payment Type,Zahlungsart, -"Payment Type must be one of Receive, Pay and Internal Transfer","Zahlungsart muss eine der Receive sein, Pay und interne Übertragung", +"Payment Type must be one of Receive, Pay and Internal Transfer","Zahlungsart muss entweder 'Empfangen', 'Zahlen' oder 'Interner Transfer' sein", Payment against {0} {1} cannot be greater than Outstanding Amount {2},Zahlung zu {0} {1} kann nicht größer als ausstehender Betrag {2} sein, Payment of {0} from {1} to {2},Zahlung von {0} von {1} an {2}, Payment request {0} created,Zahlungsaufforderung {0} erstellt, @@ -2293,9 +2296,9 @@ Read blog,Blog lesen, Read the ERPNext Manual,Lesen Sie das ERPNext-Handbuch, Reading Uploaded File,Hochgeladene Datei lesen, Real Estate,Immobilien, -Reason For Putting On Hold,Grund für das Halten, -Reason for Hold,Grund für das Halten, -Reason for hold: ,Grund für das Halten:, +Reason For Putting On Hold,Grund für das auf Eis legen, +Reason for Hold,Grund für das auf Eis legen, +Reason for hold: ,Grund für das auf Eis legen:, Receipt,Kaufbeleg, Receipt document must be submitted,Eingangsbeleg muss vorgelegt werden, Receivable,Forderung, @@ -2315,18 +2318,20 @@ Ref Date,Ref-Datum, Reference,Referenz, Reference #{0} dated {1},Referenz #{0} vom {1}, Reference Date,Referenzdatum, -Reference Doctype must be one of {0},Referenz Doctype muss man von {0}, +Reference Doctype must be one of {0},Referenz-Typ muss eine von {0} sein, Reference Document,Referenzdokument, Reference Document Type,Referenz-Dokumententyp, Reference No & Reference Date is required for {0},Referenznr. & Referenz-Tag sind erforderlich für {0}, -Reference No and Reference Date is mandatory for Bank transaction,Referenznummer und Referenzdatum ist obligatorisch für Bankengeschäft, -Reference No is mandatory if you entered Reference Date,"Referenznr. ist zwingend erforderlich, wenn Referenz-Tag eingegeben wurde", +Reference No and Reference Date is mandatory for Bank transaction,Referenznummer und Referenzdatum sind Pflichtfelder, +Reference No is mandatory if you entered Reference Date,"Referenznummer ist ein Pflichtfeld, wenn ein Referenzdatum eingegeben wurde", Reference No.,Referenznummer., Reference Number,Referenznummer, Reference Owner,Referenz Besitzer, Reference Type,Referenz-Typ, "Reference: {0}, Item Code: {1} and Customer: {2}","Referenz: {0}, Item Code: {1} und Kunde: {2}", References,Referenzen, +References {0} of type {1} had no outstanding amount left before submitting the Payment Entry. Now they have a negative outstanding amount.,"Die Referenzen {0} vom Typ {1} hatten keinen ausstehenden Betrag mehr, bevor die Zahlung gebucht wurde. Jetzt haben sie einen negativen ausstehenden Betrag.", +If this is undesirable please cancel the corresponding Payment Entry.,"Falls dies nicht erwünscht ist, stornieren Sie bitte die entsprechende Zahlung.", Refresh Token,Aktualisieren Token, Region,Region, Register,Neu registrieren, @@ -2405,7 +2410,7 @@ Return,Zurück, Return / Credit Note,Return / Gutschrift, Return / Debit Note,Return / Lastschrift, Returns,Retouren, -Reverse Journal Entry,Journaleintrag umkehren, +Reverse Journal Entry,Buchungssatz umkehren, Review Invitation Sent,Einladung überprüfen gesendet, Review and Action,Überprüfung und Aktion, Role,Rolle, @@ -2418,63 +2423,64 @@ Root cannot have a parent cost center,Root kann keine übergeordnete Kostenstell Round Off,Abschliessen, Rounded Total,Gerundete Gesamtsumme, Route,Route, -Row # {0}: ,Zeile # {0}:, -Row # {0}: Batch No must be same as {1} {2},Zeile # {0}: Chargennummer muss dieselbe sein wie {1} {2}, -Row # {0}: Cannot return more than {1} for Item {2},Zeile # {0}: Es kann nicht mehr als {1} für Artikel {2} zurückgegeben werden, -Row # {0}: Rate cannot be greater than the rate used in {1} {2},"Row # {0}: Die Rate kann nicht größer sein als die Rate, die in {1} {2}", -Row # {0}: Serial No is mandatory,Zeile # {0}: Seriennummer ist zwingend erforderlich, -Row # {0}: Serial No {1} does not match with {2} {3},Zeile # {0}: Seriennummer {1} stimmt nicht mit {2} {3} überein, -Row #{0} (Payment Table): Amount must be negative,Zeilennr. {0} (Zahlungstabelle): Betrag muss negativ sein, -Row #{0} (Payment Table): Amount must be positive,Zeile # {0} (Zahlungstabelle): Betrag muss positiv sein, -Row #{0}: Account {1} does not belong to company {2},Zeile # {0}: Konto {1} gehört nicht zur Unternehmen {2}, -Row #{0}: Allocated Amount cannot be greater than outstanding amount.,Zeile # {0}: Zugeordneter Betrag darf nicht größer als ausstehender Betrag sein., -"Row #{0}: Asset {1} cannot be submitted, it is already {2}","Zeile Nr. {0}: Vermögenswert {1} kann nicht vorgelegt werden, es ist bereits {2}", -Row #{0}: Cannot set Rate if amount is greater than billed amount for Item {1}.,"Zeilennr. {0}: Die Rate kann nicht festgelegt werden, wenn der Betrag für Artikel {1} höher als der Rechnungsbetrag ist.", -Row #{0}: Clearance date {1} cannot be before Cheque Date {2},Row # {0}: Räumungsdatum {1} kann nicht vor dem Scheck Datum sein {2}, -Row #{0}: Duplicate entry in References {1} {2},Zeile # {0}: Eintrag in Referenzen {1} {2} duplizieren, -Row #{0}: Expected Delivery Date cannot be before Purchase Order Date,Row # {0}: Voraussichtlicher Liefertermin kann nicht vor Bestelldatum sein, -Row #{0}: Item added,Zeile # {0}: Element hinzugefügt, -Row #{0}: Journal Entry {1} does not have account {2} or already matched against another voucher,Row # {0}: Journal Entry {1} nicht Konto {2} oder bereits abgestimmt gegen einen anderen Gutschein, -Row #{0}: Not allowed to change Supplier as Purchase Order already exists,"Zeile #{0}: Es ist nicht erlaubt den Lieferanten zu wechseln, da bereits eine Bestellung vorhanden ist", -Row #{0}: Please set reorder quantity,Zeile #{0}: Bitte Nachbestellmenge angeben, -Row #{0}: Please specify Serial No for Item {1},Zeile #{0}: Bitte Seriennummer für Artikel {1} angeben, -Row #{0}: Qty increased by 1,Zeile # {0}: Menge um 1 erhöht, -Row #{0}: Rate must be same as {1}: {2} ({3} / {4}) ,Zeile #{0}: Preis muss derselbe wie {1}: {2} ({3} / {4}) sein, -Row #{0}: Reference Document Type must be one of Expense Claim or Journal Entry,Row # {0}: Referenzdokumenttyp muss entweder 'Auslagenabrechnung' oder 'Journaleintrag' sein, -"Row #{0}: Reference Document Type must be one of Purchase Order, Purchase Invoice or Journal Entry","Row # {0}: Referenzdokumenttyp muss eine der Bestellung, Rechnung oder Kaufjournaleintrag sein", -Row #{0}: Rejected Qty can not be entered in Purchase Return,Zeile #{0}: Abgelehnte Menge kann nicht in Kaufrückgabe eingegeben werden, -Row #{0}: Rejected Warehouse is mandatory against rejected Item {1},Row # {0}: Abgelehnt Warehouse ist obligatorisch gegen zurückgewiesen Artikel {1}, -Row #{0}: Reqd by Date cannot be before Transaction Date,Zeilennr. {0}: Erforderlich nach Datum darf nicht vor dem Transaktionsdatum liegen, -Row #{0}: Set Supplier for item {1},Zeile #{0}: Lieferanten für Artikel {1} einstellen, -Row #{0}: Status must be {1} for Invoice Discounting {2},Zeile # {0}: Status muss {1} für Rechnungsrabatt {2} sein, -"Row #{0}: The batch {1} has only {2} qty. Please select another batch which has {3} qty available or split the row into multiple rows, to deliver/issue from multiple batches","Zeile # {0}: Der Batch {1} hat nur {2} Menge. Bitte wähle eine andere Charge aus, die {3} Menge zur Verfügung hat oder die Zeile in mehrere Zeilen aufteilt, um aus mehreren Chargen zu liefern / auszutauschen", -Row #{0}: Timings conflicts with row {1},Zeile #{0}: Timing-Konflikte mit Zeile {1}, -Row #{0}: {1} can not be negative for item {2},Row # {0}: {1} kann für Artikel nicht negativ sein {2}, -Row No {0}: Amount cannot be greater than Pending Amount against Expense Claim {1}. Pending Amount is {2},Zeile Nr. {0}: Der Betrag kann nicht größer als der ausstehende Betrag der Auslagenabrechnung {1} sein. Der ausstehende Betrag ist {2}, +Row # {0}: ,Zeile {0}:, +Row # {0}: Batch No must be same as {1} {2},Zeile {0}: Chargennummer muss dieselbe sein wie {1} {2}, +Row # {0}: Cannot return more than {1} for Item {2},Zeile {0}: Es kann nicht mehr als {1} für Artikel {2} zurückgegeben werden, +Row # {0}: Rate cannot be greater than the rate used in {1} {2},"Zeile {0}: Die Rate kann nicht größer sein als die Rate, die in {1} {2}", +Row # {0}: Serial No is mandatory,Zeile {0}: Seriennummer ist zwingend erforderlich, +Row # {0}: Serial No {1} does not match with {2} {3},Zeile {0}: Seriennummer {1} stimmt nicht mit {2} {3} überein, +Row #{0} (Payment Table): Amount must be negative,Zeile {0} (Zahlungstabelle): Betrag muss negativ sein, +Row #{0} (Payment Table): Amount must be positive,Zeile {0} (Zahlungstabelle): Betrag muss positiv sein, +Row #{0}: Account {1} does not belong to company {2},Zeile {0}: Konto {1} gehört nicht zur Unternehmen {2}, +Row #{0}: Allocated Amount cannot be greater than outstanding amount.,Zeile {0}: Zugeordneter Betrag darf nicht größer als ausstehender Betrag sein., +"Row #{0}: Asset {1} cannot be submitted, it is already {2}","Zeile {0}: Vermögenswert {1} kann nicht vorgelegt werden, es ist bereits {2}", +Row #{0}: Cannot set Rate if amount is greater than billed amount for Item {1}.,"Zeile {0}: Die Rate kann nicht festgelegt werden, wenn der Betrag für Artikel {1} höher als der Rechnungsbetrag ist.", +Row #{0}: Cannot allocate more than {1} against payment term {2},Zeile {0}: Es kann nicht mehr als {1} zu Zahlungsbedingung {2} zugeordnet werden, +Row #{0}: Clearance date {1} cannot be before Cheque Date {2},Zeile {0}: Räumungsdatum {1} kann nicht vor dem Scheck Datum sein {2}, +Row #{0}: Duplicate entry in References {1} {2},Referenz {1} {2} in Zeile {0} kommt doppelt vor, +Row #{0}: Expected Delivery Date cannot be before Purchase Order Date,Zeile {0}: Voraussichtlicher Liefertermin kann nicht vor Bestelldatum sein, +Row #{0}: Item added,Zeile {0}: Element hinzugefügt, +Row #{0}: Journal Entry {1} does not have account {2} or already matched against another voucher,Zeile {0}: Buchungssatz {1} betrifft nicht Konto {2} oder bereits mit einem anderen Beleg verrechnet, +Row #{0}: Not allowed to change Supplier as Purchase Order already exists,"Zeile {0}: Es ist nicht erlaubt den Lieferanten zu wechseln, da bereits eine Bestellung vorhanden ist", +Row #{0}: Please set reorder quantity,Zeile {0}: Bitte Nachbestellmenge angeben, +Row #{0}: Please specify Serial No for Item {1},Zeile {0}: Bitte Seriennummer für Artikel {1} angeben, +Row #{0}: Qty increased by 1,Zeile {0}: Menge um 1 erhöht, +Row #{0}: Rate must be same as {1}: {2} ({3} / {4}) ,Zeile {0}: Preis muss derselbe wie {1}: {2} ({3} / {4}) sein, +Row #{0}: Reference Document Type must be one of Expense Claim or Journal Entry,Zeile {0}: Referenzdokumenttyp muss entweder 'Auslagenabrechnung' oder 'Buchungssatz' sein, +"Row #{0}: Reference Document Type must be one of Purchase Order, Purchase Invoice or Journal Entry","Zeile {0}: Referenzdokumenttyp muss eine der Bestellung, Eingangsrechnung oder Buchungssatz sein", +Row #{0}: Rejected Qty can not be entered in Purchase Return,Zeile {0}: Abgelehnte Menge kann nicht in Kaufrückgabe eingegeben werden, +Row #{0}: Rejected Warehouse is mandatory against rejected Item {1},Zeile {0}: Abgelehnt Warehouse ist obligatorisch gegen zurückgewiesen Artikel {1}, +Row #{0}: Reqd by Date cannot be before Transaction Date,Zeile {0}: Erforderlich nach Datum darf nicht vor dem Transaktionsdatum liegen, +Row #{0}: Set Supplier for item {1},Zeile {0}: Lieferanten für Artikel {1} einstellen, +Row #{0}: Status must be {1} for Invoice Discounting {2},Zeile {0}: Status muss {1} für Rechnungsrabatt {2} sein, +"Row #{0}: The batch {1} has only {2} qty. Please select another batch which has {3} qty available or split the row into multiple rows, to deliver/issue from multiple batches","Zeile {0}: Der Batch {1} hat nur {2} Menge. Bitte wähle eine andere Charge aus, die {3} Menge zur Verfügung hat oder die Zeile in mehrere Zeilen aufteilt, um aus mehreren Chargen zu liefern / auszutauschen", +Row #{0}: Timings conflicts with row {1},Zeile {0}: Timing-Konflikte mit Zeile {1}, +Row #{0}: {1} can not be negative for item {2},Zeile {0}: {1} kann für Artikel nicht negativ sein {2}, +Row No {0}: Amount cannot be greater than Pending Amount against Expense Claim {1}. Pending Amount is {2},Zeile {0}: Der Betrag kann nicht größer als der ausstehende Betrag der Auslagenabrechnung {1} sein. Der ausstehende Betrag ist {2}, Row {0} : Operation is required against the raw material item {1},Zeile {0}: Vorgang ist für die Rohmaterialposition {1} erforderlich, Row {0}# Allocated amount {1} cannot be greater than unclaimed amount {2},Zeile {0} # Der zugewiesene Betrag {1} darf nicht größer sein als der nicht beanspruchte Betrag {2}, Row {0}# Item {1} cannot be transferred more than {2} against Purchase Order {3},Zeile {0} # Artikel {1} kann nicht mehr als {2} gegen Bestellung {3} übertragen werden., -Row {0}# Paid Amount cannot be greater than requested advance amount,Zeile Nr. {0}: Bezahlter Betrag darf nicht größer sein als der geforderte Anzahlungsbetrag, -Row {0}: Activity Type is mandatory.,Row {0}: Leistungsart ist obligatorisch., -Row {0}: Advance against Customer must be credit,Row {0}: Voraus gegen Kunde muss Kredit, -Row {0}: Advance against Supplier must be debit,Row {0}: Voraus gegen Lieferant muss belasten werden, +Row {0}# Paid Amount cannot be greater than requested advance amount,Zeile {0}: Bezahlter Betrag darf nicht größer sein als der geforderte Anzahlungsbetrag, +Row {0}: Activity Type is mandatory.,Zeile {0}: Leistungsart ist obligatorisch., +Row {0}: Advance against Customer must be credit,Zeile {0}: Voraus gegen Kunde muss Kredit, +Row {0}: Advance against Supplier must be debit,Zeile {0}: Voraus gegen Lieferant muss belasten werden, Row {0}: Allocated amount {1} must be less than or equals to Payment Entry amount {2},Zeile {0}: Zugewiesener Betrag {1} muss kleiner oder gleich der Zahlungsmenge {2} sein, Row {0}: Allocated amount {1} must be less than or equals to invoice outstanding amount {2},Zeile {0}: Zugeteilte Menge {1} muss kleiner als oder gleich dem ausstehenden Betrag in Rechnung {2} sein, Row {0}: An Reorder entry already exists for this warehouse {1},Zeile {0}: Es gibt bereits eine Nachbestellungsbuchung für dieses Lager {1}, Row {0}: Bill of Materials not found for the Item {1},Zeile {0}: Bill of Materials nicht für den Artikel gefunden {1}, -Row {0}: Conversion Factor is mandatory,Row {0}: Umrechnungsfaktor ist zwingend erfoderlich, +Row {0}: Conversion Factor is mandatory,Zeile {0}: Umrechnungsfaktor ist zwingend erfoderlich, Row {0}: Cost center is required for an item {1},Zeile {0}: Kostenstelle ist für einen Eintrag {1} erforderlich, Row {0}: Credit entry can not be linked with a {1},Zeile {0}: Habenbuchung kann nicht mit ein(em) {1} verknüpft werden, Row {0}: Currency of the BOM #{1} should be equal to the selected currency {2},Zeile {0}: Währung der Stückliste # {1} sollte der gewählten Währung entsprechen {2}, Row {0}: Debit entry can not be linked with a {1},Zeile {0}: Sollbuchung kann nicht mit ein(em) {1} verknüpft werden, Row {0}: Depreciation Start Date is required,Zeile {0}: Das Abschreibungsstartdatum ist erforderlich, -Row {0}: Enter location for the asset item {1},Zeile Nr. {0}: Geben Sie den Speicherort für das Vermögenswert {1} ein., +Row {0}: Enter location for the asset item {1},Zeile {0}: Geben Sie einen Ort für den Vermögenswert {1} ein., Row {0}: Exchange Rate is mandatory,Zeile {0}: Wechselkurs ist erforderlich, Row {0}: Expected Value After Useful Life must be less than Gross Purchase Amount,Zeile {0}: Erwarteter Wert nach Nutzungsdauer muss kleiner als Brutto Kaufbetrag sein, -Row {0}: From Time and To Time is mandatory.,Row {0}: Von Zeit und zu Zeit ist obligatorisch., +Row {0}: From Time and To Time is mandatory.,Zeile {0}: Von Zeit und zu Zeit ist obligatorisch., Row {0}: From Time and To Time of {1} is overlapping with {2},Zeile {0}: Zeitüberlappung in {1} mit {2}, Row {0}: From time must be less than to time,Zeile {0}: Von Zeit zu Zeit muss kleiner sein, -Row {0}: Hours value must be greater than zero.,Row {0}: Stunden-Wert muss größer als Null sein., +Row {0}: Hours value must be greater than zero.,Zeile {0}: Stunden-Wert muss größer als Null sein., Row {0}: Invalid reference {1},Zeile {0}: Ungültige Referenz {1}, Row {0}: Party / Account does not match with {1} / {2} in {3} {4},Zeile {0}: Partei / Konto stimmt nicht mit {1} / {2} in {3} {4} überein, Row {0}: Party Type and Party is required for Receivable / Payable account {1},Zeile {0}: Partei-Typ und Partei sind für Forderungen-/Verbindlichkeiten-Konto {1} zwingend erforderlich, @@ -2869,7 +2875,7 @@ Supplier Group master.,Lieferantengruppenstamm, Supplier Id,Lieferanten-ID, Supplier Invoice Date cannot be greater than Posting Date,Lieferant Rechnungsdatum kann nicht größer sein als Datum der Veröffentlichung, Supplier Invoice No,Lieferantenrechnungsnr., -Supplier Invoice No exists in Purchase Invoice {0},Lieferantenrechnung existiert in Kauf Rechnung {0}, +Supplier Invoice No exists in Purchase Invoice {0},Die Rechnungsnummer des Lieferanten wurde bereits in Eingangsrechnung {0} verwendet, Supplier Name,Lieferantenname, Supplier Part No,Lieferant Teile-Nr, Supplier Quotation,Lieferantenangebot, @@ -2929,7 +2935,7 @@ Template of terms or contract.,Vorlage für Geschäftsbedingungen oder Vertrag, Templates of supplier scorecard criteria.,Vorlagen der Lieferanten-Scorecard-Kriterien., Templates of supplier scorecard variables.,Vorlagen der Lieferanten-Scorecard-Variablen., Templates of supplier standings.,Vorlagen der Lieferantenwertung., -Temporarily on Hold,Vorübergehend in der Warteschleife, +Temporarily on Hold,Vorübergehend auf Eis gelegt, Temporary,Temporär, Temporary Accounts,Temporäre Konten, Temporary Opening,Temporäre Eröffnungskonten, @@ -3071,7 +3077,7 @@ Total Budget,Gesamtbudget; Gesamtetat, Total Collected: {0},Gesammelt gesammelt: {0}, Total Commission,Gesamtprovision, Total Contribution Amount: {0},Gesamtbeitragsbetrag: {0}, -Total Credit/ Debit Amount should be same as linked Journal Entry,Der Gesamtkreditbetrag sollte identisch mit dem verknüpften Journaleintrag sein, +Total Credit/ Debit Amount should be same as linked Journal Entry,Der Gesamtkreditbetrag sollte identisch mit dem verknüpften Buchungssatz sein, Total Debit must be equal to Total Credit. The difference is {0},Gesamt-Soll muss gleich Gesamt-Haben sein. Die Differenz ist {0}, Total Deduction,Gesamtabzug, Total Invoiced Amount,Gesamtrechnungsbetrag, @@ -3311,7 +3317,7 @@ Workflow,Workflow, Working,In Bearbeitung, Working Hours,Arbeitszeit, Workstation,Arbeitsplatz, -Workstation is closed on the following dates as per Holiday List: {0},Arbeitsplatz ist an folgenden Tagen gemäß der Urlaubsliste geschlossen: {0}, +Workstation is closed on the following dates as per Holiday List: {0},Arbeitsplatz ist an folgenden Tagen gemäß der Feiertagsliste geschlossen: {0}, Wrapping up,Aufwickeln, Wrong Password,Falsches Passwort, Year start date or end date is overlapping with {0}. To avoid please set company,"Jahresbeginn oder Enddatum überlappt mit {0}. Bitte ein Unternehmen wählen, um dies zu verhindern", @@ -3404,7 +3410,7 @@ on,Am, {0} is not in Optional Holiday List,{0} befindet sich nicht in der optionalen Feiertagsliste, {0} is not in a valid Payroll Period,{0} befindet sich nicht in einer gültigen Abrechnungsperiode, {0} is now the default Fiscal Year. Please refresh your browser for the change to take effect.,"{0} ist jetzt das Standardgeschäftsjahr. Bitte aktualisieren Sie Ihren Browser, damit die Änderungen wirksam werden.", -{0} is on hold till {1},{0} ist zurückgestellt bis {1}, +{0} is on hold till {1},{0} ist auf Eis gelegt bis {1}, {0} item found.,{0} Artikel gefunden., {0} items found.,{0} Artikel gefunden., {0} items in progress,{0} Elemente in Bearbeitung, @@ -3424,6 +3430,8 @@ on,Am, {0} variants created.,{0} Varianten erstellt., {0} {1} created,{0} {1} erstellt, {0} {1} does not exist,{0} {1} existiert nicht, +{0} {1} has already been fully paid.,{0} {1} wurde bereits vollständig bezahlt., +{0} {1} has already been partly paid. Please use the 'Get Outstanding Invoice' or the 'Get Outstanding Orders' button to get the latest outstanding amounts.,"{0} {1} wurde bereits teilweise bezahlt. Bitte nutzen Sie den Button 'Ausstehende Rechnungen aufrufen', um die aktuell ausstehenden Beträge zu erhalten.", {0} {1} has been modified. Please refresh.,{0} {1} wurde geändert. Bitte aktualisieren., {0} {1} has not been submitted so the action cannot be completed,"{0} {1} sind nicht gebucht, deshalb kann die Aktion nicht abgeschlossen werden", "{0} {1} is associated with {2}, but Party Account is {3}","{0} {1} ist mit {2} verbunden, aber das Gegenkonto ist {3}", @@ -3435,9 +3443,10 @@ on,Am, {0} {1} is frozen,{0} {1} ist gesperrt, {0} {1} is fully billed,{0} {1} wird voll in Rechnung gestellt, {0} {1} is not active,{0} {1} ist nicht aktiv, -{0} {1} is not associated with {2} {3},{0} {1} ist nicht mit {2} {3} verknüpft, +{0} {1} is not associated with {2} {3},{0} {1} gehört nicht zu {2} {3}, {0} {1} is not present in the parent company,{0} {1} ist in der Muttergesellschaft nicht vorhanden, {0} {1} is not submitted,{0} {1} wurde nicht übertragen, +{0} {1} is on hold,{0} {1} liegt derzeit auf Eis, {0} {1} is {2},{0} {1} ist {2}, {0} {1} must be submitted,{0} {1} muss vorgelegt werden, {0} {1} not in any active Fiscal Year.,{0} {1} nicht in einem aktiven Geschäftsjahr., @@ -3566,7 +3575,7 @@ Account is mandatory to get payment entries,"Das Konto ist obligatorisch, um Zah Account is not set for the dashboard chart {0},Konto ist nicht für das Dashboard-Diagramm {0} festgelegt., Account {0} does not exists in the dashboard chart {1},Konto {0} ist im Dashboard-Diagramm {1} nicht vorhanden, Account: {0} is capital Work in progress and can not be updated by Journal Entry,Konto: {0} ist in Bearbeitung und kann von Journal Entry nicht aktualisiert werden, -Account: {0} is not permitted under Payment Entry,Konto: {0} ist unter Zahlungseingang nicht zulässig, +Account: {0} is not permitted under Payment Entry,Konto {0} kann nicht in Zahlung verwendet werden, Accounting Dimension {0} is required for 'Balance Sheet' account {1}.,Die Buchhaltungsdimension {0} ist für das Bilanzkonto {1} erforderlich., Accounting Dimension {0} is required for 'Profit and Loss' account {1}.,Die Buchhaltungsdimension {0} ist für das Konto {1} "Gewinn und Verlust" erforderlich., Accounting Masters,Accounting Masters, @@ -3574,6 +3583,7 @@ Accounting Period overlaps with {0},Abrechnungszeitraum überschneidet sich mit Activity,Aktivität, Add / Manage Email Accounts.,Hinzufügen/Verwalten von E-Mail-Konten, Add Child,Unterpunkt hinzufügen, +Add Local Holidays,Lokale Feiertage hinzufügen, Add Multiple,Mehrere hinzufügen, Add Participants,Teilnehmer hinzufügen, Add to Featured Item,Zum empfohlenen Artikel hinzufügen, @@ -3626,6 +3636,8 @@ Billing Interval Count cannot be less than 1,Die Anzahl der Abrechnungsintervall Blue,Blau, Book,Buchen, Book Appointment,Einen Termin verabreden, +Book Advance Payments as Liability option is chosen. Paid From account changed from {0} to {1}.,"Die Option 'Anzahlungen als Verbindlichkeit buchen' ist aktiviert. Das Ausgangskonto wurde von {0} auf {1} geändert.", +Book Advance Payments in Separate Party Account,Anzahlungen als Verbindlichkeit buchen, Brand,Marke, Browse,Durchsuchen, Call Connected,Anruf verbunden, @@ -3835,10 +3847,12 @@ No correct answer is set for {0},Für {0} ist keine richtige Antwort festgelegt. No description,Keine Beschreibung, No issue has been raised by the caller.,Der Anrufer hat kein Problem angesprochen., No items to publish,Keine Artikel zu veröffentlichen, +No outstanding {0} found for the {1} {2} which qualify the filters you have specified.,"Für {1} {2} wurden kein ausstehender Beleg vom Typ {0} gefunden, der den angegebenen Filtern entspricht.", No outstanding invoices found,Keine offenen Rechnungen gefunden, No outstanding invoices found for the {0} {1} which qualify the filters you have specified.,"Für {0} {1} wurden keine ausstehenden Rechnungen gefunden, die die von Ihnen angegebenen Filter qualifizieren.", No outstanding invoices require exchange rate revaluation,Keine ausstehenden Rechnungen erfordern eine Neubewertung des Wechselkurses, No reviews yet,Noch keine Bewertungen, +No Stock Available Currently,Derzeit kein Lagerbestand verfügbar, No views yet,Noch keine Ansichten, Non stock items,Nicht vorrätige Artikel, Not Allowed,Nicht Erlaubt, @@ -3924,8 +3938,8 @@ Publish More Items,Veröffentlichen Sie weitere Elemente, Publish Your First Items,Veröffentlichen Sie Ihre ersten Artikel, Publish {0} Items,{0} Elemente veröffentlichen, Published Items,Veröffentlichte Artikel, -Purchase Invoice cannot be made against an existing asset {0},Kaufrechnung kann nicht für ein vorhandenes Asset erstellt werden {0}, -Purchase Invoices,Rechnungen kaufen, +Purchase Invoice cannot be made against an existing asset {0},Eingangsrechnung kann nicht für ein vorhandenes Asset erstellt werden {0}, +Purchase Invoices,Eingangsrechnungen, Purchase Orders,Kauforder, Purchase Receipt doesn't have any Item for which Retain Sample is enabled.,"Der Kaufbeleg enthält keinen Artikel, für den die Option "Probe aufbewahren" aktiviert ist.", Purchase Return,Warenrücksendung, @@ -3961,20 +3975,20 @@ Review,Rezension, Room,Zimmer, Room Type,Zimmertyp, Row # ,Zeile #, -Row #{0}: Accepted Warehouse and Supplier Warehouse cannot be same,Zeile # {0}: Akzeptiertes Lager und Lieferantenlager können nicht identisch sein, -Row #{0}: Cannot delete item {1} which has already been billed.,Zeile # {0}: Der bereits abgerechnete Artikel {1} kann nicht gelöscht werden., -Row #{0}: Cannot delete item {1} which has already been delivered,"Zeile # {0}: Element {1}, das bereits geliefert wurde, kann nicht gelöscht werden", -Row #{0}: Cannot delete item {1} which has already been received,"Zeile # {0}: Element {1}, das bereits empfangen wurde, kann nicht gelöscht werden", -Row #{0}: Cannot delete item {1} which has work order assigned to it.,"Zeile # {0}: Element {1}, dem ein Arbeitsauftrag zugewiesen wurde, kann nicht gelöscht werden.", -Row #{0}: Cannot delete item {1} which is assigned to customer's purchase order.,"Zeile # {0}: Artikel {1}, der der Bestellung des Kunden zugeordnet ist, kann nicht gelöscht werden.", -Row #{0}: Cannot select Supplier Warehouse while suppling raw materials to subcontractor,"Zeile # {0}: Supplier Warehouse kann nicht ausgewählt werden, während Rohstoffe an Subunternehmer geliefert werden", -Row #{0}: Cost Center {1} does not belong to company {2},Zeile # {0}: Kostenstelle {1} gehört nicht zu Firma {2}, -Row #{0}: Operation {1} is not completed for {2} qty of finished goods in Work Order {3}. Please update operation status via Job Card {4}.,Zeile # {0}: Vorgang {1} ist für {2} Fertigwarenmenge im Fertigungsauftrag {3} nicht abgeschlossen. Bitte aktualisieren Sie den Betriebsstatus über die Jobkarte {4}., -Row #{0}: Payment document is required to complete the transaction,"Zeile # {0}: Der Zahlungsbeleg ist erforderlich, um die Transaktion abzuschließen", -Row #{0}: Serial No {1} does not belong to Batch {2},Zeile # {0}: Seriennummer {1} gehört nicht zu Charge {2}, -Row #{0}: Service End Date cannot be before Invoice Posting Date,Zeile # {0}: Das Service-Enddatum darf nicht vor dem Rechnungsbuchungsdatum liegen, -Row #{0}: Service Start Date cannot be greater than Service End Date,Zeile # {0}: Das Servicestartdatum darf nicht höher als das Serviceenddatum sein, -Row #{0}: Service Start and End Date is required for deferred accounting,Zeile # {0}: Das Start- und Enddatum des Service ist für die aufgeschobene Abrechnung erforderlich, +Row #{0}: Accepted Warehouse and Supplier Warehouse cannot be same,Zeile {0}: Akzeptiertes Lager und Lieferantenlager können nicht identisch sein, +Row #{0}: Cannot delete item {1} which has already been billed.,Zeile {0}: Der bereits abgerechnete Artikel {1} kann nicht gelöscht werden., +Row #{0}: Cannot delete item {1} which has already been delivered,"Zeile {0}: Element {1}, das bereits geliefert wurde, kann nicht gelöscht werden", +Row #{0}: Cannot delete item {1} which has already been received,"Zeile {0}: Element {1}, das bereits empfangen wurde, kann nicht gelöscht werden", +Row #{0}: Cannot delete item {1} which has work order assigned to it.,"Zeile {0}: Element {1}, dem ein Arbeitsauftrag zugewiesen wurde, kann nicht gelöscht werden.", +Row #{0}: Cannot delete item {1} which is assigned to customer's purchase order.,"Zeile {0}: Artikel {1}, der der Bestellung des Kunden zugeordnet ist, kann nicht gelöscht werden.", +Row #{0}: Cannot select Supplier Warehouse while suppling raw materials to subcontractor,"Zeile {0}: Supplier Warehouse kann nicht ausgewählt werden, während Rohstoffe an Subunternehmer geliefert werden", +Row #{0}: Cost Center {1} does not belong to company {2},Zeile {0}: Kostenstelle {1} gehört nicht zu Firma {2}, +Row #{0}: Operation {1} is not completed for {2} qty of finished goods in Work Order {3}. Please update operation status via Job Card {4}.,Zeile {0}: Vorgang {1} ist für {2} Fertigwarenmenge im Fertigungsauftrag {3} nicht abgeschlossen. Bitte aktualisieren Sie den Betriebsstatus über die Jobkarte {4}., +Row #{0}: Payment document is required to complete the transaction,"Zeile {0}: Der Zahlungsbeleg ist erforderlich, um die Transaktion abzuschließen", +Row #{0}: Serial No {1} does not belong to Batch {2},Zeile {0}: Seriennummer {1} gehört nicht zu Charge {2}, +Row #{0}: Service End Date cannot be before Invoice Posting Date,Zeile {0}: Das Service-Enddatum darf nicht vor dem Rechnungsbuchungsdatum liegen, +Row #{0}: Service Start Date cannot be greater than Service End Date,Zeile {0}: Das Servicestartdatum darf nicht höher als das Serviceenddatum sein, +Row #{0}: Service Start and End Date is required for deferred accounting,Zeile {0}: Das Start- und Enddatum des Service ist für die aufgeschobene Abrechnung erforderlich, Row {0}: Invalid Item Tax Template for item {1},Zeile {0}: Ungültige Artikelsteuervorlage für Artikel {1}, Row {0}: Quantity not available for {4} in warehouse {1} at posting time of the entry ({2} {3}),Zeile {0}: Menge für {4} in Lager {1} zum Buchungszeitpunkt des Eintrags nicht verfügbar ({2} {3}), Row {0}: user has not applied the rule {1} on the item {2},Zeile {0}: Der Nutzer hat die Regel {1} nicht auf das Element {2} angewendet., @@ -4033,6 +4047,7 @@ Stock Ledger ID,Bestandsbuch-ID, Stock Value ({0}) and Account Balance ({1}) are out of sync for account {2} and it's linked warehouses.,Der Bestandswert ({0}) und der Kontostand ({1}) sind für das Konto {2} und die verknüpften Lager nicht synchron., Stores - {0},Stores - {0}, Student with email {0} does not exist,Der Student mit der E-Mail-Adresse {0} existiert nicht, +Subdivision,Teilgebiet, Submit Review,Bewertung abschicken, Submitted,Gebucht, Supplier Addresses And Contacts,Lieferanten-Adressen und Kontaktdaten, @@ -4179,6 +4194,7 @@ Mode Of Payment,Zahlungsart, No students Found,Keine Schüler gefunden, Not in Stock,Nicht lagernd, Please select a Customer,Bitte wählen Sie einen Kunden aus, +Please select a country,Bitte wählen Sie ein Land aus, Printed On,Gedruckt auf, Received From,Erhalten von, Sales Person,Verkäufer, @@ -4236,7 +4252,7 @@ to,An, Cards,Karten, Percentage,Prozentsatz, Failed to setup defaults for country {0}. Please contact support@erpnext.com,Fehler beim Einrichten der Standardeinstellungen für Land {0}. Bitte wenden Sie sich an support@erpnext.com, -Row #{0}: Item {1} is not a Serialized/Batched Item. It cannot have a Serial No/Batch No against it.,Zeile # {0}: Element {1} ist kein serialisiertes / gestapeltes Element. Es kann keine Seriennummer / Chargennummer dagegen haben., +Row #{0}: Item {1} is not a Serialized/Batched Item. It cannot have a Serial No/Batch No against it.,Zeile {0}: Element {1} ist kein serialisiertes / gestapeltes Element. Es kann keine Seriennummer / Chargennummer dagegen haben., Please set {0},Bitte {0} setzen, Please set {0},Bitte setzen Sie {0},supplier Draft,Entwurf,"docstatus,=,0" @@ -4323,7 +4339,7 @@ Row {0}: {1} is required in the expenses table to book an expense claim.,"Zeile Set the default account for the {0} {1},Legen Sie das Standardkonto für {0} {1} fest, (Half Day),(Halber Tag), Income Tax Slab,Einkommensteuerplatte, -Row #{0}: Cannot set amount or formula for Salary Component {1} with Variable Based On Taxable Salary,Zeile # {0}: Betrag oder Formel für Gehaltskomponente {1} mit Variable basierend auf steuerpflichtigem Gehalt kann nicht festgelegt werden, +Row #{0}: Cannot set amount or formula for Salary Component {1} with Variable Based On Taxable Salary,Zeile {0}: Betrag oder Formel für Gehaltskomponente {1} mit Variable basierend auf steuerpflichtigem Gehalt kann nicht festgelegt werden, Row #{}: {} of {} should be {}. Please modify the account or select a different account.,Zeile # {}: {} von {} sollte {} sein. Bitte ändern Sie das Konto oder wählen Sie ein anderes Konto aus., Row #{}: Please asign task to a member.,Zeile # {}: Bitte weisen Sie einem Mitglied eine Aufgabe zu., Process Failed,Prozess fehlgeschlagen, @@ -4332,10 +4348,11 @@ Please set Warehouse in Woocommerce Settings,Bitte stellen Sie Warehouse in den Row {0}: Delivery Warehouse ({1}) and Customer Warehouse ({2}) can not be same,Zeile {0}: Lieferlager ({1}) und Kundenlager ({2}) können nicht identisch sein, Row {0}: Due Date in the Payment Terms table cannot be before Posting Date,Zeile {0}: Fälligkeitsdatum in der Tabelle "Zahlungsbedingungen" darf nicht vor dem Buchungsdatum liegen, Cannot find {} for item {}. Please set the same in Item Master or Stock Settings.,{} Für Element {} kann nicht gefunden werden. Bitte stellen Sie dasselbe in den Artikelstamm- oder Lagereinstellungen ein., -Row #{0}: The batch {1} has already expired.,Zeile # {0}: Der Stapel {1} ist bereits abgelaufen., +Row #{0}: The batch {1} has already expired.,Zeile {0}: Der Stapel {1} ist bereits abgelaufen., Start Year and End Year are mandatory,Startjahr und Endjahr sind obligatorisch, GL Entry,Buchung zum Hauptbuch, Cannot allocate more than {0} against payment term {1},Es kann nicht mehr als {0} für die Zahlungsbedingung {1} zugeordnet werden., +Cannot receive from customer against negative outstanding,Negativer Gesamtbetrag kann nicht vom Kunden empfangen werden, The root account {0} must be a group,Das Root-Konto {0} muss eine Gruppe sein, Shipping rule not applicable for country {0} in Shipping Address,Versandregel gilt nicht für Land {0} in Versandadresse, Get Payments from,Zahlungen erhalten von, @@ -4475,7 +4492,7 @@ Determine Address Tax Category From,Adresssteuerkategorie bestimmen von, Over Billing Allowance (%),Mehr als Abrechnungsbetrag (%), Credit Controller,Kredit-Controller, Check Supplier Invoice Number Uniqueness,"Aktivieren, damit dieselbe Lieferantenrechnungsnummer nur einmal vorkommen kann", -Make Payment via Journal Entry,Zahlung über Journaleintrag, +Make Payment via Journal Entry,Zahlung über Buchungssatz, Unlink Payment on Cancellation of Invoice,Zahlung bei Stornierung der Rechnung aufheben, Book Asset Depreciation Entry Automatically,Vermögensabschreibung automatisch verbuchen, Automatically Add Taxes and Charges from Item Tax Template,Steuern und Gebühren aus Artikelsteuervorlage automatisch hinzufügen, @@ -4544,7 +4561,7 @@ Bank Statement Transaction Entry,Kontoauszug Transaktionseintrag, Bank Transaction Entries,Banktransaktionseinträge, New Transactions,Neue Transaktionen, Match Transaction to Invoices,Transaktion mit Rechnungen abgleichen, -Create New Payment/Journal Entry,Erstellen Sie eine neue Zahlung / Journaleintrag, +Create New Payment/Journal Entry,Erstellen Sie eine neue Zahlung / Buchungssatz, Submit/Reconcile Payments,Zahlungen absenden / abstimmen, Matching Invoices,Passende Rechnungen, Payment Invoice Items,Zahlung Rechnungspositionen, @@ -4797,6 +4814,7 @@ Account Paid To,Eingangskonto, Paid Amount (Company Currency),Gezahlter Betrag (Unternehmenswährung), Received Amount,erhaltenen Betrag, Received Amount (Company Currency),Erhaltene Menge (Gesellschaft Währung), +Received Amount cannot be greater than Paid Amount,Der erhaltene Betrag darf nicht größer sein als der gezahlte Betrag, Get Outstanding Invoice,Erhalten Sie eine ausstehende Rechnung, Payment References,Bezahlung Referenzen, Writeoff,Abschreiben, @@ -4945,7 +4963,7 @@ Apply Tax Withholding Amount,Steuereinbehaltungsbetrag anwenden, Accounting Dimensions ,Buchhaltung Dimensionen, Supplier Invoice Details,Lieferant Rechnungsdetails, Supplier Invoice Date,Lieferantenrechnungsdatum, -Return Against Purchase Invoice,Zurück zur Eingangsrechnung, +Return Against Purchase Invoice,Gutschrift zur Eingangsrechnung, Select Supplier Address,Lieferantenadresse auswählen, Contact Person,Kontaktperson, Select Shipping Address,Lieferadresse auswählen, @@ -4995,7 +5013,7 @@ Terms,Geschäftsbedingungen, Terms and Conditions1,Allgemeine Geschäftsbedingungen1, Group same items,Gruppe gleichen Artikel, Print Language,Drucksprache, -"Once set, this invoice will be on hold till the set date","Einmal eingestellt, wird diese Rechnung bis zum festgelegten Datum gehalten", +"Once set, this invoice will be on hold till the set date","Einmal eingestellt, liegt diese Rechnung bis zum festgelegten Datum auf Eis", Credit To,Gutschreiben auf, Party Account Currency,Währung des Kontos der Partei, Against Expense Account,Zu Aufwandskonto, @@ -5319,7 +5337,7 @@ Asset Owner,Eigentümer des Vermögenswertes, Asset Owner Company,Eigentümergesellschaft, Custodian,Depotbank, Disposal Date,Verkauf Datum, -Journal Entry for Scrap,Journaleintrag für Ausschuss, +Journal Entry for Scrap,Buchungssatz für Ausschuss, Available-for-use Date,Verfügbarkeitsdatum, Calculate Depreciation,Abschreibung berechnen, Allow Monthly Depreciation,Monatliche Abschreibung zulassen, @@ -5481,8 +5499,8 @@ Default Bank Account,Standardbankkonto, Is Transporter,Ist Transporter, Represents Company,Repräsentiert das Unternehmen, Supplier Type,Lieferantentyp, -Allow Purchase Invoice Creation Without Purchase Order,Erstellen von Kaufrechnungen ohne Bestellung zulassen, -Allow Purchase Invoice Creation Without Purchase Receipt,Zulassen der Erstellung von Kaufrechnungen ohne Kaufbeleg, +Allow Purchase Invoice Creation Without Purchase Order,Erstellen von Eingangsrechnung ohne Bestellung zulassen, +Allow Purchase Invoice Creation Without Purchase Receipt,Erstellen von Eingangsrechnung ohne Kaufbeleg ohne Kaufbeleg zulassen, Warn RFQs,Warnung Ausschreibungen, Warn POs,Warnen Sie POs, Prevent RFQs,Vermeidung von Ausschreibungen, @@ -6482,7 +6500,7 @@ Reports to,Vorgesetzter, Attendance and Leave Details,Anwesenheits- und Urlaubsdetails, Leave Policy,Urlaubsrichtlinie, Attendance Device ID (Biometric/RF tag ID),Anwesenheitsgeräte-ID (biometrische / RF-Tag-ID), -Applicable Holiday List,Geltende Urlaubsliste, +Applicable Holiday List,Geltende Feiertagsliste, Default Shift,Standardverschiebung, Salary Details,Gehaltsdetails, Salary Mode,Gehaltsmodus, @@ -6647,12 +6665,12 @@ Unclaimed amount,Nicht beanspruchter Betrag, Expense Claim Detail,Auslage, Expense Date,Datum der Auslage, Expense Claim Type,Art der Auslagenabrechnung, -Holiday List Name,Urlaubslistenname, -Total Holidays,Insgesamt Feiertage, -Add Weekly Holidays,Wöchentliche Feiertage hinzufügen, +Holiday List Name,Name der Feiertagsliste, +Total Holidays,Insgesamt freie Tage, +Add Weekly Holidays,Wöchentlich freie Tage hinzufügen, Weekly Off,Wöchentlich frei, -Add to Holidays,Zu Feiertagen hinzufügen, -Holidays,Ferien, +Add to Holidays,Zu freien Tagen hinzufügen, +Holidays,Arbeitsfreie Tage, Clear Table,Tabelle leeren, HR Settings,Einstellungen zum Modul Personalwesen, Employee Settings,Mitarbeitereinstellungen, @@ -6762,7 +6780,7 @@ Transaction Name,Transaktionsname, Is Carry Forward,Ist Übertrag, Is Expired,Ist abgelaufen, Is Leave Without Pay,Ist unbezahlter Urlaub, -Holiday List for Optional Leave,Urlaubsliste für optionalen Urlaub, +Holiday List for Optional Leave,Feiertagsliste für optionalen Urlaub, Leave Allocations,Zuteilungen verlassen, Leave Policy Details,Urlaubsrichtliniendetails, Leave Policy Detail,Urlaubsrichtliniendetail, @@ -7631,7 +7649,7 @@ Legal Entity / Subsidiary with a separate Chart of Accounts belonging to the Org Change Abbreviation,Abkürzung ändern, Parent Company,Muttergesellschaft, Default Values,Standardwerte, -Default Holiday List,Standard-Urlaubsliste, +Default Holiday List,Standard Feiertagsliste, Default Selling Terms,Standardverkaufsbedingungen, Default Buying Terms,Standard-Einkaufsbedingungen, Create Chart Of Accounts Based On,"Kontenplan erstellen, basierend auf", @@ -7667,7 +7685,7 @@ Enable Perpetual Inventory,Permanente Inventur aktivieren, Default Inventory Account,Standard Inventurkonto, Stock Adjustment Account,Bestandskorrektur-Konto, Fixed Asset Depreciation Settings,Einstellungen Abschreibung von Anlagevermögen, -Series for Asset Depreciation Entry (Journal Entry),Serie für Abschreibungs-Eintrag (Journaleintrag), +Series for Asset Depreciation Entry (Journal Entry),Serie für Abschreibungs-Eintrag (Buchungssatz), Gain/Loss Account on Asset Disposal,Gewinn-/Verlustrechnung auf die Veräußerung von Vermögenswerten, Asset Depreciation Cost Center,Kostenstelle für Vermögenswertabschreibung, Budget Detail,Budget-Detail, @@ -8079,7 +8097,7 @@ Price Not UOM Dependent,Preis nicht UOM abhängig, Applicable for Countries,Anwenden für Länder, Price List Country,Preisliste Land, MAT-PRE-.YYYY.-,MAT-PRE-.JJJJ.-, -Supplier Delivery Note,Lieferumfang, +Supplier Delivery Note,Lieferschein Nr., Time at which materials were received,"Zeitpunkt, zu dem Materialien empfangen wurden", Return Against Purchase Receipt,Zurück zum Kaufbeleg, Rate at which supplier's currency is converted to company's base currency,"Kurs, zu dem die Währung des Lieferanten in die Basiswährung des Unternehmens umgerechnet wird", @@ -8453,10 +8471,10 @@ Automatically Process Deferred Accounting Entry,Aufgeschobene Buchungsbuchung au Bank Clearance,Bankfreigabe, Bank Clearance Detail,Bankfreigabedetail, Update Cost Center Name / Number,Name / Nummer der Kostenstelle aktualisieren, -Journal Entry Template,Journaleintragsvorlage, +Journal Entry Template,Buchungssatz-Vorlage, Template Title,Vorlagentitel, -Journal Entry Type,Journaleintragstyp, -Journal Entry Template Account,Journaleintragsvorlagenkonto, +Journal Entry Type,Buchungssatz-Typ, +Journal Entry Template Account,Buchungssatzvorlagenkonto, Process Deferred Accounting,Aufgeschobene Buchhaltung verarbeiten, Manual entry cannot be created! Disable automatic entry for deferred accounting in accounts settings and try again,Manuelle Eingabe kann nicht erstellt werden! Deaktivieren Sie die automatische Eingabe für die verzögerte Buchhaltung in den Konteneinstellungen und versuchen Sie es erneut, End date cannot be before start date,Das Enddatum darf nicht vor dem Startdatum liegen, @@ -8531,7 +8549,7 @@ Deferred Accounting Settings,Aufgeschobene Buchhaltungseinstellungen, Book Deferred Entries Based On,Buch verzögerte Einträge basierend auf, Days,Tage, Months,Monate, -Book Deferred Entries Via Journal Entry,Buch verzögerte Einträge über Journaleintrag, +Book Deferred Entries Via Journal Entry,Separaten Buchungssatz für latente Buchungen erstellen, Submit Journal Entries,Journaleinträge senden, If this is unchecked Journal Entries will be saved in a Draft state and will have to be submitted manually,"Wenn dieses Kontrollkästchen deaktiviert ist, werden Journaleinträge in einem Entwurfsstatus gespeichert und müssen manuell übermittelt werden", Enable Distributed Cost Center,Aktivieren Sie die verteilte Kostenstelle, @@ -8560,7 +8578,7 @@ Dunning Letter,Mahnbrief, Reference Detail No,Referenz Detail Nr, Custom Remarks,Benutzerdefinierte Bemerkungen, Please select a Company first.,Bitte wählen Sie zuerst eine Firma aus., -"Row #{0}: Reference Document Type must be one of Sales Order, Sales Invoice, Journal Entry or Dunning","Zeile # {0}: Der Referenzdokumenttyp muss Auftrag, Ausgangsrechnung, Journaleintrag oder Mahnwesen sein", +"Row #{0}: Reference Document Type must be one of Sales Order, Sales Invoice, Journal Entry or Dunning","Zeile {0}: Der Referenzdokumenttyp muss Auftrag, Ausgangsrechnung, Buchungssatz oder Mahnung sein", POS Closing Entry,POS Closing Entry, POS Opening Entry,POS-Eröffnungseintrag, POS Transactions,POS-Transaktionen, @@ -8679,9 +8697,9 @@ Depreciation Posting Date,Buchungsdatum der Abschreibung, "By default, the Supplier Name is set as per the Supplier Name entered. If you want Suppliers to be named by a ","Standardmäßig wird der Lieferantenname gemäß dem eingegebenen Lieferantennamen festgelegt. Wenn Sie möchten, dass Lieferanten von a benannt werden", choose the 'Naming Series' option.,Wählen Sie die Option "Naming Series"., Configure the default Price List when creating a new Purchase transaction. Item prices will be fetched from this Price List.,Konfigurieren Sie die Standardpreisliste beim Erstellen einer neuen Kauftransaktion. Artikelpreise werden aus dieser Preisliste abgerufen., -"If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice or Receipt without creating a Purchase Order first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Order' checkbox in the Supplier master.","Wenn diese Option auf "Ja" konfiguriert ist, verhindert ERPNext, dass Sie eine Kaufrechnung oder einen Beleg erstellen können, ohne zuvor eine Bestellung zu erstellen. Diese Konfiguration kann für einen bestimmten Lieferanten überschrieben werden, indem das Kontrollkästchen "Erstellung von Eingangsrechnungen ohne Bestellung zulassen" im Lieferantenstamm aktiviert wird.", -"If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice without creating a Purchase Receipt first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Receipt' checkbox in the Supplier master.","Wenn diese Option auf "Ja" konfiguriert ist, verhindert ERPNext, dass Sie eine Kaufrechnung erstellen können, ohne zuvor einen Kaufbeleg zu erstellen. Diese Konfiguration kann für einen bestimmten Lieferanten überschrieben werden, indem das Kontrollkästchen "Erstellung von Kaufrechnungen ohne Kaufbeleg zulassen" im Lieferantenstamm aktiviert wird.", -Quantity & Stock,Menge & Lager, +"If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice or Receipt without creating a Purchase Order first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Order' checkbox in the Supplier master.","Wenn diese Option auf 'Ja' gesetzt ist, validiert ERPNext, dass Sie eine Bestellung angelegt haben, bevor Sie eine Eingangsrechnung oder einen Kaufbeleg erfassen können. Diese Konfiguration kann für einzelne Lieferanten überschrieben werden, indem Sie die Option 'Erstellung von Eingangsrechnungen ohne Bestellung zulassen' im Lieferantenstamm aktivieren.", +"If this option is configured 'Yes', ERPNext will prevent you from creating a Purchase Invoice without creating a Purchase Receipt first. This configuration can be overridden for a particular supplier by enabling the 'Allow Purchase Invoice Creation Without Purchase Receipt' checkbox in the Supplier master.","Wenn diese Option auf 'Ja' gesetzt ist, validiert ERPNext, dass Sie einen Kaufbeleg angelegt haben, bevor Sie eine Eingangsrechnung erfasen können. Diese Konfiguration kann für einzelne Lieferanten überschrieben werden, indem Sie die Option 'Erstellung von Kaufrechnungen ohne Kaufbeleg zulassen' im Lieferantenstamm aktivieren.", +Quantity & Stock,Menge & Lager, Call Details,Anrufdetails, Authorised By,Authorisiert von, Signee (Company),Unterzeichner (Firma), @@ -8915,7 +8933,7 @@ Rented To Date,Bisher vermietet, Monthly Eligible Amount,Monatlicher förderfähiger Betrag, Total Eligible HRA Exemption,Insgesamt berechtigte HRA-Befreiung, Validating Employee Attendance...,Überprüfung der Mitarbeiterbeteiligung ..., -Submitting Salary Slips and creating Journal Entry...,Einreichen von Gehaltsabrechnungen und Erstellen eines Journaleintrags ..., +Submitting Salary Slips and creating Journal Entry...,Gehaltsabrechnungen werden gebucht und Buchungssätze erstellt..., Calculate Payroll Working Days Based On,Berechnen Sie die Arbeitstage der Personalabrechnung basierend auf, Consider Unmarked Attendance As,Betrachten Sie die nicht markierte Teilnahme als, Fraction of Daily Salary for Half Day,Bruchteil des Tagesgehalts für einen halben Tag, @@ -8968,12 +8986,12 @@ Show 'Scan Barcode' field above every child table to insert Items with ease.,"Ze "If blank, parent Warehouse Account or company default will be considered in transactions","Wenn leer, wird das übergeordnete Lagerkonto oder der Firmenstandard bei Transaktionen berücksichtigt", Service Level Agreement Details,Details zum Service Level Agreement, Service Level Agreement Status,Status des Service Level Agreements, -On Hold Since,In der Warteschleife seit, +On Hold Since,Auf Eis gelegt seit, Total Hold Time,Gesamte Haltezeit, Response Details,Antwortdetails, Average Response Time,Durchschnittliche Reaktionszeit, User Resolution Time,Benutzerauflösungszeit, -SLA is on hold since {0},"SLA wird gehalten, da {0}", +SLA is on hold since {0},"SLA ist seit {0} auf Eis gelegt", Pause SLA On Status,SLA On Status anhalten, Pause SLA On,SLA anhalten Ein, Greetings Section,Grüße Abschnitt, @@ -9187,6 +9205,7 @@ Total Assets,Gesamtvermögen, New Assets (This Year),Neue Vermögenswerte (dieses Jahr), Row #{}: Depreciation Posting Date should not be equal to Available for Use Date.,Zeile # {}: Das Buchungsdatum der Abschreibung sollte nicht dem Datum der Verfügbarkeit entsprechen., Incorrect Date,Falsches Datum, +Incorrect Payment Type,Falsche Zahlungsart, Invalid Gross Purchase Amount,Ungültiger Bruttokaufbetrag, There are active maintenance or repairs against the asset. You must complete all of them before cancelling the asset.,"Es gibt aktive Wartungs- oder Reparaturarbeiten am Vermögenswert. Sie müssen alle Schritte ausführen, bevor Sie das Asset stornieren können.", % Complete,% Komplett, @@ -9209,7 +9228,7 @@ Please check your Plaid client ID and secret values,Bitte überprüfen Sie Ihre Bank transaction creation error,Fehler beim Erstellen der Banküberweisung, Unit of Measurement,Maßeinheit, Fiscal Year {0} Does Not Exist,Geschäftsjahr {0} existiert nicht, -Row # {0}: Returned Item {1} does not exist in {2} {3},Zeile # {0}: Zurückgegebenes Element {1} ist in {2} {3} nicht vorhanden, +Row # {0}: Returned Item {1} does not exist in {2} {3},Zeile {0}: Zurückgegebenes Element {1} ist in {2} {3} nicht vorhanden, Valuation type charges can not be marked as Inclusive,Bewertungsgebühren können nicht als Inklusiv gekennzeichnet werden, You do not have permissions to {} items in a {}.,Sie haben keine Berechtigungen für {} Elemente in einem {}., Insufficient Permissions,Nicht ausreichende Berechtigungen, @@ -9220,7 +9239,7 @@ Invalid Value,Ungültiger Wert, The value {0} is already assigned to an existing Item {1}.,Der Wert {0} ist bereits einem vorhandenen Element {1} zugeordnet., "To still proceed with editing this Attribute Value, enable {0} in Item Variant Settings.","Aktivieren Sie {0} in den Einstellungen für Elementvarianten, um mit der Bearbeitung dieses Attributwerts fortzufahren.", Edit Not Allowed,Bearbeiten nicht erlaubt, -Row #{0}: Item {1} is already fully received in Purchase Order {2},Zeile # {0}: Artikel {1} ist bereits vollständig in der Bestellung {2} eingegangen., +Row #{0}: Item {1} is already fully received in Purchase Order {2},Zeile {0}: Artikel {1} ist bereits vollständig in der Bestellung {2} eingegangen., You cannot create or cancel any accounting entries with in the closed Accounting Period {0},Sie können im abgeschlossenen Abrechnungszeitraum {0} keine Buchhaltungseinträge mit erstellen oder stornieren., POS Invoice should have {} field checked.,Für die POS-Rechnung sollte das Feld {} aktiviert sein., Invalid Item,Ungültiger Artikel, @@ -9315,7 +9334,7 @@ Items Required,Erforderliche Artikel, Operation {0} does not belong to the work order {1},Operation {0} gehört nicht zum Arbeitsauftrag {1}, Print UOM after Quantity,UOM nach Menge drucken, Set default {0} account for perpetual inventory for non stock items,Legen Sie das Standardkonto {0} für die fortlaufende Bestandsaufnahme für nicht vorrätige Artikel fest, -Row #{0}: Child Item should not be a Product Bundle. Please remove Item {1} and Save,Zeile # {0}: Untergeordnetes Element sollte kein Produktpaket sein. Bitte entfernen Sie Artikel {1} und speichern Sie, +Row #{0}: Child Item should not be a Product Bundle. Please remove Item {1} and Save,Zeile {0}: Untergeordnetes Element sollte kein Produktpaket sein. Bitte entfernen Sie Artikel {1} und speichern Sie, Credit limit reached for customer {0},Kreditlimit für Kunde erreicht {0}, Could not auto create Customer due to the following missing mandatory field(s):,Der Kunde konnte aufgrund der folgenden fehlenden Pflichtfelder nicht automatisch erstellt werden:, Please create Customer from Lead {0}.,Bitte erstellen Sie einen Kunden aus Lead {0}., @@ -9327,7 +9346,7 @@ Payroll date can not be less than employee's joining date.,Das Abrechnungsdatum From date can not be less than employee's joining date.,Ab dem Datum darf das Beitrittsdatum des Mitarbeiters nicht unterschritten werden., To date can not be greater than employee's relieving date.,Bisher kann das Entlastungsdatum des Mitarbeiters nicht überschritten werden., Payroll date can not be greater than employee's relieving date.,Das Abrechnungsdatum darf nicht größer sein als das Entlastungsdatum des Mitarbeiters., -Row #{0}: Please enter the result value for {1},Zeile # {0}: Bitte geben Sie den Ergebniswert für {1} ein, +Row #{0}: Please enter the result value for {1},Zeile {0}: Bitte geben Sie den Ergebniswert für {1} ein, Mandatory Results,Obligatorische Ergebnisse, Sales Invoice or Patient Encounter is required to create Lab Tests,Für die Erstellung von Labortests ist eine Ausgangsrechnung oder eine Patientenbegegnung erforderlich, Insufficient Data,Unzureichende Daten, @@ -9335,12 +9354,12 @@ Lab Test(s) {0} created successfully,Labortest (e) {0} erfolgreich erstellt, Test :,Prüfung :, Sample Collection {0} has been created,Die Probensammlung {0} wurde erstellt, Normal Range: ,Normalbereich:, -Row #{0}: Check Out datetime cannot be less than Check In datetime,Zeile # {0}: Die Check-out-Datumszeit darf nicht kleiner als die Check-In-Datumszeit sein, +Row #{0}: Check Out datetime cannot be less than Check In datetime,Zeile {0}: Die Check-out-Datumszeit darf nicht kleiner als die Check-In-Datumszeit sein, "Missing required details, did not create Inpatient Record","Fehlende erforderliche Details, keine stationäre Aufzeichnung erstellt", Unbilled Invoices,Nicht in Rechnung gestellte Rechnungen, Standard Selling Rate should be greater than zero.,Die Standardverkaufsrate sollte größer als Null sein., Conversion Factor is mandatory,Der Umrechnungsfaktor ist obligatorisch, -Row #{0}: Conversion Factor is mandatory,Zeile # {0}: Der Umrechnungsfaktor ist obligatorisch, +Row #{0}: Conversion Factor is mandatory,Zeile {0}: Der Umrechnungsfaktor ist obligatorisch, Sample Quantity cannot be negative or 0,Die Probenmenge darf nicht negativ oder 0 sein, Invalid Quantity,Ungültige Menge, "Please set defaults for Customer Group, Territory and Selling Price List in Selling Settings","Bitte legen Sie in den Verkaufseinstellungen die Standardeinstellungen für Kundengruppe, Gebiet und Verkaufspreisliste fest", @@ -9532,7 +9551,7 @@ Row #{}: No batch selected against item: {}. Please select a batch or remove it Payment amount cannot be less than or equal to 0,Der Zahlungsbetrag darf nicht kleiner oder gleich 0 sein, Please enter the phone number first,Bitte geben Sie zuerst die Telefonnummer ein, Row #{}: {} {} does not exist.,Zeile # {}: {} {} existiert nicht., -Row #{0}: {1} is required to create the Opening {2} Invoices,"Zeile # {0}: {1} ist erforderlich, um die Eröffnungsrechnungen {2} zu erstellen", +Row #{0}: {1} is required to create the Opening {2} Invoices,"Zeile {0}: {1} ist erforderlich, um die Eröffnungsrechnungen {2} zu erstellen", You had {} errors while creating opening invoices. Check {} for more details,Beim Erstellen von Eröffnungsrechnungen sind {} Fehler aufgetreten. Überprüfen Sie {} auf weitere Details, Error Occured,Fehler aufgetreten, Opening Invoice Creation In Progress,Öffnen der Rechnungserstellung läuft, @@ -9544,7 +9563,7 @@ Stock Transactions for Item {0} under warehouse {1} cannot be posted before this Posting future stock transactions are not allowed due to Immutable Ledger,Das Buchen zukünftiger Lagertransaktionen ist aufgrund des unveränderlichen Hauptbuchs nicht zulässig, A BOM with name {0} already exists for item {1}.,Für Artikel {1} ist bereits eine Stückliste mit dem Namen {0} vorhanden., {0}{1} Did you rename the item? Please contact Administrator / Tech support,{0} {1} Haben Sie den Artikel umbenannt? Bitte wenden Sie sich an den Administrator / technischen Support, -At row #{0}: the sequence id {1} cannot be less than previous row sequence id {2},In Zeile # {0}: Die Sequenz-ID {1} darf nicht kleiner sein als die vorherige Zeilen-Sequenz-ID {2}., +At row #{0}: the sequence id {1} cannot be less than previous row sequence id {2},In Zeile {0}: Die Sequenz-ID {1} darf nicht kleiner sein als die vorherige Zeilen-Sequenz-ID {2}., The {0} ({1}) must be equal to {2} ({3}),Die {0} ({1}) muss gleich {2} ({3}) sein., "{0}, complete the operation {1} before the operation {2}.","{0}, schließen Sie die Operation {1} vor der Operation {2} ab.", Cannot ensure delivery by Serial No as Item {0} is added with and without Ensure Delivery by Serial No.,"Die Lieferung per Seriennummer kann nicht sichergestellt werden, da Artikel {0} mit und ohne Lieferung per Seriennummer hinzugefügt wird.", diff --git a/erpnext/translations/vi.csv b/erpnext/translations/vi.csv index 23290a13a6..e3761373ef 100644 --- a/erpnext/translations/vi.csv +++ b/erpnext/translations/vi.csv @@ -100,7 +100,7 @@ Active,có hiệu lực, Activity Cost exists for Employee {0} against Activity Type - {1},Chi phí hoạt động tồn tại cho Nhân viên {0} đối với Kiểu công việc - {1}, Activity Cost per Employee,Chi phí hoạt động cho một nhân viên, Activity Type,Loại hoạt động, -Actual Cost,Gia thật, +Actual Cost,Giá thật, Actual Delivery Date,Ngày giao hàng thực tế, Actual Qty,Số lượng thực tế, Actual Qty is mandatory,Số lượng thực tế là bắt buộc, @@ -727,14 +727,14 @@ Current Qty,Số lượng hiện tại, Current invoice {0} is missing,Hóa đơn hiện tại {0} bị thiếu, Custom HTML,Tuỳ chỉnh HTML, Custom?,Tùy chỉnh?, -Customer,khách hàng, +Customer,Khách Hàng, Customer Addresses And Contacts,Địa chỉ Khách hàng Và Liên hệ, Customer Contact,Liên hệ Khách hàng, Customer Database.,Cơ sở dữ liệu khách hàng., Customer Group,Nhóm khách hàng, Customer LPO,Khách hàng LPO, Customer LPO No.,Số LPO của khách hàng, -Customer Name,tên khách hàng, +Customer Name,Tên khách hàng, Customer POS Id,POS ID Khách hàng, Customer Service,Dịch vụ chăm sóc khách hàng, Customer and Supplier,Khách hàng và nhà cung cấp, @@ -743,7 +743,7 @@ Customer isn't enrolled in any Loyalty Program,Khách hàng không được đă Customer required for 'Customerwise Discount',"Khách hàng phải có cho 'Giảm giá phù hợp KH """, Customer {0} does not belong to project {1},Khách hàng {0} không thuộc về dự án {1}, Customer {0} is created.,Đã tạo {0} khách hàng., -Customers in Queue,Khách hàng ở Queue, +Customers in Queue,Khách hàng trong hàng đợi, Customize Homepage Sections,Tùy chỉnh phần Trang chủ, Customizing Forms,Các hình thức tùy biến, Daily Project Summary for {0},Tóm tắt dự án hàng ngày cho {0}, @@ -3641,7 +3641,7 @@ Click on the link below to verify your email and confirm the appointment,Nhấp Close,Đóng, Communication,Liên lạc, Compact Item Print,Nhỏ gọn mục Print, -Company,Giỏ hàng Giá liệt kê, +Company,Công ty, Company of asset {0} and purchase document {1} doesn't matches.,Công ty tài sản {0} và tài liệu mua hàng {1} không khớp., Compare BOMs for changes in Raw Materials and Operations,So sánh các BOM cho những thay đổi trong Nguyên liệu thô và Hoạt động, Compare List function takes on list arguments,Chức năng So sánh Danh sách đảm nhận đối số danh sách, @@ -7589,7 +7589,7 @@ Contribution (%),Đóng góp (%), Contribution to Net Total,Đóng góp cho tổng số, Selling Settings,thiết lập thông số bán hàng, Settings for Selling Module,Thiết lập module bán hàng, -Customer Naming By,đặt tên khách hàng theo, +Customer Naming By,Đặt tên khách hàng theo, Campaign Naming By,Đặt tên chiến dịch theo, Default Customer Group,Nhóm khách hàng mặc định, Default Territory,Địa bàn mặc định, @@ -7617,7 +7617,7 @@ Average Discount,Giảm giá trung bình, Customerwise Discount,Giảm giá 1 cách thông minh, Itemwise Discount,Mẫu hàng thông minh giảm giá, Customer or Item,Khách hàng hoặc mục, -Customer / Item Name,Khách hàng / tên hàng hóa, +Customer / Item Name,Khách hàng / Tên hàng hóa, Authorized Value,Giá trị được ủy quyền, Applicable To (Role),Để áp dụng (Role), Applicable To (Employee),Để áp dụng (nhân viên), @@ -7686,8 +7686,8 @@ From Currency,Từ tệ, To Currency,Tới tiền tệ, For Buying,Để mua, For Selling,Để bán, -Customer Group Name,Tên Nhóm khách hàng, -Parent Customer Group,Nhóm mẹ của nhóm khách hàng, +Customer Group Name,Tên Nhóm Khách Hàng, +Parent Customer Group,Nhóm cha của nhóm khách hàng, Only leaf nodes are allowed in transaction,Chỉ các nút lá được cho phép trong giao dịch, Mention if non-standard receivable account applicable,Đề cập đến nếu tài khoản phải thu phi tiêu chuẩn áp dụng, Credit Limits,Hạn mức tín dụng, @@ -9065,7 +9065,7 @@ Time Required (In Mins),Thời gian cần thiết (Trong phút), From Posting Date,Từ ngày đăng, To Posting Date,Đến ngày đăng, No records found,Không có dữ liệu được tìm thấy, -Customer/Lead Name,Tên khách hàng / khách hàng tiềm năng, +Customer/Lead Name,Tên khách hàng / Khách hàng tiềm năng, Unmarked Days,Ngày không đánh dấu, Jan,tháng một, Feb,Tháng hai, diff --git a/pyproject.toml b/pyproject.toml index 012ffb17a6..3e0dfb29b4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -13,6 +13,7 @@ dependencies = [ "Unidecode~=1.3.6", "barcodenumber~=0.5.0", "rapidfuzz~=2.15.0", + "holidays~=0.28", # integration dependencies "gocardless-pro~=1.22.0",