Merge branch 'develop' into payments-based-dunning

This commit is contained in:
Marica 2023-07-17 12:33:19 +05:30 committed by GitHub
commit 1c1e7380e3
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
116 changed files with 2604 additions and 2907 deletions

View File

@ -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 }}

38
.github/workflows/release_notes.yml vendored Normal file
View File

@ -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 }}

View File

@ -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,6 +146,7 @@ def get_booking_dates(doc, item, posting_date=None):
"deferred_revenue_account" if doc.doctype == "Sales Invoice" else "deferred_expense_account"
)
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
@ -179,6 +180,8 @@ def get_booking_dates(doc, item, posting_date=None):
else:
start_date = item.service_start_date
else:
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(

View File

@ -2,10 +2,6 @@
"country_code": "nl",
"name": "Netherlands - Grootboekschema",
"tree": {
"FABRIKAGEREKENINGEN": {
"is_group": 1,
"root_type": "Expense"
},
"FINANCIELE REKENINGEN, KORTLOPENDE VORDERINGEN EN SCHULDEN": {
"Bank": {
"RABO Bank": {
@ -13,6 +9,144 @@
},
"account_type": "Bank"
},
"LIQUIDE MIDDELEN": {
"ABN-AMRO bank": {},
"Bankbetaalkaarten": {},
"Effecten": {},
"Girobetaalkaarten": {},
"Kas": {
"account_type": "Cash"
},
"Kas valuta": {
"account_type": "Cash"
},
"Kleine kas": {
"account_type": "Cash"
},
"Kruisposten": {},
"Postbank": {
"account_type": "Cash"
},
"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"
},
"Dubieuze debiteuren": {},
"Overige vorderingen": {},
"Rekening-courant directie 1": {},
"Te ontvangen ziekengeld": {},
"Voorschotten personeel": {},
"Vooruitbetaalde kosten": {},
"Voorziening dubieuze debiteuren": {}
},
"root_type": "Asset"
},
"KORTLOPENDE SCHULDEN": {
"Af te dragen Btw-verlegd": {
"account_type": "Tax"
@ -69,42 +203,13 @@
"Vakantiegeld 1": {},
"Vakantiezegels": {},
"Vennootschapsbelasting": {},
"Vooruit ontvangen bedr.": {}
},
"LIQUIDE MIDDELEN": {
"ABN-AMRO bank": {},
"Bankbetaalkaarten": {},
"Effecten": {},
"Girobetaalkaarten": {},
"Kas": {
"account_type": "Cash"
},
"Kas valuta": {
"account_type": "Cash"
},
"Kleine kas": {
"account_type": "Cash"
},
"Kruisposten": {},
"Postbank": {
"account_type": "Cash"
},
"account_type": "Cash"
},
"VORDERINGEN": {
"Debiteuren": {
"account_type": "Receivable"
},
"Dubieuze debiteuren": {},
"Overige vorderingen": {},
"Rekening-courant directie 1": {},
"Te ontvangen ziekengeld": {},
"Voorschotten personeel": {},
"Vooruitbetaalde kosten": {},
"Voorziening dubieuze debiteuren": {}
},
"root_type": "Asset"
"Vooruit ontvangen bedr.": {},
"is_group": 1,
"root_type": "Liability"
},
"FABRIKAGEREKENINGEN": {
"is_group": 1,
"root_type": "Expense",
"INDIRECTE KOSTEN": {
"is_group": 1,
"root_type": "Expense"
@ -291,91 +396,49 @@
"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"
},
"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"
"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"
}
}
}

View File

@ -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(
[

View File

@ -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) {

View File

@ -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');

View File

@ -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 - (

View File

@ -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 =

View File

@ -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):

View File

@ -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",

View File

@ -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(

View File

@ -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.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),
)
+ "<br><br>"
+ _("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,22 +1629,21 @@ 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
orders = []
if voucher_type:
if party_account_currency == company_currency:
grand_total_field = "base_grand_total"
rounded_total_field = "base_rounded_total"
@ -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)

View File

@ -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()

View File

@ -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",

View File

@ -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",

View File

@ -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) {

View File

@ -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()),
)

View File

@ -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);

View File

@ -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:

View File

@ -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);

View File

@ -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"),

View File

@ -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"),

View File

@ -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;

View File

@ -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)

View File

@ -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

View File

@ -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"),

View File

@ -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,14 +80,7 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
"posting_date": d.posting_date,
"supplier": d.supplier,
"supplier_name": d.supplier_name,
}
if additional_query_columns:
for col in additional_query_columns:
row.update({col: d.get(col)})
row.update(
{
**get_values_for_columns(additional_table_columns, d),
"credit_to": d.credit_to,
"mode_of_payment": d.mode_of_payment,
"project": d.project,
@ -99,7 +93,6 @@ def _execute(filters=None, additional_table_columns=None, additional_query_colum
"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:
@ -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,
)

View File

@ -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,14 +75,7 @@ def _execute(
"customer": d.customer,
"customer_name": customer_record.customer_name,
"customer_group": customer_record.customer_group,
}
if additional_query_columns:
for col in additional_query_columns:
row.update({col: d.get(col)})
row.update(
{
**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,
@ -101,7 +90,6 @@ def _execute(
"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})
@ -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,

View File

@ -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"),

View File

@ -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;

View File

@ -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,
)

View File

@ -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,14 +55,7 @@ def _execute(filters, additional_table_columns=None, additional_query_columns=No
"posting_date": inv.posting_date,
"customer": inv.customer,
"customer_name": inv.customer_name,
}
if additional_query_columns:
for col in additional_query_columns:
row.update({col: inv.get(col)})
row.update(
{
**get_values_for_columns(additional_table_columns, inv),
"customer_group": inv.get("customer_group"),
"territory": inv.get("territory"),
"tax_id": inv.get("tax_id"),
@ -76,7 +70,6 @@ def _execute(filters, additional_table_columns=None, additional_query_columns=No
"warehouse": ", ".join(warehouse),
"currency": company_currency,
}
)
# map income values
base_net_total = 0
@ -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,
)

View File

@ -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
),

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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

View File

@ -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:

View File

@ -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\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"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\":\"<span class=\\\"h4\\\"><b>Reports &amp; Masters</b></span>\",\"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\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"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\":\"<span class=\\\"h4\\\"><b>Reports &amp; Masters</b></span>\",\"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",

View File

@ -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):
)
+ "."
+ "<br><br>"
+ _(
"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
)
+ "."
+ "<br><br>"
+ _("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()

View File

@ -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 }
};
}

View File

@ -62,27 +62,18 @@ 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:
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)
)
if d.to_employee and d.target_location:
elif 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"
"Asset {0} cannot be received at a location and given to an employee in a single movement"
).format(d.asset)
)

View File

@ -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'",
},
{

View File

@ -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 {

View File

@ -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\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"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\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"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\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"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\":\"<span class=\\\"h4\\\"><b>Reports &amp; Masters</b></span>\",\"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",

View File

@ -61,7 +61,7 @@
"fieldname": "communication_channel",
"fieldtype": "Select",
"label": "Communication Channel",
"options": "\nExotel"
"options": ""
}
],
"links": [],

View File

@ -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"):

View File

@ -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

View File

@ -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(

View File

@ -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"));

View File

@ -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({

View File

@ -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
}

View File

@ -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"))

View File

@ -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
)

View File

@ -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",

View File

@ -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",
]

View File

@ -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

View File

@ -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

View File

@ -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;

View File

@ -1,6 +1,6 @@
{
"charts": [],
"content": "[{\"id\":\"csBCiDglCE\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"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\":\"<span class=\\\"h4\\\"><b>Reports &amp; Masters</b></span>\",\"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\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"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\":\"<span class=\\\"h4\\\"><b>Reports &amp; Masters</b></span>\",\"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",

View File

@ -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

View File

@ -13,12 +13,13 @@ from erpnext.accounts.utils import get_fiscal_year
def execute():
frappe.db.truncate("Account Closing Balance")
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},
filters={"docstatus": 1, "company": company},
order_by="posting_date",
):
@ -38,6 +39,9 @@ def execute():
entry["closing_date"] = pcv_doc.posting_date
entry["period_closing_voucher"] = pcv_doc.name
closing_entries = []
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",
@ -46,6 +50,7 @@ def execute():
"voucher_no": ["!=", pcv.name],
"posting_date": ["between", [pcv_doc.year_start_date, pcv.posting_date]],
"is_opening": "No",
"company": company,
},
fields=["*"],
)
@ -54,7 +59,7 @@ def execute():
# add opening entries only for the first pcv
closing_entries += frappe.db.get_all(
"GL Entry",
filters={"is_cancelled": 0, "is_opening": "Yes"},
filters={"is_cancelled": 0, "is_opening": "Yes", "company": company},
fields=["*"],
)
@ -62,7 +67,9 @@ def execute():
entry["closing_date"] = pcv_doc.posting_date
entry["period_closing_voucher"] = pcv_doc.name
make_closing_entries(gl_entries + closing_entries, voucher_name=pcv.name)
company_wise_order[pcv.company].append(pcv.posting_date)
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)

View File

@ -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))

View File

@ -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\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Task\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Project\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Timesheet\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Project Billing Summary\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports &amp; Masters</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Projects\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Time Tracking\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}}]",
"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\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"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\":\"<span class=\\\"h4\\\"><b>Reports &amp; Masters</b></span>\",\"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",

View File

@ -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() {

View File

@ -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")
});
},
}
]

View File

@ -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,13 +888,7 @@ $(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(r.message, function(_i, d) {
$.each(frappe.boot.service_level_agreement_doctypes, function(_i, d) {
frappe.ui.form.on(d, {
onload: function(frm) {
if (!frm.doc.service_level_agreement)
@ -942,8 +970,6 @@ $(document).on('app_ready', function() {
},
});
});
}
});
});
function set_time_to_resolve_and_response(frm, apply_sla_for_resolution) {

View File

@ -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
}
],

View File

@ -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,
},

View File

@ -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);

View File

@ -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\":\"<span class=\\\"h4\\\"><b>Quick Access</b></span>\",\"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\":\"<span class=\\\"h4\\\"><b>Reports &amp; Masters</b></span>\",\"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\":\"<span class=\\\"h4\\\"><b>Quick Access</b></span>\",\"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\":\"<span class=\\\"h4\\\"><b>Reports &amp; Masters</b></span>\",\"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",

View File

@ -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});

View File

@ -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",

View File

@ -1,352 +1,99 @@
{
"actions": [],
"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,
"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
"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
},
{
"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
"options": "Country"
},
{
"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
"options": "UOM"
},
{
"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
"fieldtype": "Column Break"
},
{
"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
"reqd": 1
},
{
"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
"options": "\nNo\nYes"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "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
"label": "Disable Rounded Total"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "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
"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",
"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,
"write": 1
}
],
"quick_entry": 0,
"read_only": 1,
"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": []
}

View File

@ -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)

View File

@ -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"] = [

View File

@ -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": []
}

View File

@ -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)

View File

@ -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

View File

@ -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);

View File

@ -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",

View File

@ -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"),

View File

@ -47,8 +47,6 @@ frappe.ui.form.on('Batch', {
return;
}
debugger
const section = frm.dashboard.add_section('', __("Stock Levels"));
// sort by qty

View File

@ -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)"""

View File

@ -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")

View File

@ -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",

View File

@ -773,7 +773,7 @@ class Item(Document):
rows = ""
for docname, attr_list in not_included.items():
link = "<a href='/app/Form/Item/{0}'>{0}</a>".format(frappe.bold(_(docname)))
link = f"<a href='/app/item/{docname}'>{frappe.bold(docname)}</a>"
rows += table_row(link, body(attr_list))
error_description = _(

View File

@ -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,
"actions": [],
"creation": "2015-05-19 05:12:30.344797",
"custom": 0,
"docstatus": 0,
"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
"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
"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
"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
"label": "Attribute Value"
},
{
"allow_bulk_edit": 0,
"allow_in_quick_entry": 0,
"allow_on_submit": 0,
"bold": 0,
"collapsible": 0,
"columns": 0,
"default": "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
"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
"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
"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
"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
"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
"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",
"links": [],
"modified": "2023-07-14 17:15:19.112119",
"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
"states": []
}

View File

@ -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);

View File

@ -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",

View File

@ -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

View File

@ -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",

View File

@ -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",

View File

@ -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

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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",
};
},
});

View File

@ -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}
}
}
}
]
}

View File

@ -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")
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"]
)
def get_data(filters):
data = []
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,
]
)
return data
def get_batch_details(filters):
batch = frappe.qb.DocType("Batch")
query = (
frappe.qb.from_(sle)
.select(sle.item_code, sle.batch_no, sle.warehouse, sle.posting_date, sle.actual_qty)
frappe.qb.from_(batch)
.select(
batch.name,
batch.creation,
batch.expiry_date,
batch.item,
batch.item_name,
batch.stock_uom,
batch.batch_qty,
)
.where(
(sle.is_cancelled == 0)
& (sle.docstatus < 2)
& (IfNull(sle.batch_no, "") != "")
& (sle.posting_date <= filters["to_date"])
(batch.disabled == 0)
& (batch.batch_qty > 0)
& (
(Date(batch.creation) >= filters["from_date"]) & (Date(batch.creation) <= filters["to_date"])
)
.orderby(sle.item_code, sle.warehouse)
)
.orderby(batch.creation)
)
if filters.get("item"):
query = query.where(batch.item == filters["item"])
return query.run(as_dict=True)
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})
)
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
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)
return item_map

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