Merge pull request #39054 from ruthra-kumar/provision_to_set_dimension_in_reconciliation_tool
refactor: provision to filter on dimensions in reconciliation tool
diff --git a/.github/workflows/linters.yml b/.github/workflows/linters.yml
index 94b76b1..c7caa4c 100644
--- a/.github/workflows/linters.yml
+++ b/.github/workflows/linters.yml
@@ -20,6 +20,18 @@
- name: Install and Run Pre-commit
uses: pre-commit/action@v3.0.0
+ semgrep:
+ name: semgrep
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Set up Python 3.10
+ uses: actions/setup-python@v4
+ with:
+ python-version: '3.10'
+ cache: pip
+
- name: Download Semgrep rules
run: git clone --depth 1 https://github.com/frappe/semgrep-rules.git frappe-semgrep-rules
diff --git a/erpnext/accounts/deferred_revenue.py b/erpnext/accounts/deferred_revenue.py
index 6282e9a..367b017 100644
--- a/erpnext/accounts/deferred_revenue.py
+++ b/erpnext/accounts/deferred_revenue.py
@@ -358,11 +358,9 @@
account_currency = get_account_currency(item.expense_account or item.income_account)
if doc.doctype == "Sales Invoice":
- against_type = "Customer"
against, project = doc.customer, doc.project
credit_account, debit_account = item.income_account, item.deferred_revenue_account
else:
- against_type = "Supplier"
against, project = doc.supplier, item.project
credit_account, debit_account = item.deferred_expense_account, item.expense_account
@@ -415,7 +413,6 @@
doc,
credit_account,
debit_account,
- against_type,
against,
amount,
base_amount,
@@ -497,7 +494,6 @@
doc,
credit_account,
debit_account,
- against_type,
against,
amount,
base_amount,
@@ -519,9 +515,7 @@
doc.get_gl_dict(
{
"account": credit_account,
- "against_type": against_type,
"against": against,
- "against_link": against,
"credit": base_amount,
"credit_in_account_currency": amount,
"cost_center": cost_center,
@@ -540,9 +534,7 @@
doc.get_gl_dict(
{
"account": debit_account,
- "against_type": against_type,
"against": against,
- "against_link": against,
"debit": base_amount,
"debit_in_account_currency": amount,
"cost_center": cost_center,
diff --git a/erpnext/accounts/doctype/fiscal_year/fiscal_year.json b/erpnext/accounts/doctype/fiscal_year/fiscal_year.json
index 5ab91f2..bd2bfbd 100644
--- a/erpnext/accounts/doctype/fiscal_year/fiscal_year.json
+++ b/erpnext/accounts/doctype/fiscal_year/fiscal_year.json
@@ -82,11 +82,11 @@
"icon": "fa fa-calendar",
"idx": 1,
"links": [],
- "modified": "2020-11-05 12:16:53.081573",
+ "modified": "2024-01-17 13:06:01.608953",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Fiscal Year",
- "owner": "Administrator",
+ "owner": "Administrator",
"permissions": [
{
"create": 1,
@@ -118,6 +118,14 @@
{
"read": 1,
"role": "Employee"
+ },
+ {
+ "read": 1,
+ "role": "Accounts Manager"
+ },
+ {
+ "read": 1,
+ "role": "Stock Manager"
}
],
"show_name_in_global_search": 1,
diff --git a/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py b/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py
index 181406b..8d43716 100644
--- a/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py
+++ b/erpnext/accounts/doctype/fiscal_year/test_fiscal_year.py
@@ -39,7 +39,7 @@
]
start = 2012
- end = now_datetime().year + 5
+ end = now_datetime().year + 25
for year in range(start, end):
test_records.append(
{
diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.json b/erpnext/accounts/doctype/gl_entry/gl_entry.json
index 09912e9..c071193 100644
--- a/erpnext/accounts/doctype/gl_entry/gl_entry.json
+++ b/erpnext/accounts/doctype/gl_entry/gl_entry.json
@@ -17,9 +17,7 @@
"account_currency",
"debit_in_account_currency",
"credit_in_account_currency",
- "against_type",
"against",
- "against_link",
"against_voucher_type",
"against_voucher",
"voucher_type",
@@ -132,13 +130,6 @@
"options": "account_currency"
},
{
- "fieldname": "against_type",
- "fieldtype": "Link",
- "in_filter": 1,
- "label": "Against Type",
- "options": "DocType"
- },
- {
"fieldname": "against",
"fieldtype": "Text",
"in_filter": 1,
@@ -147,13 +138,6 @@
"oldfieldtype": "Text"
},
{
- "fieldname": "against_link",
- "fieldtype": "Dynamic Link",
- "in_filter": 1,
- "label": "Against",
- "options": "against_type"
- },
- {
"fieldname": "against_voucher_type",
"fieldtype": "Link",
"label": "Against Voucher Type",
@@ -306,7 +290,7 @@
"idx": 1,
"in_create": 1,
"links": [],
- "modified": "2023-12-18 15:38:14.006208",
+ "modified": "2023-09-26 12:03:23.031733",
"modified_by": "Administrator",
"module": "Accounts",
"name": "GL Entry",
diff --git a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py
index 69b0860..76f4dad 100644
--- a/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py
+++ b/erpnext/accounts/doctype/invoice_discounting/invoice_discounting.py
@@ -153,9 +153,7 @@
"account": inv.debit_to,
"party_type": "Customer",
"party": d.customer,
- "against_type": "Account",
"against": self.accounts_receivable_credit,
- "against_link": self.accounts_receivable_credit,
"credit": outstanding_in_company_currency,
"credit_in_account_currency": outstanding_in_company_currency
if inv.party_account_currency == company_currency
@@ -175,9 +173,7 @@
"account": self.accounts_receivable_credit,
"party_type": "Customer",
"party": d.customer,
- "against_type": "Account",
"against": inv.debit_to,
- "against_link": inv.debit_to,
"debit": outstanding_in_company_currency,
"debit_in_account_currency": outstanding_in_company_currency
if ar_credit_account_currency == company_currency
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js
index abf8781..07fb5e8 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.js
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js
@@ -220,16 +220,6 @@
return erpnext.journal_entry.account_query(me.frm);
});
- me.frm.set_query("against_account_link", "accounts", function(doc, cdt, cdn) {
- return erpnext.journal_entry.against_account_query(me.frm);
- });
-
- me.frm.set_query("against_type", "accounts", function(){
- return {
- query: "erpnext.accounts.doctype.journal_entry.journal_entry.get_against_type",
- }
- })
-
me.frm.set_query("party_type", "accounts", function(doc, cdt, cdn) {
const row = locals[cdt][cdn];
@@ -601,21 +591,6 @@
return { filters: filters };
},
- against_account_query: function(frm) {
- if (frm.doc.against_type != "Account"){
- return { filters: {} };
- }
- else {
- let filters = { company: frm.doc.company, is_group: 0 };
- if(!frm.doc.multi_currency) {
- $.extend(filters, {
- account_currency: ['in', [frappe.get_doc(":Company", frm.doc.company).default_currency, null]]
- });
- }
- return { filters: filters };
- }
- },
-
reverse_journal_entry: function() {
frappe.model.open_mapped_doc({
method: "erpnext.accounts.doctype.journal_entry.journal_entry.make_reverse_journal_entry",
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py
index 79f1ab0..7579da8 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py
@@ -186,9 +186,12 @@
def update_advance_paid(self):
advance_paid = frappe._dict()
+ advance_payment_doctypes = frappe.get_hooks(
+ "advance_payment_customer_doctypes"
+ ) + frappe.get_hooks("advance_payment_supplier_doctypes")
for d in self.get("accounts"):
if d.is_advance:
- if d.reference_type in frappe.get_hooks("advance_payment_doctypes"):
+ if d.reference_type in advance_payment_doctypes:
advance_paid.setdefault(d.reference_type, []).append(d.reference_name)
for voucher_type, order_list in advance_paid.items():
@@ -304,7 +307,6 @@
"account": tax_withholding_details.get("account_head"),
rev_debit_or_credit: tax_withholding_details.get("tax_amount"),
"against_account": parties[0],
- "against_account_link": parties[0],
},
)
@@ -751,90 +753,27 @@
)
def set_against_account(self):
+ accounts_debited, accounts_credited = [], []
if self.voucher_type in ("Deferred Revenue", "Deferred Expense"):
for d in self.get("accounts"):
if d.reference_type == "Sales Invoice":
- against_type = "Customer"
+ field = "customer"
else:
- against_type = "Supplier"
+ field = "supplier"
- against_account = frappe.db.get_value(d.reference_type, d.reference_name, against_type.lower())
- d.against_type = against_type
- d.against_account_link = against_account
+ d.against_account = frappe.db.get_value(d.reference_type, d.reference_name, field)
else:
- self.get_debited_credited_accounts()
- if len(self.accounts_credited) > 1 and len(self.accounts_debited) > 1:
- self.auto_set_against_accounts()
- return
- self.get_against_accounts()
+ for d in self.get("accounts"):
+ if flt(d.debit) > 0:
+ accounts_debited.append(d.party or d.account)
+ if flt(d.credit) > 0:
+ accounts_credited.append(d.party or d.account)
- def auto_set_against_accounts(self):
- for i in range(0, len(self.accounts), 2):
- acc = self.accounts[i]
- against_acc = self.accounts[i + 1]
- if acc.debit_in_account_currency > 0:
- current_val = acc.debit_in_account_currency * flt(acc.exchange_rate)
- against_val = against_acc.credit_in_account_currency * flt(against_acc.exchange_rate)
- else:
- current_val = acc.credit_in_account_currency * flt(acc.exchange_rate)
- against_val = against_acc.debit_in_account_currency * flt(against_acc.exchange_rate)
-
- if current_val == against_val:
- acc.against_type = against_acc.party_type or "Account"
- against_acc.against_type = acc.party_type or "Account"
-
- acc.against_account_link = against_acc.party or against_acc.account
- against_acc.against_account_link = acc.party or acc.account
- else:
- frappe.msgprint(
- _(
- "Unable to automatically determine {0} accounts. Set them up in the {1} table if needed."
- ).format(frappe.bold("against"), frappe.bold("Accounting Entries")),
- alert=True,
- )
- break
-
- def get_against_accounts(self):
- self.against_accounts = []
- self.split_account = {}
- self.get_debited_credited_accounts()
-
- if self.separate_against_account_entries:
- no_of_credited_acc, no_of_debited_acc = len(self.accounts_credited), len(self.accounts_debited)
- if no_of_credited_acc <= 1 and no_of_debited_acc <= 1:
- self.set_against_accounts_for_single_dr_cr()
- self.separate_against_account_entries = 0
- elif no_of_credited_acc == 1:
- self.against_accounts = self.accounts_debited
- self.split_account = self.accounts_credited[0]
- elif no_of_debited_acc == 1:
- self.against_accounts = self.accounts_credited
- self.split_account = self.accounts_debited[0]
-
- def get_debited_credited_accounts(self):
- self.accounts_debited, self.accounts_credited = [], []
- self.separate_against_account_entries = 1
- for d in self.get("accounts"):
- if flt(d.debit) > 0:
- self.accounts_debited.append(d)
- elif flt(d.credit) > 0:
- self.accounts_credited.append(d)
-
- if d.against_account_link:
- self.separate_against_account_entries = 0
- break
-
- def set_against_accounts_for_single_dr_cr(self):
- against_account = None
- for d in self.accounts:
- if flt(d.debit) > 0:
- against_account = self.accounts_credited[0]
- elif flt(d.credit) > 0:
- against_account = self.accounts_debited[0]
- if against_account:
- d.against_type = against_account.party_type or "Account"
- d.against_account = against_account.party or against_account.account
- d.against_account_link = against_account.party or against_account.account
+ for d in self.get("accounts"):
+ if flt(d.debit) > 0:
+ d.against_account = ", ".join(list(set(accounts_credited)))
+ if flt(d.credit) > 0:
+ d.against_account = ", ".join(list(set(accounts_debited)))
def validate_debit_credit_amount(self):
if not (self.voucher_type == "Exchange Gain Or Loss" and self.multi_currency):
@@ -1031,82 +970,40 @@
def build_gl_map(self):
gl_map = []
- self.get_against_accounts()
for d in self.get("accounts"):
if d.debit or d.credit or (self.voucher_type == "Exchange Gain Or Loss"):
r = [d.user_remark, self.remark]
r = [x for x in r if x]
remarks = "\n".join(r)
- gl_dict = self.get_gl_dict(
- {
- "account": d.account,
- "party_type": d.party_type,
- "due_date": self.due_date,
- "party": d.party,
- "debit": flt(d.debit, d.precision("debit")),
- "credit": flt(d.credit, d.precision("credit")),
- "account_currency": d.account_currency,
- "debit_in_account_currency": flt(
- d.debit_in_account_currency, d.precision("debit_in_account_currency")
- ),
- "credit_in_account_currency": flt(
- d.credit_in_account_currency, d.precision("credit_in_account_currency")
- ),
- "against_voucher_type": d.reference_type,
- "against_voucher": d.reference_name,
- "remarks": remarks,
- "voucher_detail_no": d.reference_detail_no,
- "cost_center": d.cost_center,
- "project": d.project,
- "finance_book": self.finance_book,
- },
- item=d,
+ gl_map.append(
+ self.get_gl_dict(
+ {
+ "account": d.account,
+ "party_type": d.party_type,
+ "due_date": self.due_date,
+ "party": d.party,
+ "against": d.against_account,
+ "debit": flt(d.debit, d.precision("debit")),
+ "credit": flt(d.credit, d.precision("credit")),
+ "account_currency": d.account_currency,
+ "debit_in_account_currency": flt(
+ d.debit_in_account_currency, d.precision("debit_in_account_currency")
+ ),
+ "credit_in_account_currency": flt(
+ d.credit_in_account_currency, d.precision("credit_in_account_currency")
+ ),
+ "against_voucher_type": d.reference_type,
+ "against_voucher": d.reference_name,
+ "remarks": remarks,
+ "voucher_detail_no": d.reference_detail_no,
+ "cost_center": d.cost_center,
+ "project": d.project,
+ "finance_book": self.finance_book,
+ },
+ item=d,
+ )
)
-
- if not self.separate_against_account_entries:
- gl_dict.update(
- {
- "against_type": d.against_type,
- "against_link": d.against_account_link,
- }
- )
- gl_map.append(gl_dict)
-
- elif d in self.against_accounts:
- gl_dict.update(
- {
- "against_type": self.split_account.get("party_type") or "Account",
- "against": self.split_account.get("party") or self.split_account.get("account"),
- "against_link": self.split_account.get("party") or self.split_account.get("account"),
- }
- )
- gl_map.append(gl_dict)
-
- else:
- for against_account in self.against_accounts:
- against_account = against_account.as_dict()
- debit = against_account.credit or against_account.credit_in_account_currency
- credit = against_account.debit or against_account.debit_in_account_currency
- gl_dict = gl_dict.copy()
- gl_dict.update(
- {
- "against_type": against_account.party_type or "Account",
- "against": against_account.party or against_account.account,
- "against_link": against_account.party or against_account.account,
- "debit": flt(debit, d.precision("debit")),
- "credit": flt(credit, d.precision("credit")),
- "account_currency": d.account_currency,
- "debit_in_account_currency": flt(
- debit / d.exchange_rate, d.precision("debit_in_account_currency")
- ),
- "credit_in_account_currency": flt(
- credit / d.exchange_rate, d.precision("credit_in_account_currency")
- ),
- }
- )
- gl_map.append(gl_dict)
-
return gl_map
def make_gl_entries(self, cancel=0, adv_adj=0):
@@ -1731,10 +1628,3 @@
)
return doclist
-
-
-@frappe.whitelist()
-def get_against_type(doctype, txt, searchfield, start, page_len, filters):
- against_types = frappe.db.get_list("Party Type", pluck="name") + ["Account"]
- doctype = frappe.qb.DocType("DocType")
- return frappe.qb.from_(doctype).select(doctype.name).where(doctype.name.isin(against_types)).run()
diff --git a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
index 01006bd..99e66e6 100644
--- a/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
+++ b/erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
@@ -37,9 +37,7 @@
"col_break3",
"is_advance",
"user_remark",
- "against_type",
- "against_account",
- "against_account_link"
+ "against_account"
],
"fields": [
{
@@ -252,21 +250,14 @@
"print_hide": 1
},
{
- "fieldname": "against_account",
- "fieldtype": "Text",
- "hidden": 1,
- "label": "Against Account",
- "no_copy": 1,
- "oldfieldname": "against_account",
- "oldfieldtype": "Text",
- "print_hide": 1
- },
- {
- "fieldname": "against_account_link",
- "fieldtype": "Dynamic Link",
+ "fieldname": "against_account",
+ "fieldtype": "Text",
+ "hidden": 1,
"label": "Against Account",
"no_copy": 1,
- "options": "against_type"
+ "oldfieldname": "against_account",
+ "oldfieldtype": "Text",
+ "print_hide": 1
},
{
"collapsible": 1,
@@ -290,18 +281,12 @@
"hidden": 1,
"label": "Reference Detail No",
"no_copy": 1
- },
- {
- "fieldname": "against_type",
- "fieldtype": "Link",
- "label": "Against Type",
- "options": "DocType"
}
],
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2023-12-02 23:21:22.205409",
+ "modified": "2023-12-03 23:21:22.205409",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry Account",
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js
index f5a07bf..62e2181 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.js
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js
@@ -944,7 +944,7 @@
if(frm.doc.payment_type == "Receive"
&& frm.doc.base_total_allocated_amount < frm.doc.base_received_amount + total_deductions
&& frm.doc.total_allocated_amount < frm.doc.paid_amount + (total_deductions / frm.doc.source_exchange_rate)) {
- unallocated_amount = (frm.doc.base_received_amount + total_deductions + flt(frm.doc.base_total_taxes_and_charges)
+ unallocated_amount = (frm.doc.base_received_amount + total_deductions - flt(frm.doc.base_total_taxes_and_charges)
- frm.doc.base_total_allocated_amount) / frm.doc.source_exchange_rate;
} else if (frm.doc.payment_type == "Pay"
&& frm.doc.base_total_allocated_amount < frm.doc.base_paid_amount - total_deductions
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index 76f9c46..69f0dda 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -926,7 +926,10 @@
def calculate_base_allocated_amount_for_reference(self, d) -> float:
base_allocated_amount = 0
- if d.reference_doctype in frappe.get_hooks("advance_payment_doctypes"):
+ advance_payment_doctypes = frappe.get_hooks(
+ "advance_payment_customer_doctypes"
+ ) + frappe.get_hooks("advance_payment_supplier_doctypes")
+ if d.reference_doctype in advance_payment_doctypes:
# When referencing Sales/Purchase Order, use the source/target exchange rate depending on payment type.
# This is so there are no Exchange Gain/Loss generated for such doctypes
@@ -1145,9 +1148,7 @@
"account": self.party_account,
"party_type": self.party_type,
"party": self.party,
- "against_type": "Account",
"against": against_account,
- "against_link": against_account,
"account_currency": self.party_account_currency,
"cost_center": self.cost_center,
},
@@ -1312,9 +1313,7 @@
{
"account": self.paid_from,
"account_currency": self.paid_from_account_currency,
- "against_type": self.party_type if self.payment_type == "Pay" else "Account",
"against": self.party if self.payment_type == "Pay" else self.paid_to,
- "against_link": self.party if self.payment_type == "Pay" else self.paid_to,
"credit_in_account_currency": self.paid_amount,
"credit": self.base_paid_amount,
"cost_center": self.cost_center,
@@ -1329,9 +1328,7 @@
{
"account": self.paid_to,
"account_currency": self.paid_to_account_currency,
- "against_type": self.party_type if self.payment_type == "Receive" else "Account",
"against": self.party if self.payment_type == "Receive" else self.paid_from,
- "against_link": self.party if self.payment_type == "Receive" else self.paid_from,
"debit_in_account_currency": self.received_amount,
"debit": self.base_received_amount,
"cost_center": self.cost_center,
@@ -1355,7 +1352,6 @@
rev_dr_or_cr = "credit" if dr_or_cr == "debit" else "debit"
against = self.party or self.paid_to
- against_type = self.party_type or "Account"
payment_account = self.get_party_account_for_taxes()
tax_amount = d.tax_amount
base_tax_amount = d.base_tax_amount
@@ -1364,9 +1360,7 @@
self.get_gl_dict(
{
"account": d.account_head,
- "against_type": against_type,
"against": against,
- "against_link": against,
dr_or_cr: tax_amount,
dr_or_cr + "_in_account_currency": base_tax_amount
if account_currency == self.company_currency
@@ -1391,9 +1385,7 @@
self.get_gl_dict(
{
"account": payment_account,
- "against_type": against_type,
"against": against,
- "against_link": against,
rev_dr_or_cr: tax_amount,
rev_dr_or_cr + "_in_account_currency": base_tax_amount
if account_currency == self.company_currency
@@ -1418,9 +1410,7 @@
{
"account": d.account,
"account_currency": account_currency,
- "against_type": self.party_type or "Account",
"against": self.party or self.paid_from,
- "against_link": self.party or self.paid_from,
"debit_in_account_currency": d.amount,
"debit": d.amount,
"cost_center": d.cost_center,
@@ -1437,8 +1427,11 @@
def update_advance_paid(self):
if self.payment_type in ("Receive", "Pay") and self.party:
+ advance_payment_doctypes = frappe.get_hooks(
+ "advance_payment_customer_doctypes"
+ ) + frappe.get_hooks("advance_payment_supplier_doctypes")
for d in self.get("references"):
- if d.allocated_amount and d.reference_doctype in frappe.get_hooks("advance_payment_doctypes"):
+ if d.allocated_amount and d.reference_doctype in advance_payment_doctypes:
frappe.get_doc(
d.reference_doctype, d.reference_name, for_update=True
).set_total_advance_paid()
diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py
index 9772b94..839348a 100644
--- a/erpnext/accounts/doctype/payment_request/payment_request.py
+++ b/erpnext/accounts/doctype/payment_request/payment_request.py
@@ -169,6 +169,13 @@
elif self.payment_channel == "Phone":
self.request_phone_payment()
+ advance_payment_doctypes = frappe.get_hooks(
+ "advance_payment_customer_doctypes"
+ ) + frappe.get_hooks("advance_payment_supplier_doctypes")
+ if self.reference_doctype in advance_payment_doctypes:
+ # set advance payment status
+ ref_doc.set_total_advance_paid()
+
def request_phone_payment(self):
controller = _get_payment_gateway_controller(self.payment_gateway)
request_amount = self.get_request_amount()
@@ -207,6 +214,14 @@
self.check_if_payment_entry_exists()
self.set_as_cancelled()
+ ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
+ advance_payment_doctypes = frappe.get_hooks(
+ "advance_payment_customer_doctypes"
+ ) + frappe.get_hooks("advance_payment_supplier_doctypes")
+ if self.reference_doctype in advance_payment_doctypes:
+ # set advance payment status
+ ref_doc.set_total_advance_paid()
+
def make_invoice(self):
ref_doc = frappe.get_doc(self.reference_doctype, self.reference_name)
if hasattr(ref_doc, "order_type") and getattr(ref_doc, "order_type") == "Shopping Cart":
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index b8ecf98..c4e09b4 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -827,9 +827,7 @@
"party_type": "Supplier",
"party": self.supplier,
"due_date": self.due_date,
- "against_type": "Account",
"against": self.against_expense_account,
- "against_link": self.against_expense_account,
"credit": base_grand_total,
"credit_in_account_currency": base_grand_total
if self.party_account_currency == self.company_currency
@@ -902,9 +900,7 @@
self.get_gl_dict(
{
"account": warehouse_account[item.warehouse]["account"],
- "against_type": "Account",
"against": warehouse_account[item.from_warehouse]["account"],
- "against_link": warehouse_account[item.from_warehouse]["account"],
"cost_center": item.cost_center,
"project": item.project or self.project,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
@@ -924,9 +920,7 @@
self.get_gl_dict(
{
"account": warehouse_account[item.from_warehouse]["account"],
- "against_type": "Account",
"against": warehouse_account[item.warehouse]["account"],
- "against_link": warehouse_account[item.warehouse]["account"],
"cost_center": item.cost_center,
"project": item.project or self.project,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
@@ -943,9 +937,7 @@
self.get_gl_dict(
{
"account": item.expense_account,
- "against_type": "Supplier",
"against": self.supplier,
- "against_link": self.supplier,
"debit": flt(item.base_net_amount, item.precision("base_net_amount")),
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"cost_center": item.cost_center,
@@ -962,9 +954,7 @@
self.get_gl_dict(
{
"account": item.expense_account,
- "against_type": "Supplier",
"against": self.supplier,
- "against_link": self.supplier,
"debit": warehouse_debit_amount,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"cost_center": item.cost_center,
@@ -983,9 +973,7 @@
self.get_gl_dict(
{
"account": account,
- "against_type": "Account",
"against": item.expense_account,
- "against_link": item.expense_account,
"cost_center": item.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"credit": flt(amount["base_amount"]),
@@ -1005,9 +993,7 @@
self.get_gl_dict(
{
"account": supplier_warehouse_account,
- "against_type": "Account",
"against": item.expense_account,
- "against_link": item.expense_account,
"cost_center": item.cost_center,
"project": item.project or self.project,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
@@ -1062,9 +1048,7 @@
self.get_gl_dict(
{
"account": expense_account,
- "against_type": "Supplier",
"against": self.supplier,
- "against_link": self.supplier,
"debit": amount,
"cost_center": item.cost_center,
"project": item.project or self.project,
@@ -1090,9 +1074,7 @@
self.get_gl_dict(
{
"account": expense_account,
- "against_type": "Supplier",
"against": self.supplier,
- "against_link": self.supplier,
"debit": discrepancy_caused_by_exchange_rate_difference,
"cost_center": item.cost_center,
"project": item.project or self.project,
@@ -1105,9 +1087,7 @@
self.get_gl_dict(
{
"account": self.get_company_default("exchange_gain_loss_account"),
- "against_type": "Supplier",
"against": self.supplier,
- "against_link": self.supplier,
"credit": discrepancy_caused_by_exchange_rate_difference,
"cost_center": item.cost_center,
"project": item.project or self.project,
@@ -1140,10 +1120,8 @@
gl_entries.append(
self.get_gl_dict(
{
- "account": stock_rbnb,
- "against_type": "Supplier",
+ "account": self.stock_received_but_not_billed,
"against": self.supplier,
- "against_link": self.supplier,
"debit": flt(item.item_tax_amount, item.precision("item_tax_amount")),
"remarks": self.remarks or _("Accounting Entry for Stock"),
"cost_center": self.cost_center,
@@ -1204,9 +1182,7 @@
self.get_gl_dict(
{
"account": cost_of_goods_sold_account,
- "against_type": "Account",
"against": item.expense_account,
- "against_link": item.expense_account,
"debit": stock_adjustment_amt,
"remarks": self.get("remarks") or _("Stock Adjustment"),
"cost_center": item.cost_center,
@@ -1236,9 +1212,7 @@
self.get_gl_dict(
{
"account": tax.account_head,
- "against_type": "Supplier",
"against": self.supplier,
- "against_link": self.supplier,
dr_or_cr: base_amount,
dr_or_cr + "_in_account_currency": base_amount
if account_currency == self.company_currency
@@ -1286,10 +1260,8 @@
self.get_gl_dict(
{
"account": tax.account_head,
- "against_type": "Supplier",
"cost_center": tax.cost_center,
"against": self.supplier,
- "against_link": self.supplier,
"credit": applicable_amount,
"remarks": self.remarks or _("Accounting Entry for Stock"),
},
@@ -1307,9 +1279,7 @@
{
"account": tax.account_head,
"cost_center": tax.cost_center,
- "against_type": "Supplier",
"against": self.supplier,
- "against_link": self.supplier,
"credit": valuation_tax[tax.name],
"remarks": self.remarks or _("Accounting Entry for Stock"),
},
@@ -1324,9 +1294,7 @@
self.get_gl_dict(
{
"account": self.unrealized_profit_loss_account,
- "against_type": "Supplier",
"against": self.supplier,
- "against_link": self.supplier,
"credit": flt(self.total_taxes_and_charges),
"credit_in_account_currency": flt(self.base_total_taxes_and_charges),
"cost_center": self.cost_center,
@@ -1347,9 +1315,7 @@
"account": self.credit_to,
"party_type": "Supplier",
"party": self.supplier,
- "against_type": "Account",
"against": self.cash_bank_account,
- "against_link": self.cash_bank_account,
"debit": self.base_paid_amount,
"debit_in_account_currency": self.base_paid_amount
if self.party_account_currency == self.company_currency
@@ -1370,9 +1336,7 @@
self.get_gl_dict(
{
"account": self.cash_bank_account,
- "against_type": "Supplier",
"against": self.supplier,
- "against_link": self.supplier,
"credit": self.base_paid_amount,
"credit_in_account_currency": self.base_paid_amount
if bank_account_currency == self.company_currency
@@ -1396,9 +1360,7 @@
"account": self.credit_to,
"party_type": "Supplier",
"party": self.supplier,
- "against_type": "Account",
"against": self.write_off_account,
- "against_link": self.write_off_account,
"debit": self.base_write_off_amount,
"debit_in_account_currency": self.base_write_off_amount
if self.party_account_currency == self.company_currency
@@ -1418,9 +1380,7 @@
self.get_gl_dict(
{
"account": self.write_off_account,
- "against_type": "Supplier",
"against": self.supplier,
- "against_link": self.supplier,
"credit": flt(self.base_write_off_amount),
"credit_in_account_currency": self.base_write_off_amount
if write_off_account_currency == self.company_currency
@@ -1447,9 +1407,7 @@
self.get_gl_dict(
{
"account": round_off_account,
- "against_type": "Supplier",
"against": self.supplier,
- "against_link": self.supplier,
"debit_in_account_currency": self.rounding_adjustment,
"debit": self.base_rounding_adjustment,
"cost_center": round_off_cost_center
diff --git a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
index 9cf4e4f..26984d9 100644
--- a/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
+++ b/erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
@@ -64,6 +64,7 @@
"warehouse",
"from_warehouse",
"quality_inspection",
+ "add_serial_batch_bundle",
"serial_and_batch_bundle",
"serial_no",
"col_br_wh",
@@ -913,12 +914,18 @@
"fieldtype": "Link",
"label": "WIP Composite Asset",
"options": "Asset"
+ },
+ {
+ "depends_on": "eval:parent.update_stock === 1",
+ "fieldname": "add_serial_batch_bundle",
+ "fieldtype": "Button",
+ "label": "Add Serial / Batch No"
}
],
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2023-12-25 22:00:28.043555",
+ "modified": "2024-01-21 19:46:25.537861",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice Item",
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 8da4505..cc19650 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -7,7 +7,7 @@
from frappe.contacts.doctype.address.address import get_address_display
from frappe.model.mapper import get_mapped_doc
from frappe.model.utils import get_fetch_values
-from frappe.utils import add_days, cint, flt, formatdate, get_link_to_form, getdate, nowdate
+from frappe.utils import add_days, cint, cstr, flt, formatdate, get_link_to_form, getdate, nowdate
import erpnext
from erpnext.accounts.deferred_revenue import validate_service_stop_date
@@ -1233,9 +1233,7 @@
"party_type": "Customer",
"party": self.customer,
"due_date": self.due_date,
- "against_type": "Account",
"against": self.against_income_account,
- "against_link": self.against_income_account,
"debit": base_grand_total,
"debit_in_account_currency": base_grand_total
if self.party_account_currency == self.company_currency
@@ -1264,9 +1262,7 @@
self.get_gl_dict(
{
"account": tax.account_head,
- "against_type": "Customer",
"against": self.customer,
- "against_link": self.customer,
"credit": flt(base_amount, tax.precision("tax_amount_after_discount_amount")),
"credit_in_account_currency": (
flt(base_amount, tax.precision("base_tax_amount_after_discount_amount"))
@@ -1287,9 +1283,7 @@
self.get_gl_dict(
{
"account": self.unrealized_profit_loss_account,
- "against_type": "Customer",
"against": self.customer,
- "against_link": self.customer,
"debit": flt(self.total_taxes_and_charges),
"debit_in_account_currency": flt(self.base_total_taxes_and_charges),
"cost_center": self.cost_center,
@@ -1357,9 +1351,7 @@
add_asset_activity(asset.name, _("Asset sold"))
for gle in fixed_asset_gl_entries:
- gle["against_type"] = "Customer"
gle["against"] = self.customer
- gle["against_link"] = self.customer
gl_entries.append(self.get_gl_dict(gle, item=item))
self.set_asset_status(asset)
@@ -1380,9 +1372,7 @@
self.get_gl_dict(
{
"account": income_account,
- "against_type": "Customer",
"against": self.customer,
- "against_link": self.customer,
"credit": flt(base_amount, item.precision("base_net_amount")),
"credit_in_account_currency": (
flt(base_amount, item.precision("base_net_amount"))
@@ -1436,9 +1426,9 @@
"account": self.debit_to,
"party_type": "Customer",
"party": self.customer,
- "against_type": "Account",
- "against": self.loyalty_redemption_account,
- "against_link": self.loyalty_redemption_account,
+ "against": "Expense account - "
+ + cstr(self.loyalty_redemption_account)
+ + " for the Loyalty Program",
"credit": self.loyalty_amount,
"against_voucher": self.return_against if cint(self.is_return) else self.name,
"against_voucher_type": self.doctype,
@@ -1452,9 +1442,7 @@
{
"account": self.loyalty_redemption_account,
"cost_center": self.cost_center or self.loyalty_redemption_cost_center,
- "against_type": "Customer",
"against": self.customer,
- "against_link": self.customer,
"debit": self.loyalty_amount,
"remark": "Loyalty Points redeemed by the customer",
},
@@ -1481,9 +1469,7 @@
"account": self.debit_to,
"party_type": "Customer",
"party": self.customer,
- "against_type": "Account",
"against": payment_mode.account,
- "against_link": payment_mode.account,
"credit": payment_mode.base_amount,
"credit_in_account_currency": payment_mode.base_amount
if self.party_account_currency == self.company_currency
@@ -1504,9 +1490,7 @@
self.get_gl_dict(
{
"account": payment_mode.account,
- "against_type": "Customer",
"against": self.customer,
- "against_link": self.customer,
"debit": payment_mode.base_amount,
"debit_in_account_currency": payment_mode.base_amount
if payment_mode_account_currency == self.company_currency
@@ -1530,9 +1514,7 @@
"account": self.debit_to,
"party_type": "Customer",
"party": self.customer,
- "against_type": "Account",
"against": self.account_for_change_amount,
- "against_link": self.account_for_change_amount,
"debit": flt(self.base_change_amount),
"debit_in_account_currency": flt(self.base_change_amount)
if self.party_account_currency == self.company_currency
@@ -1553,9 +1535,7 @@
self.get_gl_dict(
{
"account": self.account_for_change_amount,
- "against_type": "Customer",
"against": self.customer,
- "against_link": self.customer,
"credit": self.base_change_amount,
"cost_center": self.cost_center,
},
@@ -1581,9 +1561,7 @@
"account": self.debit_to,
"party_type": "Customer",
"party": self.customer,
- "against_type": "Account",
"against": self.write_off_account,
- "against_link": self.write_off_account,
"credit": flt(self.base_write_off_amount, self.precision("base_write_off_amount")),
"credit_in_account_currency": (
flt(self.base_write_off_amount, self.precision("base_write_off_amount"))
@@ -1603,9 +1581,7 @@
self.get_gl_dict(
{
"account": self.write_off_account,
- "against_type": "Customer",
"against": self.customer,
- "against_link": self.customer,
"debit": flt(self.base_write_off_amount, self.precision("base_write_off_amount")),
"debit_in_account_currency": (
flt(self.base_write_off_amount, self.precision("base_write_off_amount"))
@@ -1633,9 +1609,7 @@
self.get_gl_dict(
{
"account": round_off_account,
- "against_type": "Customer",
"against": self.customer,
- "against_link": self.customer,
"credit_in_account_currency": flt(
self.rounding_adjustment, self.precision("rounding_adjustment")
),
diff --git a/erpnext/accounts/doctype/subscription/subscription.py b/erpnext/accounts/doctype/subscription/subscription.py
index 72e574c..6d27806 100644
--- a/erpnext/accounts/doctype/subscription/subscription.py
+++ b/erpnext/accounts/doctype/subscription/subscription.py
@@ -16,6 +16,7 @@
date_diff,
flt,
get_last_day,
+ get_link_to_form,
getdate,
nowdate,
)
@@ -317,6 +318,37 @@
if self.is_new():
self.set_subscription_status()
+ self.validate_party_billing_currency()
+
+ def validate_party_billing_currency(self):
+ """
+ Subscription should be of the same currency as the Party's default billing currency or company default.
+ """
+ if self.party:
+ party_billing_currency = frappe.get_cached_value(
+ self.party_type, self.party, "default_currency"
+ ) or frappe.get_cached_value("Company", self.company, "default_currency")
+
+ plans = [x.plan for x in self.plans]
+ subscription_plan_currencies = frappe.db.get_all(
+ "Subscription Plan", filters={"name": ("in", plans)}, fields=["name", "currency"]
+ )
+ unsupported_plans = []
+ for x in subscription_plan_currencies:
+ if x.currency != party_billing_currency:
+ unsupported_plans.append("{0}".format(get_link_to_form("Subscription Plan", x.name)))
+
+ if unsupported_plans:
+ unsupported_plans = [
+ _(
+ "Below Subscription Plans are of different currency to the party default billing currency/Company currency: {0}"
+ ).format(frappe.bold(party_billing_currency))
+ ] + unsupported_plans
+
+ frappe.throw(
+ unsupported_plans, frappe.ValidationError, "Unsupported Subscription Plans", as_list=True
+ )
+
def validate_trial_period(self) -> None:
"""
Runs sanity checks on trial period dates for the `Subscription`
@@ -563,6 +595,8 @@
) and self.can_generate_new_invoice(posting_date):
self.generate_invoice(posting_date=posting_date)
self.update_subscription_period(add_days(self.current_invoice_end, 1))
+ elif posting_date and getdate(posting_date) > getdate(self.current_invoice_end):
+ self.update_subscription_period()
if self.cancel_at_period_end and (
getdate(posting_date) >= getdate(self.current_invoice_end)
diff --git a/erpnext/accounts/doctype/subscription/test_subscription.py b/erpnext/accounts/doctype/subscription/test_subscription.py
index 785fd04..a46642a 100644
--- a/erpnext/accounts/doctype/subscription/test_subscription.py
+++ b/erpnext/accounts/doctype/subscription/test_subscription.py
@@ -460,11 +460,13 @@
self.assertEqual(len(subscription.invoices), 1)
def test_multi_currency_subscription(self):
+ party = "_Test Subscription Customer"
+ frappe.db.set_value("Customer", party, "default_currency", "USD")
subscription = create_subscription(
start_date="2018-01-01",
generate_invoice_at="Beginning of the current subscription period",
- plans=[{"plan": "_Test Plan Multicurrency", "qty": 1}],
- party="_Test Subscription Customer",
+ plans=[{"plan": "_Test Plan Multicurrency", "qty": 1, "currency": "USD"}],
+ party=party,
)
subscription.process()
@@ -528,13 +530,21 @@
def make_plans():
- create_plan(plan_name="_Test Plan Name", cost=900)
- create_plan(plan_name="_Test Plan Name 2", cost=1999)
+ create_plan(plan_name="_Test Plan Name", cost=900, currency="INR")
+ create_plan(plan_name="_Test Plan Name 2", cost=1999, currency="INR")
create_plan(
- plan_name="_Test Plan Name 3", cost=1999, billing_interval="Day", billing_interval_count=14
+ plan_name="_Test Plan Name 3",
+ cost=1999,
+ billing_interval="Day",
+ billing_interval_count=14,
+ currency="INR",
)
create_plan(
- plan_name="_Test Plan Name 4", cost=20000, billing_interval="Month", billing_interval_count=3
+ plan_name="_Test Plan Name 4",
+ cost=20000,
+ billing_interval="Month",
+ billing_interval_count=3,
+ currency="INR",
)
create_plan(
plan_name="_Test Plan Multicurrency", cost=50, billing_interval="Month", currency="USD"
diff --git a/erpnext/accounts/doctype/subscription_plan/subscription_plan.json b/erpnext/accounts/doctype/subscription_plan/subscription_plan.json
index 563df79..bc1f579 100644
--- a/erpnext/accounts/doctype/subscription_plan/subscription_plan.json
+++ b/erpnext/accounts/doctype/subscription_plan/subscription_plan.json
@@ -41,7 +41,8 @@
"fieldname": "currency",
"fieldtype": "Link",
"label": "Currency",
- "options": "Currency"
+ "options": "Currency",
+ "reqd": 1
},
{
"fieldname": "column_break_3",
@@ -148,10 +149,11 @@
}
],
"links": [],
- "modified": "2021-12-10 15:24:15.794477",
+ "modified": "2024-01-14 17:59:34.687977",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Subscription Plan",
+ "naming_rule": "By fieldname",
"owner": "Administrator",
"permissions": [
{
@@ -193,5 +195,6 @@
],
"sort_field": "modified",
"sort_order": "DESC",
+ "states": [],
"track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/subscription_plan/subscription_plan.py b/erpnext/accounts/doctype/subscription_plan/subscription_plan.py
index 118d254..cdfa3e5 100644
--- a/erpnext/accounts/doctype/subscription_plan/subscription_plan.py
+++ b/erpnext/accounts/doctype/subscription_plan/subscription_plan.py
@@ -24,7 +24,7 @@
billing_interval_count: DF.Int
cost: DF.Currency
cost_center: DF.Link | None
- currency: DF.Link | None
+ currency: DF.Link
item: DF.Link
payment_gateway: DF.Link | None
plan_name: DF.Data
diff --git a/erpnext/accounts/general_ledger.py b/erpnext/accounts/general_ledger.py
index b48a8e6..1c8ac2f 100644
--- a/erpnext/accounts/general_ledger.py
+++ b/erpnext/accounts/general_ledger.py
@@ -280,7 +280,6 @@
"project",
"finance_book",
"voucher_no",
- "against_link",
]
if dimensions:
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.html b/erpnext/accounts/report/accounts_receivable/accounts_receivable.html
index ed3b991..7d8d33c 100644
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.html
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.html
@@ -10,10 +10,8 @@
<h2 class="text-center" style="margin-top:0">{%= __(report.report_name) %}</h2>
<h4 class="text-center">
- {% if (filters.customer_name) { %}
- {%= filters.customer_name %}
- {% } else { %}
- {%= filters.customer || filters.supplier %}
+ {% if (filters.party) { %}
+ {%= __(filters.party) %}
{% } %}
</h4>
<h6 class="text-center">
@@ -141,7 +139,7 @@
<th style="width: 24%">{%= __("Reference") %}</th>
{% } %}
{% if(!filters.show_future_payments) { %}
- <th style="width: 20%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
+ <th style="width: 20%">{%= (filters.party) ? __("Remarks"): __("Party") %}</th>
{% } %}
<th style="width: 10%; text-align: right">{%= __("Invoiced Amount") %}</th>
{% if(!filters.show_future_payments) { %}
@@ -158,7 +156,7 @@
<th style="width: 10%">{%= __("Remaining Balance") %}</th>
{% } %}
{% } else { %}
- <th style="width: 40%">{%= (filters.customer || filters.supplier) ? __("Remarks"): __("Party") %}</th>
+ <th style="width: 40%">{%= (filters.party) ? __("Remarks"): __("Party") %}</th>
<th style="width: 15%">{%= __("Total Invoiced Amount") %}</th>
<th style="width: 15%">{%= __("Total Paid Amount") %}</th>
<th style="width: 15%">{%= report.report_name === "Accounts Receivable Summary" ? __('Credit Note Amount') : __('Debit Note Amount') %}</th>
@@ -187,7 +185,7 @@
{% if(!filters.show_future_payments) { %}
<td>
- {% if(!(filters.customer || filters.supplier)) { %}
+ {% if(!(filters.party)) { %}
{%= data[i]["party"] %}
{% if(data[i]["customer_name"] && data[i]["customer_name"] != data[i]["party"]) { %}
<br> {%= data[i]["customer_name"] %}
@@ -260,7 +258,7 @@
{% if(data[i]["party"]|| " ") { %}
{% if(!data[i]["is_total_row"]) { %}
<td>
- {% if(!(filters.customer || filters.supplier)) { %}
+ {% if(!(filters.party)) { %}
{%= data[i]["party"] %}
{% if(data[i]["customer_name"] && data[i]["customer_name"] != data[i]["party"]) { %}
<br> {%= data[i]["customer_name"] %}
diff --git a/erpnext/accounts/report/budget_variance_report/budget_variance_report.js b/erpnext/accounts/report/budget_variance_report/budget_variance_report.js
index 9c356bf..d6a4755 100644
--- a/erpnext/accounts/report/budget_variance_report/budget_variance_report.js
+++ b/erpnext/accounts/report/budget_variance_report/budget_variance_report.js
@@ -84,10 +84,6 @@
options: budget_against_options,
default: "Cost Center",
reqd: 1,
- get_data: function() {
- console.log(this.options);
- return ["Emacs", "Rocks"];
- },
on_change: function() {
frappe.query_report.set_filter_value("budget_against_filter", []);
frappe.query_report.refresh();
diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py
index 004a929..aadd873 100644
--- a/erpnext/accounts/report/financial_statements.py
+++ b/erpnext/accounts/report/financial_statements.py
@@ -8,17 +8,7 @@
import frappe
from frappe import _
-from frappe.utils import (
- add_days,
- add_months,
- cint,
- cstr,
- flt,
- formatdate,
- get_first_day,
- getdate,
- today,
-)
+from frappe.utils import add_days, add_months, cint, cstr, flt, formatdate, get_first_day, getdate
from erpnext.accounts.doctype.accounting_dimension.accounting_dimension import (
get_accounting_dimensions,
@@ -53,8 +43,6 @@
year_start_date = getdate(period_start_date)
year_end_date = getdate(period_end_date)
- year_end_date = getdate(today()) if year_end_date > getdate(today()) else year_end_date
-
months_to_add = {"Yearly": 12, "Half-Yearly": 6, "Quarterly": 3, "Monthly": 1}[periodicity]
period_list = []
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py
index 6636b8e..110ec75 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.py
+++ b/erpnext/accounts/report/general_ledger/general_ledger.py
@@ -203,7 +203,7 @@
voucher_type, voucher_subtype, voucher_no, {dimension_fields}
cost_center, project, {transaction_currency_fields}
against_voucher_type, against_voucher, account_currency,
- against_link, against, is_opening, creation {select_fields}
+ against, is_opening, creation {select_fields}
from `tabGL Entry`
where company=%(company)s {conditions}
{order_by_statement}
@@ -398,7 +398,6 @@
group_by = group_by_field(filters.get("group_by"))
for gle in gl_entries:
- gle.against = gle.get("against_link") or gle.get("against")
gle_map.setdefault(gle.get(group_by), _dict(totals=get_totals_dict(), entries=[]))
return gle_map
@@ -449,6 +448,10 @@
for gle in gl_entries:
group_by_value = gle.get(group_by)
gle.voucher_type = _(gle.voucher_type)
+ gle.voucher_subtype = _(gle.voucher_subtype)
+ gle.against_voucher_type = _(gle.against_voucher_type)
+ gle.remarks = _(gle.remarks)
+ gle.party_type = _(gle.party_type)
if gle.posting_date < from_date or (cstr(gle.is_opening) == "Yes" and not show_opening_entries):
if not group_by_voucher_consolidated:
diff --git a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py
index 604bc01..a52aaff 100644
--- a/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py
+++ b/erpnext/accounts/report/gross_and_net_profit_report/gross_and_net_profit_report.py
@@ -134,7 +134,7 @@
def remove_parent_with_no_child(data):
data_to_be_removed = False
- for parent in data:
+ for parent in list(data):
if "is_group" in parent and parent.get("is_group") == 1:
have_child = False
for child in data:
diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js
index 39fb3ca..06f426b 100644
--- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js
+++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.js
@@ -59,7 +59,21 @@
"fieldname": "group_by",
"fieldtype": "Select",
"options": ["Customer Group", "Customer", "Item Group", "Item", "Territory", "Invoice"]
- }
+ },
+ {
+ "fieldname": "income_account",
+ "label": __("Income Account"),
+ "fieldtype": "Link",
+ "options": "Account",
+ get_query: () => {
+ let company = frappe.query_report.get_filter_value('company');
+ return {
+ filters: {
+ 'company': company,
+ }
+ };
+ }
+ },
],
"formatter": function(value, row, column, data, default_formatter) {
value = default_formatter(value, row, column, data);
diff --git a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
index ce22d75..56ae41a 100644
--- a/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
+++ b/erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py
@@ -83,9 +83,7 @@
"company": d.company,
"sales_order": d.sales_order,
"delivery_note": d.delivery_note,
- "income_account": d.unrealized_profit_loss_account
- if d.is_internal_customer == 1
- else d.income_account,
+ "income_account": get_income_account(d),
"cost_center": d.cost_center,
"stock_qty": d.stock_qty,
"stock_uom": d.stock_uom,
@@ -150,6 +148,15 @@
return columns, data, None, None, None, skip_total_row
+def get_income_account(row):
+ if row.enable_deferred_revenue:
+ return row.deferred_revenue_account
+ elif row.is_internal_customer == 1:
+ return row.unrealized_profit_loss_account
+ else:
+ return row.income_account
+
+
def get_columns(additional_table_columns, filters):
columns = []
@@ -358,6 +365,13 @@
if filters.get("item_group"):
conditions += """and ifnull(`tabSales Invoice Item`.item_group, '') = %(item_group)s"""
+ if filters.get("income_account"):
+ conditions += """
+ and (ifnull(`tabSales Invoice Item`.income_account, '') = %(income_account)s
+ or ifnull(`tabSales Invoice Item`.deferred_revenue_account, '') = %(income_account)s
+ or ifnull(`tabSales Invoice`.unrealized_profit_loss_account, '') = %(income_account)s)
+ """
+
if not filters.get("group_by"):
conditions += (
"ORDER BY `tabSales Invoice`.posting_date desc, `tabSales Invoice Item`.item_group desc"
@@ -399,6 +413,7 @@
`tabItem`.`item_name` as i_item_name, `tabItem`.`item_group` as i_item_group,
`tabSales Invoice Item`.sales_order, `tabSales Invoice Item`.delivery_note,
`tabSales Invoice Item`.income_account, `tabSales Invoice Item`.cost_center,
+ `tabSales Invoice Item`.enable_deferred_revenue, `tabSales Invoice Item`.deferred_revenue_account,
`tabSales Invoice Item`.stock_qty, `tabSales Invoice Item`.stock_uom,
`tabSales Invoice Item`.base_net_rate, `tabSales Invoice Item`.base_net_amount,
`tabSales Invoice`.customer_name, `tabSales Invoice`.customer_group, `tabSales Invoice Item`.so_detail,
diff --git a/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py b/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py
index d045d91..4a80dd0 100644
--- a/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py
+++ b/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py
@@ -46,12 +46,10 @@
out = []
for name, details in gle_map.items():
- tax_amount, total_amount, grand_total, base_total = 0, 0, 0, 0
- bill_no, bill_date = "", ""
- tax_withholding_category = tax_category_map.get(name)
- rate = tax_rate_map.get(tax_withholding_category)
-
for entry in details:
+ tax_amount, total_amount, grand_total, base_total = 0, 0, 0, 0
+ tax_withholding_category, rate = None, None
+ bill_no, bill_date = "", ""
party = entry.party or entry.against
posting_date = entry.posting_date
voucher_type = entry.voucher_type
@@ -61,12 +59,19 @@
if party_list:
party = party_list[0]
- if not tax_withholding_category:
- tax_withholding_category = party_map.get(party, {}).get("tax_withholding_category")
- rate = tax_rate_map.get(tax_withholding_category)
-
- if entry.account in tds_accounts:
+ if entry.account in tds_accounts.keys():
tax_amount += entry.credit - entry.debit
+ # infer tax withholding category from the account if it's the single account for this category
+ tax_withholding_category = tds_accounts.get(entry.account)
+ rate = tax_rate_map.get(tax_withholding_category)
+ # or else the consolidated value from the voucher document
+ if not tax_withholding_category:
+ # or else from the party default
+ tax_withholding_category = tax_category_map.get(name)
+ rate = tax_rate_map.get(tax_withholding_category)
+ if not tax_withholding_category:
+ tax_withholding_category = party_map.get(party, {}).get("tax_withholding_category")
+ rate = tax_rate_map.get(tax_withholding_category)
if net_total_map.get(name):
if voucher_type == "Journal Entry" and tax_amount and rate:
@@ -80,41 +85,41 @@
else:
total_amount += entry.credit
- if tax_amount:
- if party_map.get(party, {}).get("party_type") == "Supplier":
- party_name = "supplier_name"
- party_type = "supplier_type"
- else:
- party_name = "customer_name"
- party_type = "customer_type"
+ if tax_amount:
+ if party_map.get(party, {}).get("party_type") == "Supplier":
+ party_name = "supplier_name"
+ party_type = "supplier_type"
+ else:
+ party_name = "customer_name"
+ party_type = "customer_type"
- row = {
- "pan"
- if frappe.db.has_column(filters.party_type, "pan")
- else "tax_id": party_map.get(party, {}).get("pan"),
- "party": party_map.get(party, {}).get("name"),
- }
-
- if filters.naming_series == "Naming Series":
- row.update({"party_name": party_map.get(party, {}).get(party_name)})
-
- row.update(
- {
- "section_code": tax_withholding_category or "",
- "entity_type": party_map.get(party, {}).get(party_type),
- "rate": rate,
- "total_amount": total_amount,
- "grand_total": grand_total,
- "base_total": base_total,
- "tax_amount": tax_amount,
- "transaction_date": posting_date,
- "transaction_type": voucher_type,
- "ref_no": name,
- "supplier_invoice_no": bill_no,
- "supplier_invoice_date": bill_date,
+ row = {
+ "pan"
+ if frappe.db.has_column(filters.party_type, "pan")
+ else "tax_id": party_map.get(party, {}).get("pan"),
+ "party": party_map.get(party, {}).get("name"),
}
- )
- out.append(row)
+
+ if filters.naming_series == "Naming Series":
+ row.update({"party_name": party_map.get(party, {}).get(party_name)})
+
+ row.update(
+ {
+ "section_code": tax_withholding_category or "",
+ "entity_type": party_map.get(party, {}).get(party_type),
+ "rate": rate,
+ "total_amount": total_amount,
+ "grand_total": grand_total,
+ "base_total": base_total,
+ "tax_amount": tax_amount,
+ "transaction_date": posting_date,
+ "transaction_type": voucher_type,
+ "ref_no": name,
+ "supplier_invoice_no": bill_no,
+ "supplier_invoice_date": bill_date,
+ }
+ )
+ out.append(row)
out.sort(key=lambda x: x["section_code"])
@@ -282,11 +287,20 @@
journal_entry_party_map = frappe._dict()
bank_accounts = frappe.get_all("Account", {"is_group": 0, "account_type": "Bank"}, pluck="name")
- tds_accounts = frappe.get_all(
- "Tax Withholding Account", {"company": filters.get("company")}, pluck="account"
+ _tds_accounts = frappe.get_all(
+ "Tax Withholding Account",
+ {"company": filters.get("company")},
+ ["account", "parent"],
)
+ tds_accounts = {}
+ for tds_acc in _tds_accounts:
+ # if it turns out not to be the only tax withholding category, then don't include in the map
+ if tds_accounts.get(tds_acc["account"]):
+ tds_accounts[tds_acc["account"]] = None
+ else:
+ tds_accounts[tds_acc["account"]] = tds_acc["parent"]
- tds_docs = get_tds_docs_query(filters, bank_accounts, tds_accounts).run(as_dict=True)
+ tds_docs = get_tds_docs_query(filters, bank_accounts, list(tds_accounts.keys())).run(as_dict=True)
for d in tds_docs:
if d.voucher_type == "Purchase Invoice":
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 5525af4..65b3aba 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -618,7 +618,10 @@
jv_detail = journal_entry.get("accounts", {"name": d["voucher_detail_no"]})[0]
# Update Advance Paid in SO/PO since they might be getting unlinked
- if jv_detail.get("reference_type") in ("Sales Order", "Purchase Order"):
+ advance_payment_doctypes = frappe.get_hooks(
+ "advance_payment_customer_doctypes"
+ ) + frappe.get_hooks("advance_payment_supplier_doctypes")
+ if jv_detail.get("reference_type") in advance_payment_doctypes:
frappe.get_doc(jv_detail.reference_type, jv_detail.reference_name).set_total_advance_paid()
if flt(d["unadjusted_amount"]) - flt(d["allocated_amount"]) != 0:
@@ -660,7 +663,6 @@
new_row.set("reference_name", d["against_voucher"])
new_row.against_account = cstr(jv_detail.against_account)
- new_row.against_account_link = cstr(jv_detail.against_account)
new_row.is_advance = cstr(jv_detail.is_advance)
new_row.docstatus = 1
@@ -693,7 +695,10 @@
existing_row = payment_entry.get("references", {"name": d["voucher_detail_no"]})[0]
# Update Advance Paid in SO/PO since they are getting unlinked
- if existing_row.get("reference_doctype") in ("Sales Order", "Purchase Order"):
+ advance_payment_doctypes = frappe.get_hooks(
+ "advance_payment_customer_doctypes"
+ ) + frappe.get_hooks("advance_payment_supplier_doctypes")
+ if existing_row.get("reference_doctype") in advance_payment_doctypes:
frappe.get_doc(
existing_row.reference_doctype, existing_row.reference_name
).set_total_advance_paid()
diff --git a/erpnext/accounts/workspace/accounting/accounting.json b/erpnext/accounts/workspace/accounting/accounting.json
index f4aced2..45ab92e 100644
--- a/erpnext/accounts/workspace/accounting/accounting.json
+++ b/erpnext/accounts/workspace/accounting/accounting.json
@@ -5,7 +5,7 @@
"label": "Profit and Loss"
}
],
- "content": "[{\"id\":\"MmUf9abwxg\",\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Accounts\",\"col\":12}},{\"id\":\"VVvJ1lUcfc\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"Total Outgoing Bills\",\"col\":3}},{\"id\":\"Vlj2FZtlHV\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"Total Incoming Bills\",\"col\":3}},{\"id\":\"VVVjQVAhPf\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"Total Incoming Payment\",\"col\":3}},{\"id\":\"DySNdlysIW\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"Total Outgoing Payment\",\"col\":3}},{\"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 & 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\":\"Banking\",\"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\":\"nDhfcJYbKH\",\"type\":\"chart\",\"data\":{\"chart_name\":\"Profit and Loss\",\"col\":12}},{\"id\":\"VVvJ1lUcfc\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"Total Outgoing Bills\",\"col\":3}},{\"id\":\"Vlj2FZtlHV\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"Total Incoming Bills\",\"col\":3}},{\"id\":\"VVVjQVAhPf\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"Total Incoming Payment\",\"col\":3}},{\"id\":\"DySNdlysIW\",\"type\":\"number_card\",\"data\":{\"number_card_name\":\"Total Outgoing Payment\",\"col\":3}},{\"id\":\"9k1rDm2C0l\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"vikWSkNm6_\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>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 & Masters</b></span>\",\"col\":12}},{\"id\":\"DnNtsmxpty\",\"type\":\"card\",\"data\":{\"card_name\":\"Accounting Masters\",\"col\":4}},{\"id\":\"nKKr6fjgjb\",\"type\":\"card\",\"data\":{\"card_name\":\"Payments\",\"col\":4}},{\"id\":\"KlqilF5R_V\",\"type\":\"card\",\"data\":{\"card_name\":\"Tax Masters\",\"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\":\"Banking\",\"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}}]",
"creation": "2020-03-02 15:41:59.515192",
"custom_blocks": [],
"docstatus": 0,
@@ -14,565 +14,13 @@
"hide_custom": 0,
"icon": "accounting",
"idx": 0,
+ "indicator_color": "",
"is_hidden": 0,
"label": "Accounting",
"links": [
{
"hidden": 0,
"is_query_report": 0,
- "label": "Accounting Masters",
- "link_count": 0,
- "onboard": 0,
- "type": "Card Break"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Company",
- "link_count": 0,
- "link_to": "Company",
- "link_type": "DocType",
- "onboard": 1,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Chart of Accounts",
- "link_count": 0,
- "link_to": "Account",
- "link_type": "DocType",
- "onboard": 1,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Accounts Settings",
- "link_count": 0,
- "link_to": "Accounts Settings",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Fiscal Year",
- "link_count": 0,
- "link_to": "Fiscal Year",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Accounting Dimension",
- "link_count": 0,
- "link_to": "Accounting Dimension",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Finance Book",
- "link_count": 0,
- "link_to": "Finance Book",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Accounting Period",
- "link_count": 0,
- "link_to": "Accounting Period",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Payment Term",
- "link_count": 0,
- "link_to": "Payment Term",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "hidden": 0,
- "is_query_report": 0,
- "label": "General Ledger",
- "link_count": 0,
- "onboard": 0,
- "type": "Card Break"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Journal Entry",
- "link_count": 0,
- "link_to": "Journal Entry",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Journal Entry Template",
- "link_count": 0,
- "link_to": "Journal Entry Template",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "GL Entry",
- "hidden": 0,
- "is_query_report": 1,
- "label": "General Ledger",
- "link_count": 0,
- "link_to": "General Ledger",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "Sales Invoice",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Customer Ledger Summary",
- "link_count": 0,
- "link_to": "Customer Ledger Summary",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "Sales Invoice",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Supplier Ledger Summary",
- "link_count": 0,
- "link_to": "Supplier Ledger Summary",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "hidden": 0,
- "is_query_report": 0,
- "label": "Accounts Receivable",
- "link_count": 0,
- "onboard": 0,
- "type": "Card Break"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Sales Invoice",
- "link_count": 0,
- "link_to": "Sales Invoice",
- "link_type": "DocType",
- "onboard": 1,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Customer",
- "link_count": 0,
- "link_to": "Customer",
- "link_type": "DocType",
- "onboard": 1,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Payment Entry",
- "link_count": 0,
- "link_to": "Payment Entry",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Payment Request",
- "link_count": 0,
- "link_to": "Payment Request",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "hidden": 0,
- "is_query_report": 0,
- "label": "Payment Reconciliation",
- "link_count": 0,
- "link_to": "Payment Reconciliation",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "Sales Invoice",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Accounts Receivable",
- "link_count": 0,
- "link_to": "Accounts Receivable",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "Sales Invoice",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Accounts Receivable Summary",
- "link_count": 0,
- "link_to": "Accounts Receivable Summary",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "Sales Invoice",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Sales Register",
- "link_count": 0,
- "link_to": "Sales Register",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "Sales Invoice",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Item-wise Sales Register",
- "link_count": 0,
- "link_to": "Item-wise Sales Register",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "Sales Invoice",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Sales Order Analysis",
- "link_count": 0,
- "link_to": "Sales Order Analysis",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "Sales Invoice",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Delivered Items To Be Billed",
- "link_count": 0,
- "link_to": "Delivered Items To Be Billed",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "hidden": 0,
- "is_query_report": 0,
- "label": "Accounts Payable",
- "link_count": 0,
- "onboard": 0,
- "type": "Card Break"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Purchase Invoice",
- "link_count": 0,
- "link_to": "Purchase Invoice",
- "link_type": "DocType",
- "onboard": 1,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Supplier",
- "link_count": 0,
- "link_to": "Supplier",
- "link_type": "DocType",
- "onboard": 1,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Payment Entry",
- "link_count": 0,
- "link_to": "Payment Entry",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "hidden": 0,
- "is_query_report": 0,
- "label": "Payment Reconciliation",
- "link_count": 0,
- "link_to": "Payment Reconciliation",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "Purchase Invoice",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Accounts Payable",
- "link_count": 0,
- "link_to": "Accounts Payable",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "Purchase Invoice",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Accounts Payable Summary",
- "link_count": 0,
- "link_to": "Accounts Payable Summary",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "Purchase Invoice",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Purchase Register",
- "link_count": 0,
- "link_to": "Purchase Register",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "Purchase Invoice",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Item-wise Purchase Register",
- "link_count": 0,
- "link_to": "Item-wise Purchase Register",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "Purchase Order",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Purchase Order Analysis",
- "link_count": 0,
- "link_to": "Purchase Order Analysis",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "Purchase Invoice",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Received Items To Be Billed",
- "link_count": 0,
- "link_to": "Received Items To Be Billed",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "hidden": 0,
- "is_query_report": 0,
- "label": "Reports",
- "link_count": 0,
- "onboard": 0,
- "type": "Card Break"
- },
- {
- "dependencies": "GL Entry",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Trial Balance for Party",
- "link_count": 0,
- "link_to": "Trial Balance for Party",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "Journal Entry",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Payment Period Based On Invoice Date",
- "link_count": 0,
- "link_to": "Payment Period Based On Invoice Date",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "Sales Invoice",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Sales Partners Commission",
- "link_count": 0,
- "link_to": "Sales Partners Commission",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "Customer",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Customer Credit Balance",
- "link_count": 0,
- "link_to": "Customer Credit Balance",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "Sales Invoice",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Sales Payment Summary",
- "link_count": 0,
- "link_to": "Sales Payment Summary",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "Address",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Address And Contacts",
- "link_count": 0,
- "link_to": "Address And Contacts",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "GL Entry",
- "hidden": 0,
- "is_query_report": 1,
- "label": "UAE VAT 201",
- "link_count": 0,
- "link_to": "UAE VAT 201",
- "link_type": "Report",
- "onboard": 0,
- "only_for": "United Arab Emirates",
- "type": "Link"
- },
- {
- "hidden": 0,
- "is_query_report": 0,
- "label": "Financial Statements",
- "link_count": 0,
- "onboard": 0,
- "type": "Card Break"
- },
- {
- "dependencies": "GL Entry",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Trial Balance",
- "link_count": 0,
- "link_to": "Trial Balance",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "GL Entry",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Profit and Loss Statement",
- "link_count": 0,
- "link_to": "Profit and Loss Statement",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "GL Entry",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Balance Sheet",
- "link_count": 0,
- "link_to": "Balance Sheet",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "GL Entry",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Cash Flow",
- "link_count": 0,
- "link_to": "Cash Flow",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "GL Entry",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Consolidated Financial Statement",
- "link_count": 0,
- "link_to": "Consolidated Financial Statement",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "hidden": 0,
- "is_query_report": 0,
"label": "Multi Currency",
"link_count": 0,
"onboard": 0,
@@ -614,102 +62,6 @@
{
"hidden": 0,
"is_query_report": 0,
- "label": "Settings",
- "link_count": 0,
- "onboard": 0,
- "type": "Card Break"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Payment Gateway Account",
- "link_count": 0,
- "link_to": "Payment Gateway Account",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Terms and Conditions Template",
- "link_count": 0,
- "link_to": "Terms and Conditions",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Mode of Payment",
- "link_count": 0,
- "link_to": "Mode of Payment",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Bank",
- "link_count": 0,
- "link_to": "Bank",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Bank Account",
- "link_count": 0,
- "link_to": "Bank Account",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Bank Clearance",
- "link_count": 0,
- "link_to": "Bank Clearance",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "",
- "hidden": 0,
- "is_query_report": 0,
- "label": "Bank Reconciliation Tool",
- "link_count": 0,
- "link_to": "Bank Reconciliation Tool",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "GL Entry",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Bank Reconciliation Statement",
- "link_count": 0,
- "link_to": "Bank Reconciliation Statement",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "hidden": 0,
- "is_query_report": 0,
"label": "Subscription Management",
"link_count": 0,
"onboard": 0,
@@ -918,8 +270,83 @@
{
"hidden": 0,
"is_query_report": 0,
- "label": "Taxes",
+ "label": "Banking",
+ "link_count": 6,
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Card Break"
+ },
+ {
+ "dependencies": "",
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Bank",
"link_count": 0,
+ "link_to": "Bank",
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "",
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Bank Account",
+ "link_count": 0,
+ "link_to": "Bank Account",
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "",
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Bank Clearance",
+ "link_count": 0,
+ "link_to": "Bank Clearance",
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "",
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Bank Reconciliation Tool",
+ "link_count": 0,
+ "link_to": "Bank Reconciliation Tool",
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "GL Entry",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Bank Reconciliation Statement",
+ "link_count": 0,
+ "link_to": "Bank Reconciliation Statement",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Plaid Settings",
+ "link_count": 0,
+ "link_to": "Plaid Settings",
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Tax Masters",
+ "link_count": 7,
+ "link_type": "DocType",
"onboard": 0,
"type": "Card Break"
},
@@ -1003,60 +430,8 @@
{
"hidden": 0,
"is_query_report": 0,
- "label": "Profitability",
- "link_count": 0,
- "onboard": 0,
- "type": "Card Break"
- },
- {
- "dependencies": "Sales Invoice",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Gross Profit",
- "link_count": 0,
- "link_to": "Gross Profit",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "GL Entry",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Profitability Analysis",
- "link_count": 0,
- "link_to": "Profitability Analysis",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "Sales Invoice",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Sales Invoice Trends",
- "link_count": 0,
- "link_to": "Sales Invoice Trends",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "dependencies": "Purchase Invoice",
- "hidden": 0,
- "is_query_report": 1,
- "label": "Purchase Invoice Trends",
- "link_count": 0,
- "link_to": "Purchase Invoice Trends",
- "link_type": "Report",
- "onboard": 0,
- "type": "Link"
- },
- {
- "hidden": 0,
- "is_query_report": 0,
- "label": "Banking",
- "link_count": 6,
+ "label": "Accounting Masters",
+ "link_count": 8,
"link_type": "DocType",
"onboard": 0,
"type": "Card Break"
@@ -1065,9 +440,31 @@
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
- "label": "Bank",
+ "label": "Company",
"link_count": 0,
- "link_to": "Bank",
+ "link_to": "Company",
+ "link_type": "DocType",
+ "onboard": 1,
+ "type": "Link"
+ },
+ {
+ "dependencies": "",
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Chart of Accounts",
+ "link_count": 0,
+ "link_to": "Account",
+ "link_type": "DocType",
+ "onboard": 1,
+ "type": "Link"
+ },
+ {
+ "dependencies": "",
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Accounts Settings",
+ "link_count": 0,
+ "link_to": "Accounts Settings",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
@@ -1076,9 +473,9 @@
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
- "label": "Bank Account",
+ "label": "Fiscal Year",
"link_count": 0,
- "link_to": "Bank Account",
+ "link_to": "Fiscal Year",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
@@ -1087,9 +484,9 @@
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
- "label": "Bank Clearance",
+ "label": "Accounting Dimension",
"link_count": 0,
- "link_to": "Bank Clearance",
+ "link_to": "Accounting Dimension",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
@@ -1098,36 +495,98 @@
"dependencies": "",
"hidden": 0,
"is_query_report": 0,
- "label": "Bank Reconciliation Tool",
+ "label": "Finance Book",
"link_count": 0,
- "link_to": "Bank Reconciliation Tool",
+ "link_to": "Finance Book",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
- "dependencies": "GL Entry",
+ "dependencies": "",
"hidden": 0,
- "is_query_report": 1,
- "label": "Bank Reconciliation Statement",
+ "is_query_report": 0,
+ "label": "Accounting Period",
"link_count": 0,
- "link_to": "Bank Reconciliation Statement",
- "link_type": "Report",
+ "link_to": "Accounting Period",
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "",
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Payment Term",
+ "link_count": 0,
+ "link_to": "Payment Term",
+ "link_type": "DocType",
"onboard": 0,
"type": "Link"
},
{
"hidden": 0,
"is_query_report": 0,
- "label": "Plaid Settings",
+ "label": "Payments",
+ "link_count": 5,
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Card Break"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Payment Entry",
"link_count": 0,
- "link_to": "Plaid Settings",
+ "link_to": "Payment Entry",
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "",
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Journal Entry",
+ "link_count": 0,
+ "link_to": "Journal Entry",
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "",
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Journal Entry Template",
+ "link_count": 0,
+ "link_to": "Journal Entry Template",
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Terms and Conditions",
+ "link_count": 0,
+ "link_to": "Terms and Conditions",
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Mode of Payment",
+ "link_count": 0,
+ "link_to": "Mode of Payment",
"link_type": "DocType",
"onboard": 0,
"type": "Link"
}
],
- "modified": "2024-01-02 15:21:09.895531",
+ "modified": "2024-01-18 22:15:40.941711",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounting",
diff --git a/erpnext/accounts/workspace/financial_reports/financial_reports.json b/erpnext/accounts/workspace/financial_reports/financial_reports.json
new file mode 100644
index 0000000..3ab4a3e
--- /dev/null
+++ b/erpnext/accounts/workspace/financial_reports/financial_reports.json
@@ -0,0 +1,277 @@
+{
+ "charts": [],
+ "content": "[{\"id\":\"nKKr6fjgjb\",\"type\":\"card\",\"data\":{\"card_name\":\"Ledgers\",\"col\":4}},{\"id\":\"p7NY6MHe2Y\",\"type\":\"card\",\"data\":{\"card_name\":\"Financial Statements\",\"col\":4}},{\"id\":\"3AK1Zf0oew\",\"type\":\"card\",\"data\":{\"card_name\":\"Profitability\",\"col\":4}},{\"id\":\"Q_hBCnSeJY\",\"type\":\"card\",\"data\":{\"card_name\":\"Other Reports\",\"col\":4}}]",
+ "creation": "2024-01-05 16:09:16.766939",
+ "custom_blocks": [],
+ "docstatus": 0,
+ "doctype": "Workspace",
+ "for_user": "",
+ "hide_custom": 0,
+ "icon": "file",
+ "idx": 0,
+ "indicator_color": "",
+ "is_hidden": 0,
+ "label": "Financial Reports",
+ "links": [
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Profitability",
+ "link_count": 0,
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Card Break"
+ },
+ {
+ "dependencies": "Sales Invoice",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Gross Profit",
+ "link_count": 0,
+ "link_to": "Gross Profit",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "GL Entry",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Profitability Analysis",
+ "link_count": 0,
+ "link_to": "Profitability Analysis",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "Sales Invoice",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Sales Invoice Trends",
+ "link_count": 0,
+ "link_to": "Sales Invoice Trends",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "Purchase Invoice",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Purchase Invoice Trends",
+ "link_count": 0,
+ "link_to": "Purchase Invoice Trends",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Financial Statements",
+ "link_count": 5,
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Card Break"
+ },
+ {
+ "dependencies": "GL Entry",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Trial Balance",
+ "link_count": 0,
+ "link_to": "Trial Balance",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "GL Entry",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Profit and Loss Statement",
+ "link_count": 0,
+ "link_to": "Profit and Loss Statement",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "GL Entry",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Balance Sheet",
+ "link_count": 0,
+ "link_to": "Balance Sheet",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "GL Entry",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Cash Flow",
+ "link_count": 0,
+ "link_to": "Cash Flow",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "GL Entry",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Consolidated Financial Statement",
+ "link_count": 0,
+ "link_to": "Consolidated Financial Statement",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Ledgers",
+ "link_count": 3,
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Card Break"
+ },
+ {
+ "dependencies": "GL Entry",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "General Ledger",
+ "link_count": 0,
+ "link_to": "General Ledger",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "Sales Invoice",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Customer Ledger Summary",
+ "link_count": 0,
+ "link_to": "Customer Ledger Summary",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "Sales Invoice",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Supplier Ledger Summary",
+ "link_count": 0,
+ "link_to": "Supplier Ledger Summary",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Other Reports",
+ "link_count": 7,
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Card Break"
+ },
+ {
+ "dependencies": "GL Entry",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Trial Balance for Party",
+ "link_count": 0,
+ "link_to": "Trial Balance for Party",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "Journal Entry",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Payment Period Based On Invoice Date",
+ "link_count": 0,
+ "link_to": "Payment Period Based On Invoice Date",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "Sales Invoice",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Sales Partners Commission",
+ "link_count": 0,
+ "link_to": "Sales Partners Commission",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "Customer",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Customer Credit Balance",
+ "link_count": 0,
+ "link_to": "Customer Credit Balance",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "Sales Invoice",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Sales Payment Summary",
+ "link_count": 0,
+ "link_to": "Sales Payment Summary",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "Address",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Address And Contacts",
+ "link_count": 0,
+ "link_to": "Address And Contacts",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "GL Entry",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "UAE VAT 201",
+ "link_count": 0,
+ "link_to": "UAE VAT 201",
+ "link_type": "Report",
+ "onboard": 0,
+ "only_for": "United Arab Emirates",
+ "type": "Link"
+ }
+ ],
+ "modified": "2024-01-18 22:13:07.596844",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Financial Reports",
+ "number_cards": [],
+ "owner": "Administrator",
+ "parent_page": "Accounting",
+ "public": 1,
+ "quick_lists": [],
+ "restrict_to_domain": "",
+ "roles": [],
+ "sequence_id": 5.0,
+ "shortcuts": [],
+ "title": "Financial Reports"
+}
\ No newline at end of file
diff --git a/erpnext/accounts/workspace/payables/payables.json b/erpnext/accounts/workspace/payables/payables.json
new file mode 100644
index 0000000..f8c8564
--- /dev/null
+++ b/erpnext/accounts/workspace/payables/payables.json
@@ -0,0 +1,204 @@
+{
+ "charts": [],
+ "content": "[{\"id\":\"rMMsfn2eB4\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Shortcuts</b></span>\",\"col\":12}},{\"id\":\"G984SgVRJN\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Purchase Invoice\",\"col\":3}},{\"id\":\"F9f4I1viNr\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Payment Entry\",\"col\":3}},{\"id\":\"1ArNvt9qhz\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Journal Entry\",\"col\":3}},{\"id\":\"4IBBOIxfqW\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Accounts Payable\",\"col\":3}},{\"id\":\"B7-uxs8tkU\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"jAcOH-cC-Q\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"col\":12}},{\"id\":\"7dj93PEUjW\",\"type\":\"card\",\"data\":{\"card_name\":\"Invoicing\",\"col\":4}},{\"id\":\"_Cb7C8XdJJ\",\"type\":\"card\",\"data\":{\"card_name\":\"Payments\",\"col\":4}},{\"id\":\"9yseIkdG50\",\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}}]",
+ "creation": "2024-01-05 15:29:11.144373",
+ "custom_blocks": [],
+ "docstatus": 0,
+ "doctype": "Workspace",
+ "for_user": "",
+ "hide_custom": 0,
+ "icon": "arrow-left",
+ "idx": 0,
+ "indicator_color": "",
+ "is_hidden": 0,
+ "label": "Payables",
+ "links": [
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Invoicing",
+ "link_count": 2,
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Card Break"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Purchase Invoice",
+ "link_count": 0,
+ "link_to": "Purchase Invoice",
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Supplier",
+ "link_count": 0,
+ "link_to": "Supplier",
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Payments",
+ "link_count": 3,
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Card Break"
+ },
+ {
+ "dependencies": "",
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Payment Entry",
+ "link_count": 0,
+ "link_to": "Payment Entry",
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Journal Entry",
+ "link_count": 0,
+ "link_to": "Journal Entry",
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Payment Reconciliation",
+ "link_count": 0,
+ "link_to": "Payment Reconciliation",
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Reports",
+ "link_count": 7,
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Card Break"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Accounts Payable",
+ "link_count": 0,
+ "link_to": "Accounts Payable",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Accounts Payable Summary",
+ "link_count": 0,
+ "link_to": "Accounts Payable Summary",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Purchase Register",
+ "link_count": 0,
+ "link_to": "Purchase Register",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Item-wise Purchase Register",
+ "link_count": 0,
+ "link_to": "Item-wise Purchase Register",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Purchase Order Analysis",
+ "link_count": 0,
+ "link_to": "Purchase Order Analysis",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Received Items To Be Billed",
+ "link_count": 0,
+ "link_to": "Received Items To Be Billed",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Supplier Ledger Summary",
+ "link_count": 0,
+ "link_to": "Supplier Ledger Summary",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ }
+ ],
+ "modified": "2024-01-18 22:09:46.221549",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Payables",
+ "number_cards": [],
+ "owner": "Administrator",
+ "parent_page": "Accounting",
+ "public": 1,
+ "quick_lists": [],
+ "restrict_to_domain": "",
+ "roles": [],
+ "sequence_id": 3.0,
+ "shortcuts": [
+ {
+ "doc_view": "",
+ "label": "Accounts Payable",
+ "link_to": "Accounts Payable",
+ "type": "Report"
+ },
+ {
+ "doc_view": "",
+ "label": "Purchase Invoice",
+ "link_to": "Purchase Invoice",
+ "type": "DocType"
+ },
+ {
+ "doc_view": "",
+ "label": "Journal Entry",
+ "link_to": "Journal Entry",
+ "type": "DocType"
+ },
+ {
+ "doc_view": "",
+ "label": "Payment Entry",
+ "link_to": "Payment Entry",
+ "type": "DocType"
+ }
+ ],
+ "title": "Payables"
+}
\ No newline at end of file
diff --git a/erpnext/accounts/workspace/receivables/receivables.json b/erpnext/accounts/workspace/receivables/receivables.json
new file mode 100644
index 0000000..6fa8c09
--- /dev/null
+++ b/erpnext/accounts/workspace/receivables/receivables.json
@@ -0,0 +1,254 @@
+{
+ "charts": [],
+ "content": "[{\"id\":\"vikWSkNm6_\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Shortcuts</b></span>\",\"col\":12}},{\"id\":\"G984SgVRJN\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Invoice\",\"col\":3}},{\"id\":\"5yHldR0JNk\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"POS Invoice\",\"col\":3}},{\"id\":\"F9f4I1viNr\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Payment Entry\",\"col\":3}},{\"id\":\"1ArNvt9qhz\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Journal Entry\",\"col\":3}},{\"id\":\"4IBBOIxfqW\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Accounts Receivable\",\"col\":3}},{\"id\":\"ILlIxJuexy\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Cost Center\",\"col\":3}},{\"id\":\"B7-uxs8tkU\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"tHb3yxthkR\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"col\":12}},{\"id\":\"jLgv00c6ek\",\"type\":\"card\",\"data\":{\"card_name\":\"Invoicing\",\"col\":4}},{\"id\":\"npwfXlz0u1\",\"type\":\"card\",\"data\":{\"card_name\":\"Payments\",\"col\":4}},{\"id\":\"am70C27Jrb\",\"type\":\"card\",\"data\":{\"card_name\":\"Dunning\",\"col\":4}},{\"id\":\"xOHTyD8b5l\",\"type\":\"card\",\"data\":{\"card_name\":\"Reports\",\"col\":4}}]",
+ "creation": "2024-01-05 15:29:21.084241",
+ "custom_blocks": [],
+ "docstatus": 0,
+ "doctype": "Workspace",
+ "for_user": "",
+ "hide_custom": 0,
+ "icon": "arrow-right",
+ "idx": 0,
+ "indicator_color": "",
+ "is_hidden": 0,
+ "label": "Receivables",
+ "links": [
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Invoicing",
+ "link_count": 2,
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Card Break"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Sales Invoice",
+ "link_count": 0,
+ "link_to": "Sales Invoice",
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Customer",
+ "link_count": 0,
+ "link_to": "Customer",
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Payments",
+ "link_count": 4,
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Card Break"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Payment Entry",
+ "link_count": 0,
+ "link_to": "Payment Entry",
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Payment Request",
+ "link_count": 0,
+ "link_to": "Payment Request",
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Payment Reconciliation",
+ "link_count": 0,
+ "link_to": "Payment Reconciliation",
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Payment Gateway Account",
+ "link_count": 0,
+ "link_to": "Payment Gateway Account",
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Dunning",
+ "link_count": 2,
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Card Break"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Dunning",
+ "link_count": 0,
+ "link_to": "Dunning",
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Dunning Type",
+ "link_count": 0,
+ "link_to": "Dunning Type",
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "hidden": 0,
+ "is_query_report": 0,
+ "label": "Reports",
+ "link_count": 6,
+ "link_type": "DocType",
+ "onboard": 0,
+ "type": "Card Break"
+ },
+ {
+ "dependencies": "Sales Invoice",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Accounts Receivable",
+ "link_count": 0,
+ "link_to": "Accounts Receivable",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "Sales Invoice",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Accounts Receivable Summary",
+ "link_count": 0,
+ "link_to": "Accounts Receivable Summary",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "Sales Invoice",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Sales Register",
+ "link_count": 0,
+ "link_to": "Sales Register",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "Sales Invoice",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Item-wise Sales Register",
+ "link_count": 0,
+ "link_to": "Item-wise Sales Register",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "Sales Invoice",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Sales Order Analysis",
+ "link_count": 0,
+ "link_to": "Sales Order Analysis",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ },
+ {
+ "dependencies": "Sales Invoice",
+ "hidden": 0,
+ "is_query_report": 1,
+ "label": "Delivered Items To Be Billed",
+ "link_count": 0,
+ "link_to": "Delivered Items To Be Billed",
+ "link_type": "Report",
+ "onboard": 0,
+ "type": "Link"
+ }
+ ],
+ "modified": "2024-01-18 22:11:51.474477",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Receivables",
+ "number_cards": [],
+ "owner": "Administrator",
+ "parent_page": "Accounting",
+ "public": 1,
+ "quick_lists": [],
+ "restrict_to_domain": "",
+ "roles": [],
+ "sequence_id": 4.0,
+ "shortcuts": [
+ {
+ "color": "Grey",
+ "doc_view": "List",
+ "label": "POS Invoice",
+ "link_to": "POS Invoice",
+ "stats_filter": "[]",
+ "type": "DocType"
+ },
+ {
+ "color": "Grey",
+ "doc_view": "List",
+ "label": "Cost Center",
+ "link_to": "Cost Center",
+ "type": "DocType"
+ },
+ {
+ "doc_view": "",
+ "label": "Sales Invoice",
+ "link_to": "Sales Invoice",
+ "stats_filter": "[]",
+ "type": "DocType"
+ },
+ {
+ "doc_view": "",
+ "label": "Journal Entry",
+ "link_to": "Journal Entry",
+ "type": "DocType"
+ },
+ {
+ "doc_view": "",
+ "label": "Payment Entry",
+ "link_to": "Payment Entry",
+ "type": "DocType"
+ },
+ {
+ "doc_view": "",
+ "label": "Accounts Receivable",
+ "link_to": "Accounts Receivable",
+ "type": "Report"
+ }
+ ],
+ "title": "Receivables"
+}
\ No newline at end of file
diff --git a/erpnext/assets/doctype/asset/asset.json b/erpnext/assets/doctype/asset/asset.json
index d0c9350..39a0867 100644
--- a/erpnext/assets/doctype/asset/asset.json
+++ b/erpnext/assets/doctype/asset/asset.json
@@ -202,8 +202,7 @@
"fieldname": "purchase_date",
"fieldtype": "Date",
"label": "Purchase Date",
- "mandatory_depends_on": "eval:!doc.is_existing_asset",
- "read_only": 1,
+ "mandatory_depends_on": "eval:!doc.is_existing_asset && !doc.is_composite_asset",
"read_only_depends_on": "eval:!doc.is_existing_asset && !doc.is_composite_asset"
},
{
@@ -590,7 +589,7 @@
"link_fieldname": "target_asset"
}
],
- "modified": "2024-01-05 17:36:53.131512",
+ "modified": "2024-01-15 17:35:49.226603",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset",
diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py
index 5f44898..7357249 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -162,6 +162,7 @@
def on_cancel(self):
self.validate_cancellation()
self.cancel_movement_entries()
+ self.cancel_capitalization()
self.delete_depreciation_entries()
cancel_asset_depr_schedules(self)
self.set_status()
@@ -517,6 +518,16 @@
movement = frappe.get_doc("Asset Movement", movement.get("name"))
movement.cancel()
+ def cancel_capitalization(self):
+ asset_capitalization = frappe.db.get_value(
+ "Asset Capitalization",
+ {"target_asset": self.name, "docstatus": 1, "entry_type": "Capitalization"},
+ )
+
+ if asset_capitalization:
+ asset_capitalization = frappe.get_doc("Asset Capitalization", asset_capitalization)
+ asset_capitalization.cancel()
+
def delete_depreciation_entries(self):
if self.calculate_depreciation:
for row in self.get("finance_books"):
@@ -697,9 +708,7 @@
self.get_gl_dict(
{
"account": cwip_account,
- "against_type": "Account",
"against": fixed_asset_account,
- "against_link": fixed_asset_account,
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
"posting_date": self.available_for_use_date,
"credit": self.purchase_receipt_amount,
@@ -714,9 +723,7 @@
self.get_gl_dict(
{
"account": fixed_asset_account,
- "against_type": "Account",
"against": cwip_account,
- "against_link": cwip_account,
"remarks": self.get("remarks") or _("Accounting Entry for Asset"),
"posting_date": self.available_for_use_date,
"debit": self.purchase_receipt_amount,
@@ -1031,6 +1038,8 @@
@frappe.whitelist()
def get_asset_value_after_depreciation(asset_name, finance_book=None):
asset = frappe.get_doc("Asset", asset_name)
+ if not asset.calculate_depreciation:
+ return flt(asset.value_after_depreciation)
return asset.get_value_after_depreciation(finance_book)
diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py
index 0773698..25d0105 100644
--- a/erpnext/assets/doctype/asset/test_asset.py
+++ b/erpnext/assets/doctype/asset/test_asset.py
@@ -251,16 +251,7 @@
flt(18000.0 + pro_rata_amount, asset.precision("gross_purchase_amount")),
0.0,
),
- (
- "_Test Fixed Asset - _TC",
- 0.0,
- flt(18000.0 + pro_rata_amount, asset.precision("gross_purchase_amount")),
- ),
- (
- "_Test Fixed Asset - _TC",
- 0.0,
- flt(82000.0 - pro_rata_amount, asset.precision("gross_purchase_amount")),
- ),
+ ("_Test Fixed Asset - _TC", 0.0, 100000.0),
(
"_Test Gain/Loss on Asset Disposal - _TC",
flt(82000.0 - pro_rata_amount, asset.precision("gross_purchase_amount")),
@@ -900,7 +891,7 @@
["2030-12-31", 28630.14, 28630.14],
["2031-12-31", 35684.93, 64315.07],
["2032-12-31", 17842.46, 82157.53],
- ["2033-06-06", 5342.47, 87500.0],
+ ["2033-06-06", 5342.46, 87499.99],
]
schedules = [
@@ -1012,7 +1003,7 @@
asset_depr_schedule_doc = get_asset_depr_schedule_doc(asset.name, "Active")
depreciation_amount = get_depreciation_amount(
- asset_depr_schedule_doc, asset, 100000, asset.finance_books[0]
+ asset_depr_schedule_doc, asset, 100000, 100000, asset.finance_books[0]
)
self.assertEqual(depreciation_amount, 30000)
diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js
index be78d9e..2f0de97 100644
--- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js
+++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.js
@@ -21,10 +21,10 @@
this.show_stock_ledger();
}
- if (this.frm.doc.stock_items && !this.frm.doc.stock_items.length && this.frm.doc.target_asset && this.frm.doc.capitalization_method === "Choose a WIP composite asset") {
- this.set_consumed_stock_items_tagged_to_wip_composite_asset(this.frm.doc.target_asset);
- this.get_target_asset_details();
- }
+ // if (this.frm.doc.stock_items && !this.frm.doc.stock_items.length && this.frm.doc.target_asset && this.frm.doc.capitalization_method === "Choose a WIP composite asset") {
+ // this.set_consumed_stock_items_tagged_to_wip_composite_asset(this.frm.doc.target_asset);
+ // this.get_target_asset_details();
+ // }
}
setup_queries() {
@@ -143,13 +143,20 @@
},
callback: function (r) {
if (!r.exc && r.message) {
- me.frm.clear_table("stock_items");
-
- for (let item of r.message) {
- me.frm.add_child("stock_items", item);
+ if(r.message[0] && r.message[0].length) {
+ me.frm.clear_table("stock_items");
+ for (let item of r.message[0]) {
+ me.frm.add_child("stock_items", item);
+ }
+ refresh_field("stock_items");
}
-
- refresh_field("stock_items");
+ if (r.message[1] && r.message[1].length) {
+ me.frm.clear_table("asset_items");
+ for (let item of r.message[1]) {
+ me.frm.add_child("asset_items", item);
+ }
+ me.frm.refresh_field("asset_items");
+ }
me.calculate_totals();
}
diff --git a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
index de75841..cad74df 100644
--- a/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
+++ b/erpnext/assets/doctype/asset_capitalization/asset_capitalization.py
@@ -136,11 +136,19 @@
"Stock Ledger Entry",
"Repost Item Valuation",
"Serial and Batch Bundle",
+ "Asset",
)
+ self.cancel_target_asset()
self.update_stock_ledger()
self.make_gl_entries()
self.restore_consumed_asset_items()
+ def cancel_target_asset(self):
+ if self.entry_type == "Capitalization" and self.target_asset:
+ asset_doc = frappe.get_doc("Asset", self.target_asset)
+ if asset_doc.docstatus == 1:
+ asset_doc.cancel()
+
def set_title(self):
self.title = self.target_asset_name or self.target_item_name or self.target_item_code
@@ -485,9 +493,7 @@
self.get_gl_dict(
{
"account": account,
- "against_type": "Account",
"against": target_account,
- "against_link": target_account,
"cost_center": item_row.cost_center,
"project": item_row.get("project") or self.get("project"),
"remarks": self.get("remarks") or "Accounting Entry for Stock",
@@ -528,9 +534,7 @@
self.set_consumed_asset_status(asset)
for gle in fixed_asset_gl_entries:
- gle["against_type"] = "Account"
gle["against"] = target_account
- gle["against_link"] = target_account
gl_entries.append(self.get_gl_dict(gle, item=item))
target_against.add(gle["account"])
@@ -546,9 +550,7 @@
self.get_gl_dict(
{
"account": item_row.expense_account,
- "against_type": "Account",
"against": target_account,
- "against_link": target_account,
"cost_center": item_row.cost_center,
"project": item_row.get("project") or self.get("project"),
"remarks": self.get("remarks") or "Accounting Entry for Stock",
@@ -559,46 +561,41 @@
)
def get_gl_entries_for_target_item(self, gl_entries, target_against, precision):
- for target_account in target_against:
- if self.target_is_fixed_asset:
- # Capitalization
+ if self.target_is_fixed_asset:
+ # Capitalization
+ gl_entries.append(
+ self.get_gl_dict(
+ {
+ "account": self.target_fixed_asset_account,
+ "against": ", ".join(target_against),
+ "remarks": self.get("remarks") or _("Accounting Entry for Asset"),
+ "debit": flt(self.total_value, precision),
+ "cost_center": self.get("cost_center"),
+ },
+ item=self,
+ )
+ )
+ else:
+ # Target Stock Item
+ sle_list = self.sle_map.get(self.name)
+ for sle in sle_list:
+ stock_value_difference = flt(sle.stock_value_difference, precision)
+ account = self.warehouse_account[sle.warehouse]["account"]
+
gl_entries.append(
self.get_gl_dict(
{
- "account": self.target_fixed_asset_account,
- "against_type": "Account",
- "against": target_account,
- "against_link": target_account,
- "remarks": self.get("remarks") or _("Accounting Entry for Asset"),
- "debit": flt(self.total_value, precision) / len(target_against),
- "cost_center": self.get("cost_center"),
+ "account": account,
+ "against": ", ".join(target_against),
+ "cost_center": self.cost_center,
+ "project": self.get("project"),
+ "remarks": self.get("remarks") or "Accounting Entry for Stock",
+ "debit": stock_value_difference,
},
+ self.warehouse_account[sle.warehouse]["account_currency"],
item=self,
)
)
- else:
- # Target Stock Item
- sle_list = self.sle_map.get(self.name)
- for sle in sle_list:
- stock_value_difference = flt(sle.stock_value_difference, precision)
- account = self.warehouse_account[sle.warehouse]["account"]
-
- gl_entries.append(
- self.get_gl_dict(
- {
- "account": account,
- "against_type": "Account",
- "against": target_account,
- "against_link": target_account,
- "cost_center": self.cost_center,
- "project": self.get("project"),
- "remarks": self.get("remarks") or "Accounting Entry for Stock",
- "debit": stock_value_difference / len(target_against),
- },
- self.warehouse_account[sle.warehouse]["account_currency"],
- item=self,
- )
- )
def create_target_asset(self):
if (
@@ -892,7 +889,6 @@
out.cost_center = get_default_cost_center(
args, item_defaults, item_group_defaults, brand_defaults
)
-
return out
@@ -940,10 +936,27 @@
"qty",
"valuation_rate",
"amount",
+ "is_fixed_asset",
+ "parent",
]
pr_items = frappe.get_all(
- "Purchase Receipt Item", filters={"wip_composite_asset": asset}, fields=fields
+ "Purchase Receipt Item", filters={"wip_composite_asset": asset, "docstatus": 1}, fields=fields
)
- return pr_items
+ stock_items = []
+ asset_items = []
+ for d in pr_items:
+ if not d.is_fixed_asset:
+ stock_items.append(frappe._dict(d))
+ else:
+ asset_details = frappe.db.get_value(
+ "Asset",
+ {"item_code": d.item_code, "purchase_receipt": d.parent},
+ ["name as asset", "asset_name"],
+ as_dict=1,
+ )
+ d.update(asset_details)
+ asset_items.append(frappe._dict(d))
+
+ return stock_items, asset_items
diff --git a/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py b/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py
index 7a7a10d..ac7c90d 100644
--- a/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py
+++ b/erpnext/assets/doctype/asset_capitalization/test_asset_capitalization.py
@@ -98,12 +98,12 @@
# Test General Ledger Entries
expected_gle = {
- "_Test Fixed Asset - TCP1": 2999.99,
+ "_Test Fixed Asset - TCP1": 3000,
"Expenses Included In Asset Valuation - TCP1": -1000,
"_Test Warehouse - TCP1": -2000,
- "Round Off - TCP1": 0.01,
}
actual_gle = get_actual_gle_dict(asset_capitalization.name)
+
self.assertEqual(actual_gle, expected_gle)
# Test Stock Ledger Entries
@@ -189,10 +189,9 @@
# Test General Ledger Entries
default_expense_account = frappe.db.get_value("Company", company, "default_expense_account")
expected_gle = {
- "_Test Fixed Asset - _TC": 2999.99,
+ "_Test Fixed Asset - _TC": 3000,
"Expenses Included In Asset Valuation - _TC": -1000,
default_expense_account: -2000,
- "Round Off - _TC": 0.01,
}
actual_gle = get_actual_gle_dict(asset_capitalization.name)
@@ -377,10 +376,9 @@
# Test General Ledger Entries
expected_gle = {
+ "_Test Warehouse - TCP1": consumed_asset_value_before_disposal,
"_Test Accumulated Depreciations - TCP1": accumulated_depreciation,
"_Test Fixed Asset - TCP1": -consumed_asset_purchase_value,
- "_Test Warehouse - TCP1": consumed_asset_value_before_disposal - 0.01,
- "Round Off - TCP1": 0.01,
}
actual_gle = get_actual_gle_dict(asset_capitalization.name)
self.assertEqual(actual_gle, expected_gle)
diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py
index 74d2aa7..146c03e 100644
--- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py
+++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py
@@ -7,6 +7,7 @@
from frappe.utils import (
add_days,
add_months,
+ add_years,
cint,
date_diff,
flt,
@@ -18,6 +19,7 @@
)
import erpnext
+from erpnext.accounts.utils import get_fiscal_year
class AssetDepreciationSchedule(Document):
@@ -283,12 +285,20 @@
depreciation_amount = 0
number_of_pending_depreciations = final_number_of_depreciations - start
-
+ yearly_opening_wdv = value_after_depreciation
+ current_fiscal_year_end_date = None
for n in range(start, final_number_of_depreciations):
# If depreciation is already completed (for double declining balance)
if skip_row:
continue
+ schedule_date = add_months(row.depreciation_start_date, n * cint(row.frequency_of_depreciation))
+ if not current_fiscal_year_end_date:
+ current_fiscal_year_end_date = get_fiscal_year(row.depreciation_start_date)[2]
+ elif getdate(schedule_date) > getdate(current_fiscal_year_end_date):
+ current_fiscal_year_end_date = add_years(current_fiscal_year_end_date, 1)
+ yearly_opening_wdv = value_after_depreciation
+
if n > 0 and len(self.get("depreciation_schedule")) > n - 1:
prev_depreciation_amount = self.get("depreciation_schedule")[n - 1].depreciation_amount
else:
@@ -298,6 +308,7 @@
self,
asset_doc,
value_after_depreciation,
+ yearly_opening_wdv,
row,
n,
prev_depreciation_amount,
@@ -401,8 +412,9 @@
if not depreciation_amount:
continue
- value_after_depreciation -= flt(
- depreciation_amount, asset_doc.precision("gross_purchase_amount")
+ value_after_depreciation = flt(
+ value_after_depreciation - flt(depreciation_amount),
+ asset_doc.precision("gross_purchase_amount"),
)
# Adjust depreciation amount in the last period based on the expected value after useful life
@@ -582,6 +594,7 @@
asset_depr_schedule,
asset,
depreciable_value,
+ yearly_opening_wdv,
fb_row,
schedule_idx=0,
prev_depreciation_amount=0,
@@ -597,6 +610,7 @@
asset,
fb_row,
depreciable_value,
+ yearly_opening_wdv,
schedule_idx,
prev_depreciation_amount,
has_wdv_or_dd_non_yearly_pro_rata,
@@ -744,6 +758,7 @@
asset,
fb_row,
depreciable_value,
+ yearly_opening_wdv,
schedule_idx,
prev_depreciation_amount,
has_wdv_or_dd_non_yearly_pro_rata,
diff --git a/erpnext/assets/doctype/asset_repair/asset_repair.py b/erpnext/assets/doctype/asset_repair/asset_repair.py
index bb627d4..10d36e6 100644
--- a/erpnext/assets/doctype/asset_repair/asset_repair.py
+++ b/erpnext/assets/doctype/asset_repair/asset_repair.py
@@ -285,9 +285,7 @@
"account": fixed_asset_account,
"debit": self.repair_cost,
"debit_in_account_currency": self.repair_cost,
- "against_type": "Account",
"against": pi_expense_account,
- "against_link": pi_expense_account,
"voucher_type": self.doctype,
"voucher_no": self.name,
"cost_center": self.cost_center,
@@ -306,9 +304,7 @@
"account": pi_expense_account,
"credit": self.repair_cost,
"credit_in_account_currency": self.repair_cost,
- "against_type": "Account",
"against": fixed_asset_account,
- "against_link": fixed_asset_account,
"voucher_type": self.doctype,
"voucher_no": self.name,
"cost_center": self.cost_center,
@@ -342,9 +338,7 @@
"account": item.expense_account or default_expense_account,
"credit": item.amount,
"credit_in_account_currency": item.amount,
- "against_type": "Account",
"against": fixed_asset_account,
- "against_link": fixed_asset_account,
"voucher_type": self.doctype,
"voucher_no": self.name,
"cost_center": self.cost_center,
@@ -361,9 +355,7 @@
"account": fixed_asset_account,
"debit": item.amount,
"debit_in_account_currency": item.amount,
- "against_type": "Account",
"against": item.expense_account or default_expense_account,
- "against_link": item.expense_account or default_expense_account,
"voucher_type": self.doctype,
"voucher_no": self.name,
"cost_center": self.cost_center,
diff --git a/erpnext/assets/workspace/assets/assets.json b/erpnext/assets/workspace/assets/assets.json
index c6b321e..26e8d8a 100644
--- a/erpnext/assets/workspace/assets/assets.json
+++ b/erpnext/assets/workspace/assets/assets.json
@@ -196,18 +196,18 @@
"type": "Link"
}
],
- "modified": "2023-05-24 14:47:20.243146",
+ "modified": "2024-01-05 17:40:34.570041",
"modified_by": "Administrator",
"module": "Assets",
"name": "Assets",
"number_cards": [],
"owner": "Administrator",
- "parent_page": "Accounting",
+ "parent_page": "",
"public": 1,
"quick_lists": [],
"restrict_to_domain": "",
"roles": [],
- "sequence_id": 4.0,
+ "sequence_id": 7.0,
"shortcuts": [
{
"label": "Asset",
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.json b/erpnext/buying/doctype/purchase_order/purchase_order.json
index f74df66..9da49a7 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.json
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.json
@@ -134,6 +134,7 @@
"more_info_tab",
"tracking_section",
"status",
+ "advance_payment_status",
"column_break_75",
"per_billed",
"per_received",
@@ -1269,13 +1270,25 @@
"fieldtype": "Tab Break",
"label": "Connections",
"show_dashboard": 1
+ },
+ {
+ "fieldname": "advance_payment_status",
+ "fieldtype": "Select",
+ "hidden": 1,
+ "in_standard_filter": 1,
+ "label": "Advance Payment Status",
+ "no_copy": 1,
+ "oldfieldname": "status",
+ "oldfieldtype": "Select",
+ "options": "Not Initiated\nInitiated\nPartially Paid\nFully Paid",
+ "print_hide": 1
}
],
"icon": "fa fa-file-text",
"idx": 105,
"is_submittable": 1,
"links": [],
- "modified": "2023-10-01 20:58:07.851037",
+ "modified": "2023-10-10 13:37:40.158761",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order",
@@ -1330,4 +1343,4 @@
"timeline_field": "supplier",
"title_field": "supplier_name",
"track_changes": 1
-}
\ No newline at end of file
+}
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order.py b/erpnext/buying/doctype/purchase_order/purchase_order.py
index b830e7d..4efbb27 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/purchase_order.py
@@ -215,6 +215,10 @@
self.validate_fg_item_for_subcontracting()
self.set_received_qty_for_drop_ship_items()
+
+ if not self.advance_payment_status:
+ self.advance_payment_status = "Not Initiated"
+
validate_inter_company_party(
self.doctype, self.supplier, self.company, self.inter_company_order_reference
)
diff --git a/erpnext/buying/doctype/purchase_order/purchase_order_list.js b/erpnext/buying/doctype/purchase_order/purchase_order_list.js
index 6594746..d39d7f9 100644
--- a/erpnext/buying/doctype/purchase_order/purchase_order_list.js
+++ b/erpnext/buying/doctype/purchase_order/purchase_order_list.js
@@ -1,6 +1,6 @@
frappe.listview_settings['Purchase Order'] = {
add_fields: ["base_grand_total", "company", "currency", "supplier",
- "supplier_name", "per_received", "per_billed", "status"],
+ "supplier_name", "per_received", "per_billed", "status", "advance_payment_status"],
get_indicator: function (doc) {
if (doc.status === "Closed") {
return [__("Closed"), "green", "status,=,Closed"];
@@ -8,6 +8,8 @@
return [__("On Hold"), "orange", "status,=,On Hold"];
} else if (doc.status === "Delivered") {
return [__("Delivered"), "green", "status,=,Closed"];
+ } else if (doc.advance_payment_status == "Initiated") {
+ return [__("To Pay"), "gray", "advance_payment_status,=,Initiated"];
} else if (flt(doc.per_received, 2) < 100 && doc.status !== "Closed") {
if (flt(doc.per_billed, 2) < 100) {
return [__("To Receive and Bill"), "orange",
diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
index 9b382bb..5405799 100644
--- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
@@ -1021,6 +1021,33 @@
self.assertTrue(frappe.db.get_value("Subcontracting Order", {"purchase_order": po.name}))
+ def test_purchase_order_advance_payment_status(self):
+ from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
+ from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request
+
+ po = create_purchase_order()
+ self.assertEqual(
+ frappe.db.get_value(po.doctype, po.name, "advance_payment_status"), "Not Initiated"
+ )
+
+ pr = make_payment_request(dt=po.doctype, dn=po.name, submit_doc=True, return_doc=True)
+ self.assertEqual(frappe.db.get_value(po.doctype, po.name, "advance_payment_status"), "Initiated")
+
+ pe = get_payment_entry(po.doctype, po.name).save().submit()
+ self.assertEqual(
+ frappe.db.get_value(po.doctype, po.name, "advance_payment_status"), "Fully Paid"
+ )
+
+ pe.reload()
+ pe.cancel()
+ self.assertEqual(frappe.db.get_value(po.doctype, po.name, "advance_payment_status"), "Initiated")
+
+ pr.reload()
+ pr.cancel()
+ self.assertEqual(
+ frappe.db.get_value(po.doctype, po.name, "advance_payment_status"), "Not Initiated"
+ )
+
def prepare_data_for_internal_transfer():
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_internal_supplier
diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js
index 91506c0..3bf4f2b 100644
--- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js
+++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js
@@ -54,7 +54,7 @@
"fieldtype": "MultiSelectList",
"width": "80",
get_data: function(txt) {
- let status = ["To Bill", "To Receive", "To Receive and Bill", "Completed"]
+ let status = ["To Pay", "To Bill", "To Receive", "To Receive and Bill", "Completed"]
let options = []
for (let option of status){
options.push({
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index d9aa7e8..afbea61 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -1163,7 +1163,6 @@
)
credit_or_debit = "credit" if self.doctype == "Purchase Invoice" else "debit"
- against_type = "Supplier" if self.doctype == "Purchase Invoice" else "Customer"
against = self.supplier if self.doctype == "Purchase Invoice" else self.customer
if precision_loss:
@@ -1171,9 +1170,7 @@
self.get_gl_dict(
{
"account": round_off_account,
- "against_type": against_type,
"against": against,
- "against_link": against,
credit_or_debit: precision_loss,
"cost_center": round_off_cost_center
if self.use_company_roundoff_cost_center
@@ -1537,13 +1534,11 @@
if self.doctype == "Purchase Invoice":
dr_or_cr = "credit"
rev_dr_cr = "debit"
- against_type = "Supplier"
supplier_or_customer = self.supplier
else:
dr_or_cr = "debit"
rev_dr_cr = "credit"
- against_type = "Customer"
supplier_or_customer = self.customer
if enable_discount_accounting:
@@ -1568,9 +1563,7 @@
self.get_gl_dict(
{
"account": item.discount_account,
- "against_type": against_type,
"against": supplier_or_customer,
- "against_link": supplier_or_customer,
dr_or_cr: flt(
discount_amount * self.get("conversion_rate"), item.precision("discount_amount")
),
@@ -1588,9 +1581,7 @@
self.get_gl_dict(
{
"account": income_or_expense_account,
- "against_type": against_type,
"against": supplier_or_customer,
- "against_link": supplier_or_customer,
rev_dr_cr: flt(
discount_amount * self.get("conversion_rate"), item.precision("discount_amount")
),
@@ -1613,9 +1604,7 @@
self.get_gl_dict(
{
"account": self.additional_discount_account,
- "against_type": against_type,
"against": supplier_or_customer,
- "against_link": supplier_or_customer,
dr_or_cr: self.base_discount_amount,
"cost_center": self.cost_center or erpnext.get_default_cost_center(self.company),
},
@@ -1772,7 +1761,10 @@
def set_total_advance_paid(self):
ple = frappe.qb.DocType("Payment Ledger Entry")
- party = self.customer if self.doctype == "Sales Order" else self.supplier
+ if self.doctype in frappe.get_hooks("advance_payment_customer_doctypes"):
+ party = self.customer
+ if self.doctype in frappe.get_hooks("advance_payment_supplier_doctypes"):
+ party = self.supplier
advance = (
frappe.qb.from_(ple)
.select(ple.account_currency, Abs(Sum(ple.amount_in_account_currency)).as_("amount"))
@@ -1786,6 +1778,8 @@
.run(as_dict=True)
)
+ advance_paid, order_total = None, None
+
if advance:
advance = advance[0]
@@ -1814,7 +1808,38 @@
).format(formatted_advance_paid, self.name, formatted_order_total)
)
- frappe.db.set_value(self.doctype, self.name, "advance_paid", advance_paid)
+ self.db_set("advance_paid", advance_paid)
+
+ self.set_advance_payment_status(advance_paid, order_total)
+
+ def set_advance_payment_status(
+ self, advance_paid: float | None = None, order_total: float | None = None
+ ):
+ new_status = None
+ # if money is paid set the paid states
+ if advance_paid:
+ new_status = "Partially Paid" if advance_paid < order_total else "Fully Paid"
+
+ if not new_status:
+ prs = frappe.db.count(
+ "Payment Request",
+ {
+ "reference_doctype": self.doctype,
+ "reference_name": self.name,
+ "docstatus": 1,
+ },
+ )
+ if self.doctype in frappe.get_hooks("advance_payment_customer_doctypes"):
+ new_status = "Requested" if prs else "Not Requested"
+ if self.doctype in frappe.get_hooks("advance_payment_supplier_doctypes"):
+ new_status = "Initiated" if prs else "Not Initiated"
+
+ if new_status == self.advance_payment_status:
+ return
+
+ self.db_set("advance_payment_status", new_status)
+ self.set_status(update=True)
+ self.notify_update()
@property
def company_abbr(self):
diff --git a/erpnext/controllers/item_variant.py b/erpnext/controllers/item_variant.py
index ea7fb23..1eee9ea 100644
--- a/erpnext/controllers/item_variant.py
+++ b/erpnext/controllers/item_variant.py
@@ -56,7 +56,11 @@
copy_attributes_to_variant(template, variant)
- variant.item_code = append_number_if_name_exists("Item", template.name)
+ variant_name = f"{template.name} - {manufacturer}"
+ if manufacturer_part_no:
+ variant_name += f" - {manufacturer_part_no}"
+
+ variant.item_code = append_number_if_name_exists("Item", variant_name)
variant.flags.ignore_mandatory = True
variant.save()
diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py
index 8ebdcc5..e234eec 100644
--- a/erpnext/controllers/queries.py
+++ b/erpnext/controllers/queries.py
@@ -6,10 +6,12 @@
from collections import OrderedDict, defaultdict
import frappe
-from frappe import scrub
+from frappe import qb, scrub
from frappe.desk.reportview import get_filters_cond, get_match_cond
-from frappe.query_builder.functions import Concat, Sum
+from frappe.query_builder import Criterion, CustomFunction
+from frappe.query_builder.functions import Concat, Locate, Sum
from frappe.utils import nowdate, today, unique
+from pypika import Order
import erpnext
from erpnext.stock.get_item_details import _get_item_tax_template
@@ -344,37 +346,46 @@
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_project_name(doctype, txt, searchfield, start, page_len, filters):
- doctype = "Project"
- cond = ""
+ proj = qb.DocType("Project")
+ qb_filter_and_conditions = []
+ qb_filter_or_conditions = []
+ ifelse = CustomFunction("IF", ["condition", "then", "else"])
+
if filters and filters.get("customer"):
- cond = """(`tabProject`.customer = %s or
- ifnull(`tabProject`.customer,"")="") and""" % (
- frappe.db.escape(filters.get("customer"))
- )
+ qb_filter_and_conditions.append(proj.customer == filters.get("customer"))
+
+ qb_filter_and_conditions.append(proj.status.notin(["Completed", "Cancelled"]))
+
+ q = qb.from_(proj)
fields = get_fields(doctype, ["name", "project_name"])
- searchfields = frappe.get_meta(doctype).get_search_fields()
- searchfields = " or ".join(["`tabProject`." + field + " like %(txt)s" for field in searchfields])
+ for x in fields:
+ q = q.select(proj[x])
- return frappe.db.sql(
- """select {fields} from `tabProject`
- where
- `tabProject`.status not in ('Completed', 'Cancelled')
- and {cond} {scond} {match_cond}
- order by
- (case when locate(%(_txt)s, `tabProject`.name) > 0 then locate(%(_txt)s, `tabProject`.name) else 99999 end),
- `tabProject`.idx desc,
- `tabProject`.name asc
- limit {page_len} offset {start}""".format(
- fields=", ".join(["`tabProject`.{0}".format(f) for f in fields]),
- cond=cond,
- scond=searchfields,
- match_cond=get_match_cond(doctype),
- start=start,
- page_len=page_len,
- ),
- {"txt": "%{0}%".format(txt), "_txt": txt.replace("%", "")},
- )
+ # don't consider 'customer' and 'status' fields for pattern search, as they must be exactly matched
+ searchfields = [
+ x for x in frappe.get_meta(doctype).get_search_fields() if x not in ["customer", "status"]
+ ]
+
+ # pattern search
+ if txt:
+ for x in searchfields:
+ qb_filter_or_conditions.append(proj[x].like(f"%{txt}%"))
+
+ q = q.where(Criterion.all(qb_filter_and_conditions)).where(Criterion.any(qb_filter_or_conditions))
+
+ # ordering
+ if txt:
+ # project_name containing search string 'txt' will be given higher precedence
+ q = q.orderby(ifelse(Locate(txt, proj.project_name) > 0, Locate(txt, proj.project_name), 99999))
+ q = q.orderby(proj.idx, order=Order.desc).orderby(proj.name)
+
+ if page_len:
+ q = q.limit(page_len)
+
+ if start:
+ q = q.offset(start)
+ return q.run()
@frappe.whitelist()
diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py
index 297f8c2..ac8c88f 100644
--- a/erpnext/controllers/status_updater.py
+++ b/erpnext/controllers/status_updater.py
@@ -54,6 +54,10 @@
"eval:self.per_delivered < 100 and self.per_billed >= 100 and self.docstatus == 1 and not self.skip_delivery_note",
],
[
+ "To Pay",
+ "eval:self.advance_payment_status == 'Requested' and self.docstatus == 1",
+ ],
+ [
"Completed",
"eval:(self.per_delivered >= 100 or self.skip_delivery_note) and self.per_billed >= 100 and self.docstatus == 1",
],
@@ -63,16 +67,20 @@
],
"Purchase Order": [
["Draft", None],
- [
- "To Receive and Bill",
- "eval:self.per_received < 100 and self.per_billed < 100 and self.docstatus == 1",
- ],
["To Bill", "eval:self.per_received >= 100 and self.per_billed < 100 and self.docstatus == 1"],
[
"To Receive",
"eval:self.per_received < 100 and self.per_billed == 100 and self.docstatus == 1",
],
[
+ "To Receive and Bill",
+ "eval:self.per_received < 100 and self.per_billed < 100 and self.docstatus == 1",
+ ],
+ [
+ "To Pay",
+ "eval:self.advance_payment_status == 'Initiated' and self.docstatus == 1",
+ ],
+ [
"Completed",
"eval:self.per_received >= 100 and self.per_billed == 100 and self.docstatus == 1",
],
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index de86846..7c63518 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -162,9 +162,7 @@
self.get_gl_dict(
{
"account": warehouse_account[sle.warehouse]["account"],
- "against_type": "Account",
"against": expense_account,
- "against_link": expense_account,
"cost_center": item_row.cost_center,
"project": item_row.project or self.get("project"),
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
@@ -180,9 +178,7 @@
self.get_gl_dict(
{
"account": expense_account,
- "against_type": "Account",
"against": warehouse_account[sle.warehouse]["account"],
- "against_link": warehouse_account[sle.warehouse]["account"],
"cost_center": item_row.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"debit": -1 * flt(sle.stock_value_difference, precision),
@@ -214,9 +210,7 @@
self.get_gl_dict(
{
"account": expense_account,
- "against_type": "Account",
"against": warehouse_asset_account,
- "against_link": warehouse_asset_account,
"cost_center": item_row.cost_center,
"project": item_row.project or self.get("project"),
"remarks": _("Rounding gain/loss Entry for Stock Transfer"),
@@ -232,9 +226,7 @@
self.get_gl_dict(
{
"account": warehouse_asset_account,
- "against_type": "Account",
"against": expense_account,
- "against_link": expense_account,
"cost_center": item_row.cost_center,
"remarks": _("Rounding gain/loss Entry for Stock Transfer"),
"credit": sle_rounding_diff,
@@ -836,7 +828,6 @@
credit,
remarks,
against_account,
- against_type="Account",
debit_in_account_currency=None,
credit_in_account_currency=None,
account_currency=None,
@@ -851,9 +842,7 @@
"cost_center": cost_center,
"debit": debit,
"credit": credit,
- "against_type": against_type,
"against": against_account,
- "against_link": against_account,
"remarks": remarks,
}
diff --git a/erpnext/controllers/tests/test_queries.py b/erpnext/controllers/tests/test_queries.py
index 60d1733..3a3bc1c 100644
--- a/erpnext/controllers/tests/test_queries.py
+++ b/erpnext/controllers/tests/test_queries.py
@@ -68,7 +68,7 @@
self.assertGreaterEqual(len(query(txt="_Test Item Home Desktop Manufactured")), 1)
def test_project_query(self):
- query = add_default_params(queries.get_project_name, "BOM")
+ query = add_default_params(queries.get_project_name, "Project")
self.assertGreaterEqual(len(query(txt="_Test Project")), 1)
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index cd588c8..f33fff0 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -481,7 +481,8 @@
communication_doctypes = ["Customer", "Supplier"]
-advance_payment_doctypes = ["Sales Order", "Purchase Order"]
+advance_payment_customer_doctypes = ["Sales Order"]
+advance_payment_supplier_doctypes = ["Purchase Order"]
invoice_doctypes = ["Sales Invoice", "Purchase Invoice"]
diff --git a/erpnext/manufacturing/doctype/bom_creator/bom_creator.js b/erpnext/manufacturing/doctype/bom_creator/bom_creator.js
index 243e52d..3bd2021 100644
--- a/erpnext/manufacturing/doctype/bom_creator/bom_creator.js
+++ b/erpnext/manufacturing/doctype/bom_creator/bom_creator.js
@@ -101,6 +101,7 @@
}
})
+ dialog.fields_dict.item_code.get_query = "erpnext.controllers.queries.item_query";
dialog.show();
},
@@ -113,6 +114,16 @@
}
}
});
+ frm.set_query("item_code", "items", function() {
+ return {
+ query: "erpnext.controllers.queries.item_query",
+ }
+ });
+ frm.set_query("fg_item", "items", function() {
+ return {
+ query: "erpnext.controllers.queries.item_query",
+ }
+ });
},
refresh(frm) {
@@ -211,4 +222,4 @@
}
};
-extend_cscript(cur_frm.cscript, new erpnext.bom.BomConfigurator({frm: cur_frm}));
\ No newline at end of file
+extend_cscript(cur_frm.cscript, new erpnext.bom.BomConfigurator({frm: cur_frm}));
diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py
index aa5db57..f6e9a07 100644
--- a/erpnext/manufacturing/doctype/work_order/test_work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py
@@ -998,12 +998,6 @@
make_job_card(wo_order.name, operations)
job_card = frappe.db.get_value("Job Card", {"work_order": wo_order.name, "docstatus": 0}, "name")
- update_job_card(job_card, 10, 2)
-
- stock_entry = frappe.get_doc(make_stock_entry(wo_order.name, "Manufacture", 10))
- for row in stock_entry.items:
- if row.is_scrap_item:
- self.assertEqual(row.qty, 2)
def test_close_work_order(self):
items = [
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 1110617..4ead7e7 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -352,6 +352,7 @@
execute:frappe.db.set_single_value("Buying Settings", "project_update_frequency", "Each Transaction")
execute:frappe.db.set_default("date_format", frappe.db.get_single_value("System Settings", "date_format"))
erpnext.patches.v14_0.update_total_asset_cost_field
+erpnext.patches.v15_0.create_advance_payment_status
# below migration patch should always run last
erpnext.patches.v14_0.migrate_gl_to_payment_ledger
erpnext.stock.doctype.delivery_note.patches.drop_unused_return_against_index # 2023-12-20
diff --git a/erpnext/patches/v15_0/create_advance_payment_status.py b/erpnext/patches/v15_0/create_advance_payment_status.py
new file mode 100644
index 0000000..18ab9fa
--- /dev/null
+++ b/erpnext/patches/v15_0/create_advance_payment_status.py
@@ -0,0 +1,54 @@
+import frappe
+
+
+def execute():
+ """
+ Description:
+ Calculate the new Advance Payment Statuse column in SO & PO
+ """
+
+ if frappe.reload_doc("selling", "doctype", "Sales Order"):
+ so = frappe.qb.DocType("Sales Order")
+ frappe.qb.update(so).set(so.advance_payment_status, "Not Requested").where(
+ so.docstatus == 1
+ ).where(so.advance_paid == 0.0).run()
+
+ frappe.qb.update(so).set(so.advance_payment_status, "Partially Paid").where(
+ so.docstatus == 1
+ ).where(so.advance_payment_status.isnull()).where(
+ so.advance_paid < (so.rounded_total or so.grand_total)
+ ).run()
+
+ frappe.qb.update(so).set(so.advance_payment_status, "Fully Paid").where(so.docstatus == 1).where(
+ so.advance_payment_status.isnull()
+ ).where(so.advance_paid == (so.rounded_total or so.grand_total)).run()
+
+ pr = frappe.qb.DocType("Payment Request")
+ frappe.qb.update(so).join(pr).on(so.name == pr.reference_name).set(
+ so.advance_payment_status, "Requested"
+ ).where(so.docstatus == 1).where(pr.docstatus == 1).where(
+ so.advance_payment_status == "Not Requested"
+ ).run()
+
+ if frappe.reload_doc("buying", "doctype", "Purchase Order"):
+ po = frappe.qb.DocType("Purchase Order")
+ frappe.qb.update(po).set(po.advance_payment_status, "Not Initiated").where(
+ po.docstatus == 1
+ ).where(po.advance_paid == 0.0).run()
+
+ frappe.qb.update(po).set(po.advance_payment_status, "Partially Paid").where(
+ po.docstatus == 1
+ ).where(po.advance_payment_status.isnull()).where(
+ po.advance_paid < (po.rounded_total or po.grand_total)
+ ).run()
+
+ frappe.qb.update(po).set(po.advance_payment_status, "Fully Paid").where(po.docstatus == 1).where(
+ po.advance_payment_status.isnull()
+ ).where(po.advance_paid == (po.rounded_total or po.grand_total)).run()
+
+ pr = frappe.qb.DocType("Payment Request")
+ frappe.qb.update(po).join(pr).on(po.name == pr.reference_name).set(
+ po.advance_payment_status, "Initiated"
+ ).where(po.docstatus == 1).where(pr.docstatus == 1).where(
+ po.advance_payment_status == "Not Initiated"
+ ).run()
diff --git a/erpnext/public/js/utils.js b/erpnext/public/js/utils.js
index 598167b..de46271 100755
--- a/erpnext/public/js/utils.js
+++ b/erpnext/public/js/utils.js
@@ -21,7 +21,7 @@
},
toggle_naming_series: function() {
- if(cur_frm.fields_dict.naming_series) {
+ if(cur_frm && cur_frm.fields_dict.naming_series) {
cur_frm.toggle_display("naming_series", cur_frm.doc.__islocal?true:false);
}
},
diff --git a/erpnext/public/js/utils/barcode_scanner.js b/erpnext/public/js/utils/barcode_scanner.js
index cf7fab8..aacab0f 100644
--- a/erpnext/public/js/utils/barcode_scanner.js
+++ b/erpnext/public/js/utils/barcode_scanner.js
@@ -105,32 +105,47 @@
this.frm.has_items = false;
}
- if (serial_no && this.is_duplicate_serial_no(row, item_code, serial_no)) {
- this.clean_up();
- reject();
- return;
+ if (serial_no) {
+ this.is_duplicate_serial_no(row, item_code, serial_no)
+ .then((is_duplicate) => {
+ if (!is_duplicate) {
+ this.run_serially_tasks(row, data, resolve);
+ } else {
+ this.clean_up();
+ reject();
+ return;
+ }
+ });
+ } else {
+ this.run_serially_tasks(row, data, resolve);
}
- frappe.run_serially([
- () => this.set_serial_and_batch(row, item_code, serial_no, batch_no),
- () => this.set_barcode(row, barcode),
- () => this.set_item(row, item_code, barcode, batch_no, serial_no).then(qty => {
- this.show_scan_message(row.idx, row.item_code, qty);
- }),
- () => this.set_barcode_uom(row, uom),
- () => this.clean_up(),
- () => resolve(row),
- () => {
- if (row.serial_and_batch_bundle && !this.frm.is_new()) {
- this.frm.save();
- }
- frappe.flags.trigger_from_barcode_scanner = false;
- }
- ]);
});
}
+ run_serially_tasks(row, data, resolve) {
+ const {item_code, barcode, batch_no, serial_no, uom} = data;
+
+ frappe.run_serially([
+ () => this.set_serial_and_batch(row, item_code, serial_no, batch_no),
+ () => this.set_barcode(row, barcode),
+ () => this.set_item(row, item_code, barcode, batch_no, serial_no).then(qty => {
+ this.show_scan_message(row.idx, row.item_code, qty);
+ }),
+ () => this.set_barcode_uom(row, uom),
+ () => this.clean_up(),
+ () => {
+ if (row.serial_and_batch_bundle && !this.frm.is_new()) {
+ this.frm.save();
+ }
+
+ frappe.flags.trigger_from_barcode_scanner = false;
+ },
+ () => resolve(row),
+ ]);
+ }
+
set_item(row, item_code, barcode, batch_no, serial_no) {
return new Promise(resolve => {
const increment = async (value = 1) => {
@@ -475,26 +490,32 @@
}
}
- is_duplicate_serial_no(row, item_code, serial_no) {
- if (this.frm.is_new() || !row.serial_and_batch_bundle) {
- let is_duplicate = this.check_duplicate_serial_no_in_localstorage(item_code, serial_no);
- if (is_duplicate) {
- this.show_alert(__("Serial No {0} is already added", [serial_no]), "orange");
- }
-
- return is_duplicate;
- } else if (row.serial_and_batch_bundle) {
- this.check_duplicate_serial_no_in_db(row, serial_no, (r) => {
- if (r.message) {
+ async is_duplicate_serial_no(row, item_code, serial_no) {
+ let is_duplicate = false;
+ const promise = new Promise((resolve, reject) => {
+ if (this.frm.is_new() || !row.serial_and_batch_bundle) {
+ is_duplicate = this.check_duplicate_serial_no_in_localstorage(item_code, serial_no);
+ if (is_duplicate) {
this.show_alert(__("Serial No {0} is already added", [serial_no]), "orange");
}
- return r.message;
- })
- }
+ resolve(is_duplicate);
+ } else if (row.serial_and_batch_bundle) {
+ this.check_duplicate_serial_no_in_db(row, serial_no, (r) => {
+ if (r.message) {
+ this.show_alert(__("Serial No {0} is already added", [serial_no]), "orange");
+ }
+
+ is_duplicate = r.message;
+ resolve(is_duplicate);
+ })
+ }
+ });
+
+ return await promise;
}
- async check_duplicate_serial_no_in_db(row, serial_no, response) {
+ check_duplicate_serial_no_in_db(row, serial_no, response) {
frappe.call({
method: "erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle.is_duplicate_serial_no",
args: {
@@ -504,7 +525,7 @@
callback(r) {
response(r);
}
- })
+ });
}
check_duplicate_serial_no_in_localstorage(item_code, serial_no) {
diff --git a/erpnext/public/js/utils/sales_common.js b/erpnext/public/js/utils/sales_common.js
index b92b02e..b8ec77f 100644
--- a/erpnext/public/js/utils/sales_common.js
+++ b/erpnext/public/js/utils/sales_common.js
@@ -22,6 +22,15 @@
}
};
});
+
+ this.frm.set_query('project', function(doc) {
+ return {
+ query: "erpnext.controllers.queries.get_project_name",
+ filters: {
+ 'customer': doc.customer
+ }
+ }
+ });
}
setup_queries() {
@@ -439,4 +448,4 @@
}
});
}
-}
\ No newline at end of file
+}
diff --git a/erpnext/public/js/utils/serial_no_batch_selector.js b/erpnext/public/js/utils/serial_no_batch_selector.js
index bf362e3..44a4957 100644
--- a/erpnext/public/js/utils/serial_no_batch_selector.js
+++ b/erpnext/public/js/utils/serial_no_batch_selector.js
@@ -135,7 +135,7 @@
filters: this.get_serial_no_filters()
};
},
- onchange: () => this.update_serial_batch_no()
+ onchange: () => this.scan_barcode_data()
});
}
@@ -145,7 +145,7 @@
options: 'Barcode',
fieldname: 'scan_batch_no',
label: __('Scan Batch No'),
- onchange: () => this.update_serial_batch_no()
+ onchange: () => this.scan_barcode_data()
});
}
@@ -179,11 +179,54 @@
label = __('Serial Nos / Batch Nos');
}
- return [
+ let fields = [
{
fieldtype: 'Section Break',
label: __('{0} {1} via CSV File', [primary_label, label])
- },
+ }
+ ]
+
+ if (this.item?.has_serial_no) {
+ fields = [...fields,
+ {
+ fieldtype: 'Check',
+ label: __('Import Using CSV file'),
+ fieldname: 'import_using_csv_file',
+ default: 0,
+ },
+ {
+ fieldtype: 'Section Break',
+ label: __('{0} {1} Manually', [primary_label, label]),
+ depends_on: 'eval:doc.import_using_csv_file === 0',
+ },
+ {
+ fieldtype: 'Small Text',
+ label: __('Enter Serial Nos'),
+ fieldname: 'upload_serial_nos',
+ depends_on: 'eval:doc.import_using_csv_file === 0',
+ description: __('Enter each serial no in a new line'),
+ },
+ {
+ fieldtype: 'Column Break',
+ depends_on: 'eval:doc.import_using_csv_file === 0',
+ },
+ {
+ fieldtype: 'Button',
+ fieldname: 'make_serial_nos',
+ label: __('Create Serial Nos'),
+ depends_on: 'eval:doc.import_using_csv_file === 0',
+ click: () => {
+ this.create_serial_nos();
+ }
+ },
+ {
+ fieldtype: 'Section Break',
+ depends_on: 'eval:doc.import_using_csv_file === 1',
+ }
+ ];
+ }
+
+ fields = [...fields,
{
fieldtype: 'Button',
fieldname: 'download_csv',
@@ -199,7 +242,32 @@
label: __('Attach CSV File'),
onchange: () => this.upload_csv_file()
}
- ]
+ ];
+
+ return fields;
+ }
+
+ create_serial_nos() {
+ let {upload_serial_nos} = this.dialog.get_values();
+
+ if (!upload_serial_nos) {
+ frappe.throw(__('Please enter Serial Nos'));
+ }
+
+ frappe.call({
+ method: 'erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle.create_serial_nos',
+ args: {
+ item_code: this.item.item_code,
+ serial_nos: upload_serial_nos
+ },
+ callback: (r) => {
+ if (r.message) {
+ this.dialog.fields_dict.entries.df.data = [];
+ this.set_data(r.message);
+ this.update_bundle_entries();
+ }
+ }
+ });
}
download_csv_file() {
@@ -374,6 +442,26 @@
}
}
+ scan_barcode_data() {
+ const { scan_serial_no, scan_batch_no } = this.dialog.get_values();
+
+ if (scan_serial_no || scan_batch_no) {
+ frappe.call({
+ method: 'erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle.is_serial_batch_no_exists',
+ args: {
+ item_code: this.item.item_code,
+ type_of_transaction: this.item.type_of_transaction,
+ serial_no: scan_serial_no,
+ batch_no: scan_batch_no,
+ },
+ callback: (r) => {
+ this.update_serial_batch_no();
+ }
+
+ })
+ }
+ }
+
update_serial_batch_no() {
const { scan_serial_no, scan_batch_no } = this.dialog.get_values();
diff --git a/erpnext/regional/united_arab_emirates/utils.py b/erpnext/regional/united_arab_emirates/utils.py
index 3e9f761..634a152 100644
--- a/erpnext/regional/united_arab_emirates/utils.py
+++ b/erpnext/regional/united_arab_emirates/utils.py
@@ -153,9 +153,7 @@
"account": tax.account_head,
"cost_center": tax.cost_center,
"posting_date": doc.posting_date,
- "against_type": "Supplier",
"against": doc.supplier,
- "against_link": doc.supplier,
dr_or_cr: tax.base_tax_amount_after_discount_amount,
dr_or_cr + "_in_account_currency": tax.base_tax_amount_after_discount_amount
if account_currency == doc.company_currency
diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py
index ab74f7f..654f297 100644
--- a/erpnext/selling/doctype/quotation/quotation.py
+++ b/erpnext/selling/doctype/quotation/quotation.py
@@ -127,7 +127,8 @@
def validate(self):
super(Quotation, self).validate()
self.set_status()
- self.validate_uom_is_integer("stock_uom", "qty")
+ self.validate_uom_is_integer("stock_uom", "stock_qty")
+ self.validate_uom_is_integer("uom", "qty")
self.validate_valid_till()
self.set_customer_name()
if self.items:
diff --git a/erpnext/selling/doctype/quotation/test_quotation.py b/erpnext/selling/doctype/quotation/test_quotation.py
index ecb7d09..2a4855e 100644
--- a/erpnext/selling/doctype/quotation/test_quotation.py
+++ b/erpnext/selling/doctype/quotation/test_quotation.py
@@ -593,6 +593,22 @@
quotation.reload()
self.assertEqual(quotation.status, "Ordered")
+ def test_uom_validation(self):
+ from erpnext.stock.doctype.item.test_item import make_item
+
+ item = "_Test Item FOR UOM Validation"
+ make_item(item, {"is_stock_item": 1})
+
+ if not frappe.db.exists("UOM", "lbs"):
+ frappe.get_doc({"doctype": "UOM", "uom_name": "lbs", "must_be_whole_number": 1}).insert()
+ else:
+ frappe.db.set_value("UOM", "lbs", "must_be_whole_number", 1)
+
+ quotation = make_quotation(item_code=item, qty=1, rate=100, do_not_submit=1)
+ quotation.items[0].uom = "lbs"
+ quotation.items[0].conversion_factor = 2.23
+ self.assertRaises(frappe.ValidationError, quotation.save)
+
test_records = frappe.get_test_records("Quotation")
diff --git a/erpnext/selling/doctype/sales_order/sales_order.js b/erpnext/selling/doctype/sales_order/sales_order.js
index b206e3f..2bb093d 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.js
+++ b/erpnext/selling/doctype/sales_order/sales_order.js
@@ -94,6 +94,9 @@
frm.set_value("reserve_stock", 0);
frm.set_df_property("reserve_stock", "read_only", 1);
frm.set_df_property("reserve_stock", "hidden", 1);
+ frm.fields_dict.items.grid.update_docfield_property('reserve_stock', 'hidden', 1);
+ frm.fields_dict.items.grid.update_docfield_property('reserve_stock', 'default', 0);
+ frm.fields_dict.items.grid.update_docfield_property('reserve_stock', 'read_only', 1);
}
})
}
@@ -141,15 +144,6 @@
};
});
- frm.set_query('project', function(doc, cdt, cdn) {
- return {
- query: "erpnext.controllers.queries.get_project_name",
- filters: {
- 'customer': doc.customer
- }
- }
- });
-
frm.set_query('warehouse', 'items', function(doc, cdt, cdn) {
let row = locals[cdt][cdn];
let query = {
diff --git a/erpnext/selling/doctype/sales_order/sales_order.json b/erpnext/selling/doctype/sales_order/sales_order.json
index 01d047c..3c516d0 100644
--- a/erpnext/selling/doctype/sales_order/sales_order.json
+++ b/erpnext/selling/doctype/sales_order/sales_order.json
@@ -131,6 +131,7 @@
"per_billed",
"per_picked",
"billing_status",
+ "advance_payment_status",
"sales_team_section_break",
"sales_partner",
"column_break7",
@@ -1269,7 +1270,7 @@
"no_copy": 1,
"oldfieldname": "status",
"oldfieldtype": "Select",
- "options": "\nDraft\nOn Hold\nTo Deliver and Bill\nTo Bill\nTo Deliver\nCompleted\nCancelled\nClosed",
+ "options": "\nDraft\nOn Hold\nTo Pay\nTo Deliver and Bill\nTo Bill\nTo Deliver\nCompleted\nCancelled\nClosed",
"print_hide": 1,
"read_only": 1,
"reqd": 1,
@@ -1638,6 +1639,18 @@
"no_copy": 1,
"print_hide": 1,
"report_hide": 1
+ },
+ {
+ "fieldname": "advance_payment_status",
+ "fieldtype": "Select",
+ "hidden": 1,
+ "hide_days": 1,
+ "hide_seconds": 1,
+ "in_standard_filter": 1,
+ "label": "Advance Payment Status",
+ "no_copy": 1,
+ "options": "Not Requested\nRequested\nPartially Paid\nFully Paid",
+ "print_hide": 1
}
],
"icon": "fa fa-file-text",
@@ -1722,4 +1735,4 @@
"title_field": "customer_name",
"track_changes": 1,
"track_seen": 1
-}
\ No newline at end of file
+}
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index 9542361..5d1d769 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -200,6 +200,7 @@
self.validate_for_items()
self.validate_warehouse()
self.validate_drop_ship()
+ self.validate_reserved_stock()
self.validate_serial_no_based_delivery()
validate_against_blanket_order(self)
validate_inter_company_party(
@@ -222,6 +223,8 @@
self.billing_status = "Not Billed"
if not self.delivery_status:
self.delivery_status = "Not Delivered"
+ if not self.advance_payment_status:
+ self.advance_payment_status = "Not Requested"
self.reset_default_field_value("set_warehouse", "items", "warehouse")
@@ -660,6 +663,17 @@
).format(item.item_code)
)
+ def validate_reserved_stock(self):
+ """Clean reserved stock flag for non-stock Item"""
+
+ enable_stock_reservation = frappe.db.get_single_value(
+ "Stock Settings", "enable_stock_reservation"
+ )
+
+ for item in self.items:
+ if item.reserve_stock and (not enable_stock_reservation or not cint(item.is_stock_item)):
+ item.reserve_stock = 0
+
def has_unreserved_stock(self) -> bool:
"""Returns True if there is any unreserved item in the Sales Order."""
diff --git a/erpnext/selling/doctype/sales_order/sales_order_list.js b/erpnext/selling/doctype/sales_order/sales_order_list.js
index 518f018..37686a8 100644
--- a/erpnext/selling/doctype/sales_order/sales_order_list.js
+++ b/erpnext/selling/doctype/sales_order/sales_order_list.js
@@ -1,6 +1,6 @@
frappe.listview_settings['Sales Order'] = {
add_fields: ["base_grand_total", "customer_name", "currency", "delivery_date",
- "per_delivered", "per_billed", "status", "order_type", "name", "skip_delivery_note"],
+ "per_delivered", "per_billed", "status", "advance_payment_status", "order_type", "name", "skip_delivery_note"],
get_indicator: function (doc) {
if (doc.status === "Closed") {
// Closed
@@ -10,6 +10,8 @@
return [__("On Hold"), "orange", "status,=,On Hold"];
} else if (doc.status === "Completed") {
return [__("Completed"), "green", "status,=,Completed"];
+ } else if (doc.advance_payment_status === "Requested") {
+ return [__("To Pay"), "gray", "advance_payment_status,=,Requested"];
} else if (!doc.skip_delivery_note && flt(doc.per_delivered, 2) < 100) {
if (frappe.datetime.get_diff(doc.delivery_date) < 0) {
// not delivered & overdue
diff --git a/erpnext/selling/doctype/sales_order/test_sales_order.py b/erpnext/selling/doctype/sales_order/test_sales_order.py
index ac7fdb1..5ae48ee 100644
--- a/erpnext/selling/doctype/sales_order/test_sales_order.py
+++ b/erpnext/selling/doctype/sales_order/test_sales_order.py
@@ -1996,6 +1996,33 @@
self.assertEqual(so.items[0].rate, scenario.get("expected_rate"))
self.assertEqual(so.packed_items[0].rate, scenario.get("expected_rate"))
+ def test_sales_order_advance_payment_status(self):
+ from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
+ from erpnext.accounts.doctype.payment_request.payment_request import make_payment_request
+
+ so = make_sales_order(qty=1, rate=100)
+ self.assertEqual(
+ frappe.db.get_value(so.doctype, so.name, "advance_payment_status"), "Not Requested"
+ )
+
+ pr = make_payment_request(dt=so.doctype, dn=so.name, submit_doc=True, return_doc=True)
+ self.assertEqual(frappe.db.get_value(so.doctype, so.name, "advance_payment_status"), "Requested")
+
+ pe = get_payment_entry(so.doctype, so.name).save().submit()
+ self.assertEqual(
+ frappe.db.get_value(so.doctype, so.name, "advance_payment_status"), "Fully Paid"
+ )
+
+ pe.reload()
+ pe.cancel()
+ self.assertEqual(frappe.db.get_value(so.doctype, so.name, "advance_payment_status"), "Requested")
+
+ pr.reload()
+ pr.cancel()
+ self.assertEqual(
+ frappe.db.get_value(so.doctype, so.name, "advance_payment_status"), "Not Requested"
+ )
+
def automatically_fetch_payment_terms(enable=1):
accounts_settings = frappe.get_doc("Accounts Settings")
diff --git a/erpnext/selling/doctype/sales_order_item/sales_order_item.json b/erpnext/selling/doctype/sales_order_item/sales_order_item.json
index d4ccfc4..87aeeac 100644
--- a/erpnext/selling/doctype/sales_order_item/sales_order_item.json
+++ b/erpnext/selling/doctype/sales_order_item/sales_order_item.json
@@ -10,6 +10,7 @@
"item_code",
"customer_item_code",
"ensure_delivery_based_on_produced_serial_no",
+ "is_stock_item",
"reserve_stock",
"col_break1",
"delivery_date",
@@ -867,6 +868,7 @@
{
"allow_on_submit": 1,
"default": "1",
+ "depends_on": "eval:doc.is_stock_item",
"fieldname": "reserve_stock",
"fieldtype": "Check",
"label": "Reserve Stock",
@@ -891,6 +893,16 @@
"label": "Production Plan Qty",
"no_copy": 1,
"read_only": 1
+ },
+ {
+ "default": "0",
+ "fetch_from": "item_code.is_stock_item",
+ "fieldname": "is_stock_item",
+ "fieldtype": "Check",
+ "hidden": 1,
+ "label": "Is Stock Item",
+ "print_hide": 1,
+ "report_hide": 1
}
],
"idx": 1,
diff --git a/erpnext/selling/page/sales_funnel/sales_funnel.js b/erpnext/selling/page/sales_funnel/sales_funnel.js
index e3d0a55..a0a1222 100644
--- a/erpnext/selling/page/sales_funnel/sales_funnel.js
+++ b/erpnext/selling/page/sales_funnel/sales_funnel.js
@@ -126,9 +126,9 @@
if (me.options.chart == 'sales_funnel'){
me.render_funnel();
} else if (me.options.chart == 'opp_by_lead_source'){
- me.render_chart("Sales Opportunities by Source");
+ me.render_chart(__("Sales Opportunities by Source"));
} else if (me.options.chart == 'sales_pipeline'){
- me.render_chart("Sales Pipeline by Stage");
+ me.render_chart(__("Sales Pipeline by Stage"));
}
}
diff --git a/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py b/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py
index 3682c5f..00acc80 100644
--- a/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py
+++ b/erpnext/selling/report/payment_terms_status_for_sales_order/payment_terms_status_for_sales_order.py
@@ -209,7 +209,7 @@
)
.where(
(so.docstatus == 1)
- & (so.status.isin(["To Deliver and Bill", "To Bill"]))
+ & (so.status.isin(["To Deliver and Bill", "To Bill", "To Pay"]))
& (so.payment_terms_template != "NULL")
& (so.company == conditions.company)
& (so.transaction_date[conditions.start_date : conditions.end_date])
diff --git a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js
index ac3d3db..fc685e0 100644
--- a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js
+++ b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js
@@ -56,7 +56,7 @@
"fieldtype": "MultiSelectList",
"width": "80",
get_data: function(txt) {
- let status = ["To Bill", "To Deliver", "To Deliver and Bill", "Completed"]
+ let status = ["To Pay", "To Bill", "To Deliver", "To Deliver and Bill", "Completed"]
let options = []
for (let option of status){
options.push({
diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js
index 1bd469b..340a917 100644
--- a/erpnext/setup/doctype/company/company.js
+++ b/erpnext/setup/doctype/company/company.js
@@ -140,38 +140,48 @@
},
delete_company_transactions: function(frm) {
- frappe.verify_password(function() {
- var d = frappe.prompt({
- fieldtype:"Data",
- fieldname: "company_name",
- label: __("Please enter the company name to confirm"),
- reqd: 1,
- description: __("Please make sure you really want to delete all the transactions for this company. Your master data will remain as it is. This action cannot be undone.")
+ frappe.call({
+ method: "erpnext.setup.doctype.company.company.is_deletion_job_running",
+ args: {
+ company: frm.doc.name
},
- function(data) {
- if(data.company_name !== frm.doc.name) {
- frappe.msgprint(__("Company name not same"));
- return;
+ freeze: true,
+ callback: function(r) {
+ if(!r.exc) {
+ frappe.verify_password(function() {
+ var d = frappe.prompt({
+ fieldtype:"Data",
+ fieldname: "company_name",
+ label: __("Please enter the company name to confirm"),
+ reqd: 1,
+ description: __("Please make sure you really want to delete all the transactions for this company. Your master data will remain as it is. This action cannot be undone.")
+ },
+ function(data) {
+ if(data.company_name !== frm.doc.name) {
+ frappe.msgprint(__("Company name not same"));
+ return;
+ }
+ frappe.call({
+ method: "erpnext.setup.doctype.company.company.create_transaction_deletion_request",
+ args: {
+ company: data.company_name
+ },
+ freeze: true,
+ callback: function(r, rt) { },
+ onerror: function() {
+ frappe.msgprint(__("Wrong Password"));
+ }
+ });
+ },
+ __("Delete all the Transactions for this Company"), __("Delete")
+ );
+ d.get_primary_btn().addClass("btn-danger");
+ });
}
- frappe.call({
- method: "erpnext.setup.doctype.company.company.create_transaction_deletion_request",
- args: {
- company: data.company_name
- },
- freeze: true,
- callback: function(r, rt) {
- if(!r.exc)
- frappe.msgprint(__("Successfully deleted all transactions related to this company!"));
- },
- onerror: function() {
- frappe.msgprint(__("Wrong Password"));
- }
- });
+
},
- __("Delete all the Transactions for this Company"), __("Delete")
- );
- d.get_primary_btn().addClass("btn-danger");
});
+
}
});
diff --git a/erpnext/setup/doctype/company/company.py b/erpnext/setup/doctype/company/company.py
index ec953b8..68a3854 100644
--- a/erpnext/setup/doctype/company/company.py
+++ b/erpnext/setup/doctype/company/company.py
@@ -11,7 +11,8 @@
from frappe.contacts.address_and_contact import load_address_and_contact
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
from frappe.desk.page.setup_wizard.setup_wizard import make_records
-from frappe.utils import cint, formatdate, get_timestamp, today
+from frappe.utils import cint, formatdate, get_link_to_form, get_timestamp, today
+from frappe.utils.background_jobs import get_job, is_job_enqueued
from frappe.utils.nestedset import NestedSet, rebuild_tree
from erpnext.accounts.doctype.account.account import get_account_currency
@@ -900,8 +901,37 @@
return None
+def generate_id_for_deletion_job(company):
+ return "delete_company_transactions_" + company
+
+
+@frappe.whitelist()
+def is_deletion_job_running(company):
+ job_id = generate_id_for_deletion_job(company)
+ job_name = get_job(job_id).get_id() # job name will have site prefix
+ if is_job_enqueued(job_id):
+ frappe.throw(
+ _("A Transaction Deletion Job: {0} is already running for {1}").format(
+ frappe.bold(get_link_to_form("RQ Job", job_name)), frappe.bold(company)
+ )
+ )
+
+
@frappe.whitelist()
def create_transaction_deletion_request(company):
+ is_deletion_job_running(company)
+ job_id = generate_id_for_deletion_job(company)
+
tdr = frappe.get_doc({"doctype": "Transaction Deletion Record", "company": company})
tdr.insert()
- tdr.submit()
+
+ frappe.enqueue(
+ "frappe.utils.background_jobs.run_doc_method",
+ doctype=tdr.doctype,
+ name=tdr.name,
+ doc_method="submit",
+ job_id=job_id,
+ queue="long",
+ enqueue_after_commit=True,
+ )
+ frappe.msgprint(_("A Transaction Deletion Job is triggered for {0}").format(frappe.bold(company)))
diff --git a/erpnext/setup/doctype/vehicle/vehicle.json b/erpnext/setup/doctype/vehicle/vehicle.json
index b19d459..bf1a8c1 100644
--- a/erpnext/setup/doctype/vehicle/vehicle.json
+++ b/erpnext/setup/doctype/vehicle/vehicle.json
@@ -57,7 +57,7 @@
"in_global_search": 0,
"in_list_view": 0,
"in_standard_filter": 0,
- "label": "Make",
+ "label": "Manufacturer",
"length": 0,
"no_copy": 0,
"permlevel": 0,
diff --git a/erpnext/stock/doctype/bin/bin.json b/erpnext/stock/doctype/bin/bin.json
index 10d9511..39e0917 100644
--- a/erpnext/stock/doctype/bin/bin.json
+++ b/erpnext/stock/doctype/bin/bin.json
@@ -186,7 +186,7 @@
"idx": 1,
"in_create": 1,
"links": [],
- "modified": "2023-11-01 16:51:17.079107",
+ "modified": "2024-01-16 15:11:46.140323",
"modified_by": "Administrator",
"module": "Stock",
"name": "Bin",
@@ -213,6 +213,21 @@
"read": 1,
"report": 1,
"role": "Stock User"
+ },
+ {
+ "read": 1,
+ "report": 1,
+ "role": "Stock Manager"
+ },
+ {
+ "read": 1,
+ "report": 1,
+ "role": "Purchase Manager"
+ },
+ {
+ "read": 1,
+ "report": 1,
+ "role": "Sales Manager"
}
],
"quick_entry": 1,
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.js b/erpnext/stock/doctype/delivery_note/delivery_note.js
index ec68549..14aedca 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.js
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.js
@@ -31,15 +31,6 @@
});
erpnext.queries.setup_warehouse_query(frm);
- frm.set_query('project', function(doc) {
- return {
- query: "erpnext.controllers.queries.get_project_name",
- filters: {
- 'customer': doc.customer
- }
- }
- })
-
frm.set_query('transporter', function() {
return {
filters: {
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index 326ef9b..fcb7a6d 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -519,7 +519,6 @@
debit=0.0,
credit=discrepancy_caused_by_exchange_rate_difference,
remarks=remarks,
- against_type="Supplier",
against_account=self.supplier,
debit_in_account_currency=-1 * discrepancy_caused_by_exchange_rate_difference,
account_currency=account_currency,
@@ -533,7 +532,6 @@
debit=discrepancy_caused_by_exchange_rate_difference,
credit=0.0,
remarks=remarks,
- against_type="Supplier",
against_account=self.supplier,
debit_in_account_currency=-1 * discrepancy_caused_by_exchange_rate_difference,
account_currency=account_currency,
@@ -800,7 +798,7 @@
# Backward compatibility:
# and charges added via Landed Cost Voucher,
# post valuation related charges on "Stock Received But Not Billed"
- against_accounts = [d.account for d in gl_entries if flt(d.debit) > 0]
+ against_accounts = ", ".join([d.account for d in gl_entries if flt(d.debit) > 0])
total_valuation_amount = sum(valuation_tax.values())
amount_including_divisional_loss = negative_expense_to_be_booked
stock_rbnb = (
@@ -832,17 +830,16 @@
)
amount_including_divisional_loss -= applicable_amount
- for against in against_accounts:
- self.add_gl_entry(
- gl_entries=gl_entries,
- account=account,
- cost_center=tax.cost_center,
- debit=0.0,
- credit=flt(applicable_amount) / len(against_accounts),
- remarks=self.remarks or _("Accounting Entry for Stock"),
- against_account=against,
- item=tax,
- )
+ self.add_gl_entry(
+ gl_entries=gl_entries,
+ account=account,
+ cost_center=tax.cost_center,
+ debit=0.0,
+ credit=applicable_amount,
+ remarks=self.remarks or _("Accounting Entry for Stock"),
+ against_account=against_accounts,
+ item=tax,
+ )
i += 1
diff --git a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
index 497530f..57ba5bb 100644
--- a/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/test_purchase_receipt.py
@@ -4,7 +4,6 @@
import frappe
from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import add_days, cint, cstr, flt, nowtime, today
-from pypika import Order
from pypika import functions as fn
import erpnext
@@ -1009,7 +1008,7 @@
gl_entries = get_gl_entries("Purchase Receipt", pr.name)
sl_entries = get_sl_entries("Purchase Receipt", pr.name)
- self.assertEqual(len(gl_entries), 2)
+ self.assertFalse(gl_entries)
expected_sle = {"Work In Progress - TCP1": -5, "Stores - TCP1": 5}
@@ -2254,13 +2253,13 @@
def get_gl_entries(voucher_type, voucher_no):
- gle = frappe.qb.DocType("GL Entry")
- return (
- frappe.qb.from_(gle)
- .select(gle.account, gle.debit, gle.credit, gle.cost_center, gle.is_cancelled)
- .where((gle.voucher_type == voucher_type) & (gle.voucher_no == voucher_no))
- .orderby(gle.account, gle.debit, order=Order.desc)
- ).run(as_dict=True)
+ return frappe.db.sql(
+ """select account, debit, credit, cost_center, is_cancelled
+ from `tabGL Entry` where voucher_type=%s and voucher_no=%s
+ order by account desc""",
+ (voucher_type, voucher_no),
+ as_dict=1,
+ )
def get_taxes(**args):
diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.js b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.js
index 9f01ee9..91b7430 100644
--- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.js
+++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.js
@@ -74,7 +74,7 @@
let fields = [
{
- "label": __("Using CSV File"),
+ "label": __("Import Using CSV file"),
"fieldname": "using_csv_file",
"default": 1,
"fieldtype": "Check",
diff --git a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
index 2b87fcd..63cc938 100644
--- a/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
+++ b/erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py
@@ -999,9 +999,25 @@
make_serial_nos(item_code, serial_nos)
+ if kwargs.get("_has_serial_nos"):
+ return serial_nos
+
return serial_nos, batch_nos
+@frappe.whitelist()
+def create_serial_nos(item_code, serial_nos):
+ serial_nos = get_serial_batch_from_data(
+ item_code,
+ {
+ "serial_nos": serial_nos,
+ "_has_serial_nos": True,
+ },
+ )
+
+ return serial_nos
+
+
def make_serial_nos(item_code, serial_nos):
item = frappe.get_cached_value("Item", item_code, ["description", "item_code"], as_dict=1)
@@ -2080,5 +2096,34 @@
@frappe.whitelist()
+def is_serial_batch_no_exists(item_code, type_of_transaction, serial_no=None, batch_no=None):
+ if serial_no and not frappe.db.exists("Serial No", serial_no):
+ if type_of_transaction != "Inward":
+ frappe.throw(_("Serial No {0} does not exists").format(serial_no))
+
+ make_serial_no(serial_no, item_code)
+
+ if batch_no and frappe.db.exists("Batch", batch_no):
+ if type_of_transaction != "Inward":
+ frappe.throw(_("Batch No {0} does not exists").format(batch_no))
+
+ make_batch_no(batch_no, item_code)
+
+
+def make_serial_no(serial_no, item_code):
+ serial_no_doc = frappe.new_doc("Serial No")
+ serial_no_doc.serial_no = serial_no
+ serial_no_doc.item_code = item_code
+ serial_no_doc.save(ignore_permissions=True)
+
+
+def make_batch_no(batch_no, item_code):
+ batch_doc = frappe.new_doc("Batch")
+ batch_doc.batch_id = batch_no
+ batch_doc.item = item_code
+ batch_doc.save(ignore_permissions=True)
+
+
+@frappe.whitelist()
def is_duplicate_serial_no(bundle_id, serial_no):
return frappe.db.exists("Serial and Batch Entry", {"parent": bundle_id, "serial_no": serial_no})
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index bc5f428..96c249f 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -1459,9 +1459,7 @@
self.get_gl_dict(
{
"account": account,
- "against_type": "Account",
"against": d.expense_account,
- "against_link": d.expense_account,
"cost_center": d.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"credit_in_account_currency": flt(amount["amount"]),
@@ -1475,9 +1473,7 @@
self.get_gl_dict(
{
"account": d.expense_account,
- "against_type": "Account",
"against": account,
- "against_link": account,
"cost_center": d.cost_center,
"remarks": self.get("remarks") or _("Accounting Entry for Stock"),
"credit": -1
diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
index 085d215..23dacc8 100644
--- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
@@ -517,14 +517,7 @@
self.check_gl_entries(
"Stock Entry",
repack.name,
- sorted(
- [
- ["Cost of Goods Sold - TCP1", 0.0, 1200.0],
- ["Stock Adjustment - TCP1", 0.0, 1200.0],
- ["Stock Adjustment - TCP1", 1200.0, 0.0],
- [stock_in_hand_account, 1200.0, 0.0],
- ]
- ),
+ sorted([[stock_in_hand_account, 1200, 0.0], ["Cost of Goods Sold - TCP1", 0.0, 1200.0]]),
)
def check_stock_ledger_entries(self, voucher_type, voucher_no, expected_sle):
diff --git a/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js
index 61927f5..c175a4a 100644
--- a/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js
+++ b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js
@@ -1,7 +1,7 @@
frappe.pages['warehouse-capacity-summary'].on_page_load = function(wrapper) {
var page = frappe.ui.make_app_page({
parent: wrapper,
- title: 'Warehouse Capacity Summary',
+ title: __('Warehouse Capacity Summary'),
single_column: true
});
page.set_secondary_action('Refresh', () => page.capacity_dashboard.refresh(), 'refresh');
diff --git a/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary_header.html b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary_header.html
index 1183ad4..1883004 100644
--- a/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary_header.html
+++ b/erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary_header.html
@@ -1,19 +1,19 @@
<div class="dashboard-list-item" style="padding: 12px 15px;">
<div class="row">
<div class="col-sm-2 text-muted" style="margin-top: 8px;">
- Warehouse
+ {{ __("Warehouse") }}
</div>
<div class="col-sm-2 text-muted" style="margin-top: 8px;">
- Item
+ {{ __("Item") }}
</div>
<div class="col-sm-1 text-muted" style="margin-top: 8px;">
- Stock Capacity
+ {{ __("Stock Capacity") }}
</div>
<div class="col-sm-2 text-muted" style="margin-top: 8px;">
- Balance Stock Qty
+ {{ __("Balance Stock Qty") }}
</div>
<div class="col-sm-2 text-muted" style="margin-top: 8px;">
- % Occupied
+ {{ __("% Occupied") }}
</div>
</div>
</div>
diff --git a/erpnext/stock/report/stock_ageing/stock_ageing.py b/erpnext/stock/report/stock_ageing/stock_ageing.py
index f055c6c..02ac8c6 100644
--- a/erpnext/stock/report/stock_ageing/stock_ageing.py
+++ b/erpnext/stock/report/stock_ageing/stock_ageing.py
@@ -233,28 +233,30 @@
"""
stock_ledger_entries = self.sle
- if stock_ledger_entries is None:
- stock_ledger_entries = self.__get_stock_ledger_entries()
- for d in stock_ledger_entries:
- key, fifo_queue, transferred_item_key = self.__init_key_stores(d)
+ with frappe.db.unbuffered_cursor():
+ if stock_ledger_entries is None:
+ stock_ledger_entries = self.__get_stock_ledger_entries()
- if d.voucher_type == "Stock Reconciliation":
- # get difference in qty shift as actual qty
- prev_balance_qty = self.item_details[key].get("qty_after_transaction", 0)
- d.actual_qty = flt(d.qty_after_transaction) - flt(prev_balance_qty)
+ for d in stock_ledger_entries:
+ key, fifo_queue, transferred_item_key = self.__init_key_stores(d)
- serial_nos = get_serial_nos(d.serial_no) if d.serial_no else []
+ if d.voucher_type == "Stock Reconciliation":
+ # get difference in qty shift as actual qty
+ prev_balance_qty = self.item_details[key].get("qty_after_transaction", 0)
+ d.actual_qty = flt(d.qty_after_transaction) - flt(prev_balance_qty)
- if d.actual_qty > 0:
- self.__compute_incoming_stock(d, fifo_queue, transferred_item_key, serial_nos)
- else:
- self.__compute_outgoing_stock(d, fifo_queue, transferred_item_key, serial_nos)
+ serial_nos = get_serial_nos(d.serial_no) if d.serial_no else []
- self.__update_balances(d, key)
+ if d.actual_qty > 0:
+ self.__compute_incoming_stock(d, fifo_queue, transferred_item_key, serial_nos)
+ else:
+ self.__compute_outgoing_stock(d, fifo_queue, transferred_item_key, serial_nos)
- # Note that stock_ledger_entries is an iterator, you can not reuse it like a list
- del stock_ledger_entries
+ self.__update_balances(d, key)
+
+ # Note that stock_ledger_entries is an iterator, you can not reuse it like a list
+ del stock_ledger_entries
if not self.filters.get("show_warehouse_wise_stock"):
# (Item 1, WH 1), (Item 1, WH 2) => (Item 1)
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index a6206ac..0370666 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -439,7 +439,7 @@
reposting_data = get_reposting_data(doc.reposting_data_file)
if reposting_data and reposting_data.distinct_item_and_warehouse:
- return reposting_data.distinct_item_and_warehouse
+ return parse_distinct_items_and_warehouses(reposting_data.distinct_item_and_warehouse)
distinct_item_warehouses = {}
@@ -457,6 +457,16 @@
return distinct_item_warehouses
+def parse_distinct_items_and_warehouses(distinct_items_and_warehouses):
+ new_dict = frappe._dict({})
+
+ # convert string keys to tuple
+ for k, v in distinct_items_and_warehouses.items():
+ new_dict[frappe.safe_eval(k)] = frappe._dict(v)
+
+ return new_dict
+
+
def get_affected_transactions(doc, reposting_data=None) -> Set[Tuple[str, str]]:
if not reposting_data and doc and doc.reposting_data_file:
reposting_data = get_reposting_data(doc.reposting_data_file)
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py
index 9d7be36..5523c31 100644
--- a/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt/test_subcontracting_receipt.py
@@ -365,17 +365,24 @@
fg_warehouse_ac = get_inventory_account(scr.company, scr.items[0].warehouse)
supplier_warehouse_ac = get_inventory_account(scr.company, scr.supplier_warehouse)
expense_account = scr.items[0].expense_account
- expected_values = [
- [fg_warehouse_ac, 2100.0, 0.0], # FG Amount (D)
- [supplier_warehouse_ac, 0.0, 1000.0], # RM Cost (C)
- [additional_costs_expense_account, 0.0, 100.0], # Additional Cost (C)
- [expense_account, 0.0, 1000.0], # Service Cost (C)
- ]
- for i in range(len(expected_values)):
- self.assertEqual(expected_values[i][0], gl_entries[i]["account"])
- self.assertEqual(expected_values[i][1], gl_entries[i]["debit"])
- self.assertEqual(expected_values[i][2], gl_entries[i]["credit"])
+ if fg_warehouse_ac == supplier_warehouse_ac:
+ expected_values = {
+ fg_warehouse_ac: [2100.0, 1000.0], # FG Amount (D), RM Cost (C)
+ expense_account: [0.0, 1000.0], # Service Cost (C)
+ additional_costs_expense_account: [0.0, 100.0], # Additional Cost (C)
+ }
+ else:
+ expected_values = {
+ fg_warehouse_ac: [2100.0, 0.0], # FG Amount (D)
+ supplier_warehouse_ac: [0.0, 1000.0], # RM Cost (C)
+ expense_account: [0.0, 1000.0], # Service Cost (C)
+ additional_costs_expense_account: [0.0, 100.0], # Additional Cost (C)
+ }
+
+ for gle in gl_entries:
+ self.assertEqual(expected_values[gle.account][0], gle.debit)
+ self.assertEqual(expected_values[gle.account][1], gle.credit)
scr.reload()
scr.cancel()