Merge remote-tracking branch 'upstream/develop' into feat/so-po-advance-payment-status
diff --git a/erpnext/accounts/doctype/bank_transaction/auto_match_party.py b/erpnext/accounts/doctype/bank_transaction/auto_match_party.py
index 5d94a08..04dab4c 100644
--- a/erpnext/accounts/doctype/bank_transaction/auto_match_party.py
+++ b/erpnext/accounts/doctype/bank_transaction/auto_match_party.py
@@ -112,7 +112,8 @@
for party in parties:
filters = {"status": "Active"} if party == "Employee" else {"disabled": 0}
- names = frappe.get_all(party, filters=filters, pluck=party.lower() + "_name")
+ field = party.lower() + "_name"
+ names = frappe.get_all(party, filters=filters, fields=[f"{field} as party_name", "name"])
for field in ["bank_party_name", "description"]:
if not self.get(field):
@@ -131,7 +132,11 @@
def fuzzy_search_and_return_result(self, party, names, field) -> Union[Tuple, None]:
skip = False
- result = process.extract(query=self.get(field), choices=names, scorer=fuzz.token_set_ratio)
+ result = process.extract(
+ query=self.get(field),
+ choices={row.get("name"): row.get("party_name") for row in names},
+ scorer=fuzz.token_set_ratio,
+ )
party_name, skip = self.process_fuzzy_result(result)
if not party_name:
@@ -149,14 +154,14 @@
Returns: Result, Skip (whether or not to discontinue matching)
"""
- PARTY, SCORE, CUTOFF = 0, 1, 80
+ SCORE, PARTY_ID, CUTOFF = 1, 2, 80
if not result or not len(result):
return None, False
first_result = result[0]
if len(result) == 1:
- return (first_result[PARTY] if first_result[SCORE] > CUTOFF else None), True
+ return (first_result[PARTY_ID] if first_result[SCORE] > CUTOFF else None), True
second_result = result[1]
if first_result[SCORE] > CUTOFF:
@@ -165,7 +170,7 @@
if first_result[SCORE] == second_result[SCORE]:
return None, True
- return first_result[PARTY], True
+ return first_result[PARTY_ID], True
else:
return None, False
diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js
index d61f8a6..56fa6ce 100644
--- a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js
+++ b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.js
@@ -53,10 +53,18 @@
of Accounts. Please enter the account names and add more rows as per your requirement.`);
}
}
- }
+ },
+ {
+ label : "Company",
+ fieldname: "company",
+ fieldtype: "Link",
+ reqd: 1,
+ hidden: 1,
+ default: frm.doc.company,
+ },
],
primary_action: function() {
- var data = d.get_values();
+ let data = d.get_values();
if (!data.template_type) {
frappe.throw(__('Please select <b>Template Type</b> to download template'));
@@ -66,7 +74,8 @@
'/api/method/erpnext.accounts.doctype.chart_of_accounts_importer.chart_of_accounts_importer.download_template',
{
file_type: data.file_type,
- template_type: data.template_type
+ template_type: data.template_type,
+ company: data.company
}
);
diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py
index d6e1be4..5a1c139 100644
--- a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py
+++ b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py
@@ -8,6 +8,7 @@
import frappe
from frappe import _
+from frappe.desk.form.linked_with import get_linked_fields
from frappe.model.document import Document
from frappe.utils import cint, cstr
from frappe.utils.csvutils import UnicodeWriter
@@ -294,10 +295,8 @@
@frappe.whitelist()
-def download_template(file_type, template_type):
- data = frappe._dict(frappe.local.form_dict)
-
- writer = get_template(template_type)
+def download_template(file_type, template_type, company):
+ writer = get_template(template_type, company)
if file_type == "CSV":
# download csv file
@@ -308,8 +307,7 @@
build_response_as_excel(writer)
-def get_template(template_type):
-
+def get_template(template_type, company):
fields = [
"Account Name",
"Parent Account",
@@ -335,34 +333,17 @@
["", "", "", "", 0, account_type.get("account_type"), account_type.get("root_type")]
)
else:
- writer = get_sample_template(writer)
+ writer = get_sample_template(writer, company)
return writer
-def get_sample_template(writer):
- template = [
- ["Application Of Funds(Assets)", "", "", "", 1, "", "Asset"],
- ["Sources Of Funds(Liabilities)", "", "", "", 1, "", "Liability"],
- ["Equity", "", "", "", 1, "", "Equity"],
- ["Expenses", "", "", "", 1, "", "Expense"],
- ["Income", "", "", "", 1, "", "Income"],
- ["Bank Accounts", "Application Of Funds(Assets)", "", "", 1, "Bank", "Asset"],
- ["Cash In Hand", "Application Of Funds(Assets)", "", "", 1, "Cash", "Asset"],
- ["Stock Assets", "Application Of Funds(Assets)", "", "", 1, "Stock", "Asset"],
- ["Cost Of Goods Sold", "Expenses", "", "", 0, "Cost of Goods Sold", "Expense"],
- ["Asset Depreciation", "Expenses", "", "", 0, "Depreciation", "Expense"],
- ["Fixed Assets", "Application Of Funds(Assets)", "", "", 0, "Fixed Asset", "Asset"],
- ["Accounts Payable", "Sources Of Funds(Liabilities)", "", "", 0, "Payable", "Liability"],
- ["Accounts Receivable", "Application Of Funds(Assets)", "", "", 1, "Receivable", "Asset"],
- ["Stock Expenses", "Expenses", "", "", 0, "Stock Adjustment", "Expense"],
- ["Sample Bank", "Bank Accounts", "", "", 0, "Bank", "Asset"],
- ["Cash", "Cash In Hand", "", "", 0, "Cash", "Asset"],
- ["Stores", "Stock Assets", "", "", 0, "Stock", "Asset"],
- ]
-
- for row in template:
- writer.writerow(row)
+def get_sample_template(writer, company):
+ currency = frappe.db.get_value("Company", company, "default_currency")
+ with open(os.path.join(os.path.dirname(__file__), "coa_sample_template.csv"), "r") as f:
+ for row in f:
+ row = row.strip().split(",") + [currency]
+ writer.writerow(row)
return writer
@@ -453,14 +434,11 @@
def unset_existing_data(company):
- linked = frappe.db.sql(
- '''select fieldname from tabDocField
- where fieldtype="Link" and options="Account" and parent="Company"''',
- as_dict=True,
- )
-
# remove accounts data from company
- update_values = {d.fieldname: "" for d in linked}
+
+ fieldnames = get_linked_fields("Account").get("Company", {}).get("fieldname", [])
+ linked = [{"fieldname": name} for name in fieldnames]
+ update_values = {d.get("fieldname"): "" for d in linked}
frappe.db.set_value("Company", company, update_values, update_values)
# remove accounts data from various doctypes
diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/coa_sample_template.csv b/erpnext/accounts/doctype/chart_of_accounts_importer/coa_sample_template.csv
new file mode 100644
index 0000000..85a2f21
--- /dev/null
+++ b/erpnext/accounts/doctype/chart_of_accounts_importer/coa_sample_template.csv
@@ -0,0 +1,17 @@
+Application Of Funds(Assets),,,,1,,Asset
+Sources Of Funds(Liabilities),,,,1,,Liability
+Equity,,,,1,,Equity
+Expenses,,,,1,Expense Account,Expense
+Income,,,,1,Income Account,Income
+Bank Accounts,Application Of Funds(Assets),,,1,Bank,Asset
+Cash In Hand,Application Of Funds(Assets),,,1,Cash,Asset
+Stock Assets,Application Of Funds(Assets),,,1,Stock,Asset
+Cost Of Goods Sold,Expenses,,,0,Cost of Goods Sold,Expense
+Asset Depreciation,Expenses,,,0,Depreciation,Expense
+Fixed Assets,Application Of Funds(Assets),,,0,Fixed Asset,Asset
+Accounts Payable,Sources Of Funds(Liabilities),,,0,Payable,Liability
+Accounts Receivable,Application Of Funds(Assets),,,1,Receivable,Asset
+Stock Expenses,Expenses,,,0,Stock Adjustment,Expense
+Sample Bank,Bank Accounts,,,0,Bank,Asset
+Cash,Cash In Hand,,,0,Cash,Asset
+Stores,Stock Assets,,,0,Stock,Asset
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.js b/erpnext/accounts/doctype/payment_entry/payment_entry.js
index 0203c45..b3ae627 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.js
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.js
@@ -154,7 +154,7 @@
frm.events.set_dynamic_labels(frm);
frm.events.show_general_ledger(frm);
erpnext.accounts.ledger_preview.show_accounting_ledger_preview(frm);
- if(frm.doc.references.find((elem) => {return elem.exchange_gain_loss != 0})) {
+ if((frm.doc.references) && (frm.doc.references.find((elem) => {return elem.exchange_gain_loss != 0}))) {
frm.add_custom_button(__("View Exchange Gain/Loss Journals"), function() {
frappe.set_route("List", "Journal Entry", {"voucher_type": "Exchange Gain Or Loss", "reference_name": frm.doc.name});
}, __('Actions'));
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.json b/erpnext/accounts/doctype/payment_entry/payment_entry.json
index d7b6a19..4d50a35 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.json
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.json
@@ -595,6 +595,7 @@
"fieldname": "status",
"fieldtype": "Select",
"label": "Status",
+ "no_copy": 1,
"options": "\nDraft\nSubmitted\nCancelled",
"read_only": 1
},
@@ -750,7 +751,7 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2023-06-23 18:07:38.023010",
+ "modified": "2023-11-08 21:51:03.482709",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Entry",
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index e6403fd..fc22f53 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -33,6 +33,7 @@
get_account_currency,
get_balance_on,
get_outstanding_invoices,
+ get_party_types_from_account_type,
)
from erpnext.controllers.accounts_controller import (
AccountsController,
@@ -83,7 +84,6 @@
self.apply_taxes()
self.set_amounts_after_tax()
self.clear_unallocated_reference_document_rows()
- self.validate_payment_against_negative_invoice()
self.validate_transaction_reference()
self.set_title()
self.set_remarks()
@@ -952,35 +952,6 @@
self.name,
)
- def validate_payment_against_negative_invoice(self):
- if (self.payment_type != "Pay" or self.party_type != "Customer") and (
- self.payment_type != "Receive" or self.party_type != "Supplier"
- ):
- return
-
- total_negative_outstanding = sum(
- abs(flt(d.outstanding_amount)) for d in self.get("references") if flt(d.outstanding_amount) < 0
- )
-
- paid_amount = self.paid_amount if self.payment_type == "Receive" else self.received_amount
- additional_charges = sum(flt(d.amount) for d in self.deductions)
-
- if not total_negative_outstanding:
- if self.party_type == "Customer":
- msg = _("Cannot pay to Customer without any negative outstanding invoice")
- else:
- msg = _("Cannot receive from Supplier without any negative outstanding invoice")
-
- frappe.throw(msg, InvalidPaymentEntry)
-
- elif paid_amount - additional_charges > total_negative_outstanding:
- frappe.throw(
- _("Paid Amount cannot be greater than total negative outstanding amount {0}").format(
- fmt_money(total_negative_outstanding)
- ),
- InvalidPaymentEntry,
- )
-
def set_title(self):
if frappe.flags.in_import and self.title:
# do not set title dynamically if title exists during data import.
@@ -1083,9 +1054,7 @@
item=self,
)
- dr_or_cr = (
- "credit" if erpnext.get_party_account_type(self.party_type) == "Receivable" else "debit"
- )
+ dr_or_cr = "credit" if self.payment_type == "Receive" else "debit"
for d in self.get("references"):
cost_center = self.cost_center
@@ -1103,10 +1072,27 @@
against_voucher_type = d.reference_doctype
against_voucher = d.reference_name
+ reverse_dr_or_cr, standalone_note = 0, 0
+ if d.reference_doctype in ["Sales Invoice", "Purchase Invoice"]:
+ is_return, return_against = frappe.db.get_value(
+ d.reference_doctype, d.reference_name, ["is_return", "return_against"]
+ )
+ payable_party_types = get_party_types_from_account_type("Payable")
+ receivable_party_types = get_party_types_from_account_type("Receivable")
+ if is_return and self.party_type in receivable_party_types and (self.payment_type == "Pay"):
+ reverse_dr_or_cr = 1
+ elif (
+ is_return and self.party_type in payable_party_types and (self.payment_type == "Receive")
+ ):
+ reverse_dr_or_cr = 1
+
+ if is_return and not return_against and not reverse_dr_or_cr:
+ dr_or_cr = "debit" if dr_or_cr == "credit" else "credit"
+
gle.update(
{
- dr_or_cr: allocated_amount_in_company_currency,
- dr_or_cr + "_in_account_currency": d.allocated_amount,
+ dr_or_cr: abs(allocated_amount_in_company_currency),
+ dr_or_cr + "_in_account_currency": abs(d.allocated_amount),
"against_voucher_type": against_voucher_type,
"against_voucher": against_voucher,
"cost_center": cost_center,
@@ -1698,13 +1684,42 @@
return data
-def split_invoices_based_on_payment_terms(outstanding_invoices, company):
- invoice_ref_based_on_payment_terms = {}
+def split_invoices_based_on_payment_terms(outstanding_invoices, company) -> list:
+ """Split a list of invoices based on their payment terms."""
+ exc_rates = get_currency_data(outstanding_invoices, company)
+ outstanding_invoices_after_split = []
+ for entry in outstanding_invoices:
+ if entry.voucher_type in ["Sales Invoice", "Purchase Invoice"]:
+ if payment_term_template := frappe.db.get_value(
+ entry.voucher_type, entry.voucher_no, "payment_terms_template"
+ ):
+ split_rows = get_split_invoice_rows(entry, payment_term_template, exc_rates)
+ if not split_rows:
+ continue
+
+ frappe.msgprint(
+ _("Splitting {0} {1} into {2} rows as per Payment Terms").format(
+ _(entry.voucher_type), frappe.bold(entry.voucher_no), len(split_rows)
+ ),
+ alert=True,
+ )
+ outstanding_invoices_after_split += split_rows
+ continue
+
+ # If not an invoice or no payment terms template, add as it is
+ outstanding_invoices_after_split.append(entry)
+
+ return outstanding_invoices_after_split
+
+
+def get_currency_data(outstanding_invoices: list, company: str = None) -> dict:
+ """Get currency and conversion data for a list of invoices."""
+ exc_rates = frappe._dict()
company_currency = (
frappe.db.get_value("Company", company, "default_currency") if company else None
)
- exc_rates = frappe._dict()
+
for doctype in ["Sales Invoice", "Purchase Invoice"]:
invoices = [x.voucher_no for x in outstanding_invoices if x.voucher_type == doctype]
for x in frappe.db.get_all(
@@ -1719,72 +1734,54 @@
company_currency=company_currency,
)
- for idx, d in enumerate(outstanding_invoices):
- if d.voucher_type in ["Sales Invoice", "Purchase Invoice"]:
- payment_term_template = frappe.db.get_value(
- d.voucher_type, d.voucher_no, "payment_terms_template"
+ return exc_rates
+
+
+def get_split_invoice_rows(invoice: dict, payment_term_template: str, exc_rates: dict) -> list:
+ """Split invoice based on its payment schedule table."""
+ split_rows = []
+ allocate_payment_based_on_payment_terms = frappe.db.get_value(
+ "Payment Terms Template", payment_term_template, "allocate_payment_based_on_payment_terms"
+ )
+
+ if not allocate_payment_based_on_payment_terms:
+ return [invoice]
+
+ payment_schedule = frappe.get_all(
+ "Payment Schedule", filters={"parent": invoice.voucher_no}, fields=["*"], order_by="due_date"
+ )
+ for payment_term in payment_schedule:
+ if not payment_term.outstanding > 0.1:
+ continue
+
+ doc_details = exc_rates.get(payment_term.parent, None)
+ is_multi_currency_acc = (doc_details.currency != doc_details.company_currency) and (
+ doc_details.party_account_currency != doc_details.company_currency
+ )
+ payment_term_outstanding = flt(payment_term.outstanding)
+ if not is_multi_currency_acc:
+ payment_term_outstanding = doc_details.conversion_rate * flt(payment_term.outstanding)
+
+ split_rows.append(
+ frappe._dict(
+ {
+ "due_date": invoice.due_date,
+ "currency": invoice.currency,
+ "voucher_no": invoice.voucher_no,
+ "voucher_type": invoice.voucher_type,
+ "posting_date": invoice.posting_date,
+ "invoice_amount": flt(invoice.invoice_amount),
+ "outstanding_amount": payment_term_outstanding
+ if payment_term_outstanding
+ else invoice.outstanding_amount,
+ "payment_term_outstanding": payment_term_outstanding,
+ "payment_amount": payment_term.payment_amount,
+ "payment_term": payment_term.payment_term,
+ }
)
- if payment_term_template:
- allocate_payment_based_on_payment_terms = frappe.get_cached_value(
- "Payment Terms Template", payment_term_template, "allocate_payment_based_on_payment_terms"
- )
- if allocate_payment_based_on_payment_terms:
- payment_schedule = frappe.get_all(
- "Payment Schedule", filters={"parent": d.voucher_no}, fields=["*"]
- )
+ )
- for payment_term in payment_schedule:
- if payment_term.outstanding > 0.1:
- doc_details = exc_rates.get(payment_term.parent, None)
- is_multi_currency_acc = (doc_details.currency != doc_details.company_currency) and (
- doc_details.party_account_currency != doc_details.company_currency
- )
- payment_term_outstanding = flt(payment_term.outstanding)
- if not is_multi_currency_acc:
- payment_term_outstanding = doc_details.conversion_rate * flt(payment_term.outstanding)
-
- invoice_ref_based_on_payment_terms.setdefault(idx, [])
- invoice_ref_based_on_payment_terms[idx].append(
- frappe._dict(
- {
- "due_date": d.due_date,
- "currency": d.currency,
- "voucher_no": d.voucher_no,
- "voucher_type": d.voucher_type,
- "posting_date": d.posting_date,
- "invoice_amount": flt(d.invoice_amount),
- "outstanding_amount": payment_term_outstanding
- if payment_term_outstanding
- else d.outstanding_amount,
- "payment_term_outstanding": payment_term_outstanding,
- "payment_amount": payment_term.payment_amount,
- "payment_term": payment_term.payment_term,
- "account": d.account,
- }
- )
- )
-
- outstanding_invoices_after_split = []
- if invoice_ref_based_on_payment_terms:
- for idx, ref in invoice_ref_based_on_payment_terms.items():
- voucher_no = ref[0]["voucher_no"]
- voucher_type = ref[0]["voucher_type"]
-
- frappe.msgprint(
- _("Spliting {} {} into {} row(s) as per Payment Terms").format(
- voucher_type, voucher_no, len(ref)
- ),
- alert=True,
- )
-
- outstanding_invoices_after_split += invoice_ref_based_on_payment_terms[idx]
-
- existing_row = list(filter(lambda x: x.get("voucher_no") == voucher_no, outstanding_invoices))
- index = outstanding_invoices.index(existing_row[0])
- outstanding_invoices.pop(index)
-
- outstanding_invoices_after_split += outstanding_invoices
- return outstanding_invoices_after_split
+ return split_rows
def get_orders_to_be_billed(
diff --git a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
index edfec41..603f24a 100644
--- a/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/test_payment_entry.py
@@ -6,10 +6,11 @@
import frappe
from frappe import qb
from frappe.tests.utils import FrappeTestCase, change_settings
-from frappe.utils import flt, nowdate
+from frappe.utils import add_days, flt, nowdate
from erpnext.accounts.doctype.payment_entry.payment_entry import (
InvalidPaymentEntry,
+ get_outstanding_reference_documents,
get_payment_entry,
get_reference_details,
)
@@ -683,17 +684,6 @@
self.validate_gl_entries(pe.name, expected_gle)
def test_payment_against_negative_sales_invoice(self):
- pe1 = frappe.new_doc("Payment Entry")
- pe1.payment_type = "Pay"
- pe1.company = "_Test Company"
- pe1.party_type = "Customer"
- pe1.party = "_Test Customer"
- pe1.paid_from = "_Test Cash - _TC"
- pe1.paid_amount = 100
- pe1.received_amount = 100
-
- self.assertRaises(InvalidPaymentEntry, pe1.validate)
-
si1 = create_sales_invoice()
# create full payment entry against si1
@@ -751,8 +741,6 @@
# pay more than outstanding against si1
pe3 = get_payment_entry("Sales Invoice", si1.name, bank_account="_Test Cash - _TC")
- pe3.paid_amount = pe3.received_amount = 300
- self.assertRaises(InvalidPaymentEntry, pe3.validate)
# pay negative outstanding against si1
pe3.paid_to = "Debtors - _TC"
@@ -1262,6 +1250,78 @@
so.reload()
self.assertEqual(so.advance_paid, so.rounded_total)
+ def test_outstanding_invoices_api(self):
+ """
+ Test if `get_outstanding_reference_documents` fetches invoices in the right order.
+ """
+ customer = create_customer("Max Mustermann", "INR")
+ create_payment_terms_template()
+
+ # SI has an earlier due date and SI2 has a later due date
+ si = create_sales_invoice(
+ qty=1, rate=100, customer=customer, posting_date=add_days(nowdate(), -4)
+ )
+ si2 = create_sales_invoice(do_not_save=1, qty=1, rate=100, customer=customer)
+ si2.payment_terms_template = "Test Receivable Template"
+ si2.submit()
+
+ args = {
+ "posting_date": nowdate(),
+ "company": "_Test Company",
+ "party_type": "Customer",
+ "payment_type": "Pay",
+ "party": customer,
+ "party_account": "Debtors - _TC",
+ }
+ args.update(
+ {
+ "get_outstanding_invoices": True,
+ "from_posting_date": add_days(nowdate(), -4),
+ "to_posting_date": add_days(nowdate(), 2),
+ }
+ )
+ references = get_outstanding_reference_documents(args)
+
+ self.assertEqual(len(references), 3)
+ self.assertEqual(references[0].voucher_no, si.name)
+ self.assertEqual(references[1].voucher_no, si2.name)
+ self.assertEqual(references[2].voucher_no, si2.name)
+ self.assertEqual(references[1].payment_term, "Basic Amount Receivable")
+ self.assertEqual(references[2].payment_term, "Tax Receivable")
+
+ def test_receive_payment_from_payable_party_type(self):
+ pe = create_payment_entry(
+ party_type="Supplier",
+ party="_Test Supplier",
+ payment_type="Receive",
+ paid_from="Creditors - _TC",
+ paid_to="_Test Cash - _TC",
+ save=True,
+ submit=True,
+ )
+ self.voucher_no = pe.name
+ self.expected_gle = [
+ {"account": "_Test Cash - _TC", "debit": 1000.0, "credit": 0.0},
+ {"account": "Creditors - _TC", "debit": 0.0, "credit": 1000.0},
+ ]
+ self.check_gl_entries()
+
+ def check_gl_entries(self):
+ gle = frappe.qb.DocType("GL Entry")
+ gl_entries = (
+ frappe.qb.from_(gle)
+ .select(
+ gle.account,
+ gle.debit,
+ gle.credit,
+ )
+ .where((gle.voucher_no == self.voucher_no) & (gle.is_cancelled == 0))
+ .orderby(gle.account)
+ ).run(as_dict=True)
+ for row in range(len(self.expected_gle)):
+ for field in ["account", "debit", "credit"]:
+ self.assertEqual(self.expected_gle[row][field], gl_entries[row][field])
+
def create_payment_entry(**args):
payment_entry = frappe.new_doc("Payment Entry")
@@ -1322,6 +1382,9 @@
def create_payment_terms_template_with_discount(
name=None, discount_type=None, discount=None, template_name=None
):
+ """
+ Create a Payment Terms Template with % or amount discount.
+ """
create_payment_term(name or "30 Credit Days with 10% Discount")
template_name = template_name or "Test Discount Template"
diff --git a/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json b/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json
index 4ae8135..28c9529 100644
--- a/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json
+++ b/erpnext/accounts/doctype/payment_ledger_entry/payment_ledger_entry.json
@@ -65,7 +65,8 @@
"fieldtype": "Link",
"in_standard_filter": 1,
"label": "Voucher Type",
- "options": "DocType"
+ "options": "DocType",
+ "search_index": 1
},
{
"fieldname": "voucher_no",
@@ -73,14 +74,16 @@
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Voucher No",
- "options": "voucher_type"
+ "options": "voucher_type",
+ "search_index": 1
},
{
"fieldname": "against_voucher_type",
"fieldtype": "Link",
"in_standard_filter": 1,
"label": "Against Voucher Type",
- "options": "DocType"
+ "options": "DocType",
+ "search_index": 1
},
{
"fieldname": "against_voucher_no",
@@ -88,7 +91,8 @@
"in_list_view": 1,
"in_standard_filter": 1,
"label": "Against Voucher No",
- "options": "against_voucher_type"
+ "options": "against_voucher_type",
+ "search_index": 1
},
{
"fieldname": "amount",
@@ -148,13 +152,14 @@
{
"fieldname": "voucher_detail_no",
"fieldtype": "Data",
- "label": "Voucher Detail No"
+ "label": "Voucher Detail No",
+ "search_index": 1
}
],
"in_create": 1,
"index_web_pages_for_search": 1,
"links": [],
- "modified": "2023-10-30 16:15:00.470283",
+ "modified": "2023-11-03 16:39:58.904113",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Payment Ledger Entry",
diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
index 1626f25..43167be 100644
--- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
+++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py
@@ -109,6 +109,8 @@
"t2.against_account like %(bank_cash_account)s" if self.bank_cash_account else "1=1"
)
+ limit = f"limit {self.payment_limit}" if self.payment_limit else " "
+
# nosemgrep
journal_entries = frappe.db.sql(
"""
@@ -132,11 +134,13 @@
ELSE {bank_account_condition}
END)
order by t1.posting_date
+ {limit}
""".format(
**{
"dr_or_cr": dr_or_cr,
"bank_account_condition": bank_account_condition,
"condition": condition,
+ "limit": limit,
}
),
{
@@ -162,7 +166,7 @@
if self.payment_name:
conditions.append(doc.name.like(f"%{self.payment_name}%"))
- self.return_invoices = (
+ self.return_invoices_query = (
qb.from_(doc)
.select(
ConstantColumn(voucher_type).as_("voucher_type"),
@@ -170,8 +174,11 @@
doc.return_against,
)
.where(Criterion.all(conditions))
- .run(as_dict=True)
)
+ if self.payment_limit:
+ self.return_invoices_query = self.return_invoices_query.limit(self.payment_limit)
+
+ self.return_invoices = self.return_invoices_query.run(as_dict=True)
def get_dr_or_cr_notes(self):
diff --git a/erpnext/accounts/doctype/payment_request/payment_request.py b/erpnext/accounts/doctype/payment_request/payment_request.py
index f600424..5f7fa72 100644
--- a/erpnext/accounts/doctype/payment_request/payment_request.py
+++ b/erpnext/accounts/doctype/payment_request/payment_request.py
@@ -223,13 +223,6 @@
if self.payment_url:
self.db_set("payment_url", self.payment_url)
- if (
- self.payment_url
- or not self.payment_gateway_account
- or (self.payment_gateway_account and self.payment_channel == "Phone")
- ):
- self.db_set("status", "Initiated")
-
def get_payment_url(self):
if self.reference_doctype != "Fees":
data = frappe.db.get_value(
diff --git a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
index 982bdc1..200b82a 100644
--- a/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
+++ b/erpnext/accounts/doctype/pos_invoice/test_pos_invoice.py
@@ -6,7 +6,6 @@
import frappe
from frappe import _
-from frappe.utils import add_days, nowdate
from erpnext.accounts.doctype.pos_invoice.pos_invoice import make_sales_return
from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile
@@ -126,64 +125,70 @@
self.assertEqual(inv.grand_total, 5474.0)
def test_tax_calculation_with_item_tax_template(self):
- import json
+ inv = create_pos_invoice(qty=84, rate=4.6, do_not_save=1)
+ item_row = inv.get("items")[0]
- from erpnext.stock.get_item_details import get_item_details
-
- # set tax template in item
- item = frappe.get_cached_doc("Item", "_Test Item")
- item.set(
- "taxes",
- [
- {
- "item_tax_template": "_Test Account Excise Duty @ 15 - _TC",
- "valid_from": add_days(nowdate(), -5),
- }
- ],
- )
- item.save()
-
- # create POS invoice with item
- pos_inv = create_pos_invoice(qty=84, rate=4.6, do_not_save=True)
- item_details = get_item_details(
- doc=pos_inv,
- args={
- "item_code": item.item_code,
- "company": pos_inv.company,
- "doctype": "POS Invoice",
- "conversion_rate": 1.0,
- },
- )
- tax_map = json.loads(item_details.item_tax_rate)
- for tax in tax_map:
- pos_inv.append(
- "taxes",
- {
- "charge_type": "On Net Total",
- "account_head": tax,
- "rate": tax_map[tax],
- "description": "Test",
- "cost_center": "_Test Cost Center - _TC",
- },
- )
- pos_inv.submit()
- pos_inv.load_from_db()
-
- # check if correct tax values are applied from tax template
- self.assertEqual(pos_inv.net_total, 386.4)
-
- expected_taxes = [
- {
- "tax_amount": 57.96,
- "total": 444.36,
- },
+ add_items = [
+ (54, "_Test Account Excise Duty @ 12 - _TC"),
+ (288, "_Test Account Excise Duty @ 15 - _TC"),
+ (144, "_Test Account Excise Duty @ 20 - _TC"),
+ (430, "_Test Item Tax Template 1 - _TC"),
]
+ for qty, item_tax_template in add_items:
+ item_row_copy = copy.deepcopy(item_row)
+ item_row_copy.qty = qty
+ item_row_copy.item_tax_template = item_tax_template
+ inv.append("items", item_row_copy)
- for i in range(len(expected_taxes)):
- for key in expected_taxes[i]:
- self.assertEqual(expected_taxes[i][key], pos_inv.get("taxes")[i].get(key))
+ inv.append(
+ "taxes",
+ {
+ "account_head": "_Test Account Excise Duty - _TC",
+ "charge_type": "On Net Total",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "Excise Duty",
+ "doctype": "Sales Taxes and Charges",
+ "rate": 11,
+ },
+ )
+ inv.append(
+ "taxes",
+ {
+ "account_head": "_Test Account Education Cess - _TC",
+ "charge_type": "On Net Total",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "Education Cess",
+ "doctype": "Sales Taxes and Charges",
+ "rate": 0,
+ },
+ )
+ inv.append(
+ "taxes",
+ {
+ "account_head": "_Test Account S&H Education Cess - _TC",
+ "charge_type": "On Net Total",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "S&H Education Cess",
+ "doctype": "Sales Taxes and Charges",
+ "rate": 3,
+ },
+ )
+ inv.insert()
- self.assertEqual(pos_inv.get("base_total_taxes_and_charges"), 57.96)
+ self.assertEqual(inv.net_total, 4600)
+
+ self.assertEqual(inv.get("taxes")[0].tax_amount, 502.41)
+ self.assertEqual(inv.get("taxes")[0].total, 5102.41)
+
+ self.assertEqual(inv.get("taxes")[1].tax_amount, 197.80)
+ self.assertEqual(inv.get("taxes")[1].total, 5300.21)
+
+ self.assertEqual(inv.get("taxes")[2].tax_amount, 375.36)
+ self.assertEqual(inv.get("taxes")[2].total, 5675.57)
+
+ self.assertEqual(inv.grand_total, 5675.57)
+ self.assertEqual(inv.rounding_adjustment, 0.43)
+ self.assertEqual(inv.rounded_total, 5676.0)
def test_tax_calculation_with_multiple_items_and_discount(self):
inv = create_pos_invoice(qty=1, rate=75, do_not_save=True)
diff --git a/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log.json b/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log.json
index 1131a0f..b4ac981 100644
--- a/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log.json
+++ b/erpnext/accounts/doctype/process_payment_reconciliation_log/process_payment_reconciliation_log.json
@@ -110,7 +110,7 @@
"in_create": 1,
"index_web_pages_for_search": 1,
"links": [],
- "modified": "2023-04-21 17:36:26.642617",
+ "modified": "2023-11-02 11:32:12.254018",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Process Payment Reconciliation Log",
@@ -125,7 +125,19 @@
"print": 1,
"read": 1,
"report": 1,
- "role": "System Manager",
+ "role": "Accounts Manager",
+ "share": 1,
+ "write": 1
+ },
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Accounts User",
"share": 1,
"write": 1
}
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
index 2d1f445..09bffff 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
@@ -384,7 +384,8 @@
"label": "Supplier Invoice No",
"oldfieldname": "bill_no",
"oldfieldtype": "Data",
- "print_hide": 1
+ "print_hide": 1,
+ "search_index": 1
},
{
"fieldname": "column_break_15",
@@ -407,7 +408,8 @@
"no_copy": 1,
"options": "Purchase Invoice",
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "search_index": 1
},
{
"fieldname": "section_addresses",
@@ -1602,7 +1604,7 @@
"idx": 204,
"is_submittable": 1,
"links": [],
- "modified": "2023-10-16 16:24:51.886231",
+ "modified": "2023-11-03 15:47:30.319200",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Purchase Invoice",
@@ -1665,4 +1667,4 @@
"timeline_field": "supplier",
"title_field": "title",
"track_changes": 1
-}
\ No newline at end of file
+}
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index 13593bc..171cc0c 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -1783,9 +1783,14 @@
set_advance_flag(company="_Test Company", flag=0, default_account="")
def test_gl_entries_for_standalone_debit_note(self):
- make_purchase_invoice(qty=5, rate=500, update_stock=True)
+ from erpnext.stock.doctype.item.test_item import make_item
- returned_inv = make_purchase_invoice(qty=-5, rate=5, update_stock=True, is_return=True)
+ item_code = make_item(properties={"is_stock_item": 1})
+ make_purchase_invoice(item_code=item_code, qty=5, rate=500, update_stock=True)
+
+ returned_inv = make_purchase_invoice(
+ item_code=item_code, qty=-5, rate=5, update_stock=True, is_return=True
+ )
# override the rate with valuation rate
sle = frappe.get_all(
@@ -1795,7 +1800,7 @@
)[0]
rate = flt(sle.stock_value_difference) / flt(sle.actual_qty)
- self.assertAlmostEqual(returned_inv.items[0].rate, rate)
+ self.assertAlmostEqual(rate, 500)
def test_payment_allocation_for_payment_terms(self):
from erpnext.buying.doctype.purchase_order.test_purchase_order import (
@@ -1898,6 +1903,12 @@
disable_dimension()
def test_repost_accounting_entries(self):
+ # update repost settings
+ settings = frappe.get_doc("Repost Accounting Ledger Settings")
+ if not [x for x in settings.allowed_types if x.document_type == "Purchase Invoice"]:
+ settings.append("allowed_types", {"document_type": "Purchase Invoice", "allowed": True})
+ settings.save()
+
pi = make_purchase_invoice(
rate=1000,
price_list_rate=1000,
diff --git a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.js b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.js
index 3a87a38..c7b7a14 100644
--- a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.js
+++ b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.js
@@ -5,9 +5,7 @@
setup: function(frm) {
frm.fields_dict['vouchers'].grid.get_field('voucher_type').get_query = function(doc) {
return {
- filters: {
- name: ['in', ['Purchase Invoice', 'Sales Invoice', 'Payment Entry', 'Journal Entry']],
- }
+ query: "erpnext.accounts.doctype.repost_accounting_ledger.repost_accounting_ledger.get_repost_allowed_types"
}
}
diff --git a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py
index dbb0971..69cfe9f 100644
--- a/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py
+++ b/erpnext/accounts/doctype/repost_accounting_ledger/repost_accounting_ledger.py
@@ -10,9 +10,12 @@
class RepostAccountingLedger(Document):
def __init__(self, *args, **kwargs):
super(RepostAccountingLedger, self).__init__(*args, **kwargs)
- self._allowed_types = set(
- ["Purchase Invoice", "Sales Invoice", "Payment Entry", "Journal Entry"]
- )
+ self._allowed_types = [
+ x.document_type
+ for x in frappe.db.get_all(
+ "Repost Allowed Types", filters={"allowed": True}, fields=["distinct(document_type)"]
+ )
+ ]
def validate(self):
self.validate_vouchers()
@@ -157,7 +160,7 @@
doc.docstatus = 1
doc.make_gl_entries()
- elif doc.doctype in ["Payment Entry", "Journal Entry"]:
+ elif doc.doctype in ["Payment Entry", "Journal Entry", "Expense Claim"]:
if not repost_doc.delete_cancelled_entries:
doc.make_gl_entries(1)
doc.make_gl_entries()
@@ -186,3 +189,18 @@
frappe.bold(comma_and([x[0] for x in docs_with_deferred_expense + docs_with_deferred_revenue]))
)
)
+
+
+@frappe.whitelist()
+@frappe.validate_and_sanitize_search_inputs
+def get_repost_allowed_types(doctype, txt, searchfield, start, page_len, filters):
+ filters = {"allowed": True}
+
+ if txt:
+ filters.update({"document_type": ("like", f"%{txt}%")})
+
+ if allowed_types := frappe.db.get_all(
+ "Repost Allowed Types", filters=filters, fields=["distinct(document_type)"], as_list=1
+ ):
+ return allowed_types
+ return []
diff --git a/erpnext/accounts/doctype/repost_accounting_ledger/test_repost_accounting_ledger.py b/erpnext/accounts/doctype/repost_accounting_ledger/test_repost_accounting_ledger.py
index 0e75dd2..dda0ec7 100644
--- a/erpnext/accounts/doctype/repost_accounting_ledger/test_repost_accounting_ledger.py
+++ b/erpnext/accounts/doctype/repost_accounting_ledger/test_repost_accounting_ledger.py
@@ -20,10 +20,18 @@
self.create_company()
self.create_customer()
self.create_item()
+ self.update_repost_settings()
def teadDown(self):
frappe.db.rollback()
+ def update_repost_settings(self):
+ allowed_types = ["Sales Invoice", "Purchase Invoice", "Payment Entry", "Journal Entry"]
+ repost_settings = frappe.get_doc("Repost Accounting Ledger Settings")
+ for x in allowed_types:
+ repost_settings.append("allowed_types", {"document_type": x, "allowed": True})
+ repost_settings.save()
+
def test_01_basic_functions(self):
si = create_sales_invoice(
item=self.item,
diff --git a/erpnext/projects/report/employee_billing_summary/__init__.py b/erpnext/accounts/doctype/repost_accounting_ledger_settings/__init__.py
similarity index 100%
copy from erpnext/projects/report/employee_billing_summary/__init__.py
copy to erpnext/accounts/doctype/repost_accounting_ledger_settings/__init__.py
diff --git a/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.js b/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.js
new file mode 100644
index 0000000..8c83ca5
--- /dev/null
+++ b/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.js
@@ -0,0 +1,8 @@
+// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+// frappe.ui.form.on("Repost Accounting Ledger Settings", {
+// refresh(frm) {
+
+// },
+// });
diff --git a/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.json b/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.json
new file mode 100644
index 0000000..8aa0a84
--- /dev/null
+++ b/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.json
@@ -0,0 +1,46 @@
+{
+ "actions": [],
+ "creation": "2023-11-07 09:57:20.619939",
+ "doctype": "DocType",
+ "engine": "InnoDB",
+ "field_order": [
+ "allowed_types"
+ ],
+ "fields": [
+ {
+ "fieldname": "allowed_types",
+ "fieldtype": "Table",
+ "label": "Allowed Doctypes",
+ "options": "Repost Allowed Types"
+ }
+ ],
+ "in_create": 1,
+ "issingle": 1,
+ "links": [],
+ "modified": "2023-11-07 14:24:13.321522",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Repost Accounting Ledger Settings",
+ "owner": "Administrator",
+ "permissions": [
+ {
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "print": 1,
+ "read": 1,
+ "role": "Administrator",
+ "share": 1,
+ "write": 1
+ },
+ {
+ "read": 1,
+ "role": "System Manager",
+ "select": 1
+ }
+ ],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "states": [],
+ "track_changes": 1
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.py b/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.py
new file mode 100644
index 0000000..2b8230d
--- /dev/null
+++ b/erpnext/accounts/doctype/repost_accounting_ledger_settings/repost_accounting_ledger_settings.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class RepostAccountingLedgerSettings(Document):
+ pass
diff --git a/erpnext/accounts/doctype/repost_accounting_ledger_settings/test_repost_accounting_ledger_settings.py b/erpnext/accounts/doctype/repost_accounting_ledger_settings/test_repost_accounting_ledger_settings.py
new file mode 100644
index 0000000..ec4e87f
--- /dev/null
+++ b/erpnext/accounts/doctype/repost_accounting_ledger_settings/test_repost_accounting_ledger_settings.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
+# See license.txt
+
+# import frappe
+from frappe.tests.utils import FrappeTestCase
+
+
+class TestRepostAccountingLedgerSettings(FrappeTestCase):
+ pass
diff --git a/erpnext/projects/report/employee_billing_summary/__init__.py b/erpnext/accounts/doctype/repost_allowed_types/__init__.py
similarity index 100%
rename from erpnext/projects/report/employee_billing_summary/__init__.py
rename to erpnext/accounts/doctype/repost_allowed_types/__init__.py
diff --git a/erpnext/accounts/doctype/repost_allowed_types/repost_allowed_types.json b/erpnext/accounts/doctype/repost_allowed_types/repost_allowed_types.json
new file mode 100644
index 0000000..ede12fb
--- /dev/null
+++ b/erpnext/accounts/doctype/repost_allowed_types/repost_allowed_types.json
@@ -0,0 +1,45 @@
+{
+ "actions": [],
+ "allow_rename": 1,
+ "creation": "2023-11-07 09:58:03.595382",
+ "doctype": "DocType",
+ "editable_grid": 1,
+ "engine": "InnoDB",
+ "field_order": [
+ "document_type",
+ "column_break_sfzb",
+ "allowed"
+ ],
+ "fields": [
+ {
+ "fieldname": "document_type",
+ "fieldtype": "Link",
+ "in_list_view": 1,
+ "label": "Doctype",
+ "options": "DocType"
+ },
+ {
+ "default": "0",
+ "fieldname": "allowed",
+ "fieldtype": "Check",
+ "in_list_view": 1,
+ "label": "Allowed"
+ },
+ {
+ "fieldname": "column_break_sfzb",
+ "fieldtype": "Column Break"
+ }
+ ],
+ "index_web_pages_for_search": 1,
+ "istable": 1,
+ "links": [],
+ "modified": "2023-11-07 10:01:39.217861",
+ "modified_by": "Administrator",
+ "module": "Accounts",
+ "name": "Repost Allowed Types",
+ "owner": "Administrator",
+ "permissions": [],
+ "sort_field": "modified",
+ "sort_order": "DESC",
+ "states": []
+}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/repost_allowed_types/repost_allowed_types.py b/erpnext/accounts/doctype/repost_allowed_types/repost_allowed_types.py
new file mode 100644
index 0000000..0e4883b
--- /dev/null
+++ b/erpnext/accounts/doctype/repost_allowed_types/repost_allowed_types.py
@@ -0,0 +1,9 @@
+# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
+# For license information, please see license.txt
+
+# import frappe
+from frappe.model.document import Document
+
+
+class RepostAllowedTypes(Document):
+ pass
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index d4d9239..b1a7b10 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -187,7 +187,6 @@
erpnext.accounts.unreconcile_payments.add_unreconcile_btn(me.frm);
}
-
make_maintenance_schedule() {
frappe.model.open_mapped_doc({
method: "erpnext.accounts.doctype.sales_invoice.sales_invoice.make_maintenance_schedule",
@@ -563,15 +562,6 @@
}
}
-// Income Account in Details Table
-// --------------------------------
-cur_frm.set_query("income_account", "items", function(doc) {
- return{
- query: "erpnext.controllers.queries.get_income_account",
- filters: {'company': doc.company}
- }
-});
-
// Cost Center in Details Table
// -----------------------------
cur_frm.fields_dict["items"].grid.get_field("cost_center").get_query = function(doc) {
@@ -666,6 +656,16 @@
};
});
+ frm.set_query("income_account", "items", function() {
+ return{
+ query: "erpnext.controllers.queries.get_income_account",
+ filters: {
+ 'company': frm.doc.company,
+ "disabled": 0
+ }
+ }
+ });
+
frm.custom_make_buttons = {
'Delivery Note': 'Delivery',
'Sales Invoice': 'Return / Credit Note',
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
index e5adeae..cd725b9 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json
@@ -26,6 +26,7 @@
"is_return",
"return_against",
"update_billed_amount_in_sales_order",
+ "update_billed_amount_in_delivery_note",
"is_debit_note",
"amended_from",
"accounting_dimensions_section",
@@ -2153,6 +2154,13 @@
"fieldname": "use_company_roundoff_cost_center",
"fieldtype": "Check",
"label": "Use Company default Cost Center for Round off"
+ },
+ {
+ "default": "0",
+ "depends_on": "eval: doc.is_return",
+ "fieldname": "update_billed_amount_in_delivery_note",
+ "fieldtype": "Check",
+ "label": "Update Billed Amount in Delivery Note"
}
],
"icon": "fa fa-file-text",
@@ -2165,7 +2173,7 @@
"link_fieldname": "consolidated_invoice"
}
],
- "modified": "2023-07-25 16:02:18.988799",
+ "modified": "2023-11-03 14:39:38.012346",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Sales Invoice",
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index f6d9c93..fa95ccd 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -253,6 +253,7 @@
self.update_status_updater_args()
self.update_prevdoc_status()
+
self.update_billing_status_in_dn()
self.clear_unallocated_mode_of_payments()
@@ -1019,7 +1020,7 @@
def make_customer_gl_entry(self, gl_entries):
# Checked both rounding_adjustment and rounded_total
- # because rounded_total had value even before introcution of posting GLE based on rounded total
+ # because rounded_total had value even before introduction of posting GLE based on rounded total
grand_total = (
self.rounded_total if (self.rounding_adjustment and self.rounded_total) else self.grand_total
)
@@ -1267,7 +1268,7 @@
if skip_change_gl_entries and payment_mode.account == self.account_for_change_amount:
payment_mode.base_amount -= flt(self.change_amount)
- if payment_mode.amount:
+ if payment_mode.base_amount:
# POS, make payment entries
gl_entries.append(
self.get_gl_dict(
@@ -1429,6 +1430,8 @@
)
def update_billing_status_in_dn(self, update_modified=True):
+ if self.is_return and not self.update_billed_amount_in_delivery_note:
+ return
updated_delivery_notes = []
for d in self.get("items"):
if d.dn_detail:
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index 21cc253..5a41336 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -516,72 +516,70 @@
self.assertEqual(si.grand_total, 5474.0)
def test_tax_calculation_with_item_tax_template(self):
- import json
-
- from erpnext.stock.get_item_details import get_item_details
-
- # set tax template in item
- item = frappe.get_cached_doc("Item", "_Test Item")
- item.set(
- "taxes",
- [
- {
- "item_tax_template": "_Test Item Tax Template 1 - _TC",
- "valid_from": add_days(nowdate(), -5),
- }
- ],
- )
- item.save()
-
- # create sales invoice with item
si = create_sales_invoice(qty=84, rate=4.6, do_not_save=True)
- item_details = get_item_details(
- doc=si,
- args={
- "item_code": item.item_code,
- "company": si.company,
- "doctype": "Sales Invoice",
- "conversion_rate": 1.0,
+ item_row = si.get("items")[0]
+
+ add_items = [
+ (54, "_Test Account Excise Duty @ 12 - _TC"),
+ (288, "_Test Account Excise Duty @ 15 - _TC"),
+ (144, "_Test Account Excise Duty @ 20 - _TC"),
+ (430, "_Test Item Tax Template 1 - _TC"),
+ ]
+ for qty, item_tax_template in add_items:
+ item_row_copy = copy.deepcopy(item_row)
+ item_row_copy.qty = qty
+ item_row_copy.item_tax_template = item_tax_template
+ si.append("items", item_row_copy)
+
+ si.append(
+ "taxes",
+ {
+ "account_head": "_Test Account Excise Duty - _TC",
+ "charge_type": "On Net Total",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "Excise Duty",
+ "doctype": "Sales Taxes and Charges",
+ "rate": 11,
},
)
- tax_map = json.loads(item_details.item_tax_rate)
- for tax in tax_map:
- si.append(
- "taxes",
- {
- "charge_type": "On Net Total",
- "account_head": tax,
- "rate": tax_map[tax],
- "description": "Test",
- "cost_center": "_Test Cost Center - _TC",
- },
- )
- si.submit()
- si.load_from_db()
-
- # check if correct tax values are applied from tax template
- self.assertEqual(si.net_total, 386.4)
-
- expected_taxes = [
+ si.append(
+ "taxes",
{
- "tax_amount": 19.32,
- "total": 405.72,
+ "account_head": "_Test Account Education Cess - _TC",
+ "charge_type": "On Net Total",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "Education Cess",
+ "doctype": "Sales Taxes and Charges",
+ "rate": 0,
},
+ )
+ si.append(
+ "taxes",
{
- "tax_amount": 38.64,
- "total": 444.36,
+ "account_head": "_Test Account S&H Education Cess - _TC",
+ "charge_type": "On Net Total",
+ "cost_center": "_Test Cost Center - _TC",
+ "description": "S&H Education Cess",
+ "doctype": "Sales Taxes and Charges",
+ "rate": 3,
},
- {
- "tax_amount": 57.96,
- "total": 502.32,
- },
- ]
+ )
+ si.insert()
- for i in range(len(expected_taxes)):
- for key in expected_taxes[i]:
- self.assertEqual(expected_taxes[i][key], si.get("taxes")[i].get(key))
+ self.assertEqual(si.net_total, 4600)
- self.assertEqual(si.get("base_total_taxes_and_charges"), 115.92)
+ self.assertEqual(si.get("taxes")[0].tax_amount, 502.41)
+ self.assertEqual(si.get("taxes")[0].total, 5102.41)
+
+ self.assertEqual(si.get("taxes")[1].tax_amount, 197.80)
+ self.assertEqual(si.get("taxes")[1].total, 5300.21)
+
+ self.assertEqual(si.get("taxes")[2].tax_amount, 375.36)
+ self.assertEqual(si.get("taxes")[2].total, 5675.57)
+
+ self.assertEqual(si.grand_total, 5675.57)
+ self.assertEqual(si.rounding_adjustment, 0.43)
+ self.assertEqual(si.rounded_total, 5676.0)
def test_tax_calculation_with_multiple_items_and_discount(self):
si = create_sales_invoice(qty=1, rate=75, do_not_save=True)
diff --git a/erpnext/accounts/party.py b/erpnext/accounts/party.py
index 16e73ea..371474e 100644
--- a/erpnext/accounts/party.py
+++ b/erpnext/accounts/party.py
@@ -31,7 +31,12 @@
from erpnext.exceptions import InvalidAccountCurrency, PartyDisabled, PartyFrozen
from erpnext.utilities.regional import temporary_flag
-PURCHASE_TRANSACTION_TYPES = {"Purchase Order", "Purchase Receipt", "Purchase Invoice"}
+PURCHASE_TRANSACTION_TYPES = {
+ "Supplier Quotation",
+ "Purchase Order",
+ "Purchase Receipt",
+ "Purchase Invoice",
+}
SALES_TRANSACTION_TYPES = {
"Quotation",
"Sales Order",
diff --git a/erpnext/accounts/report/accounts_payable/accounts_payable.js b/erpnext/accounts/report/accounts_payable/accounts_payable.js
index 9c73cbb..10362db 100644
--- a/erpnext/accounts/report/accounts_payable/accounts_payable.js
+++ b/erpnext/accounts/report/accounts_payable/accounts_payable.js
@@ -143,7 +143,18 @@
"fieldname": "show_future_payments",
"label": __("Show Future Payments"),
"fieldtype": "Check",
+ },
+ {
+ "fieldname": "in_party_currency",
+ "label": __("In Party Currency"),
+ "fieldtype": "Check",
+ },
+ {
+ "fieldname": "ignore_accounts",
+ "label": __("Group by Voucher"),
+ "fieldtype": "Check",
}
+
],
"formatter": function(value, row, column, data, default_formatter) {
@@ -175,4 +186,4 @@
});
});
return options;
-}
\ No newline at end of file
+}
diff --git a/erpnext/accounts/report/accounts_payable/test_accounts_payable.py b/erpnext/accounts/report/accounts_payable/test_accounts_payable.py
index 9f03d92..b4cb25f 100644
--- a/erpnext/accounts/report/accounts_payable/test_accounts_payable.py
+++ b/erpnext/accounts/report/accounts_payable/test_accounts_payable.py
@@ -40,6 +40,7 @@
"range2": 60,
"range3": 90,
"range4": 120,
+ "in_party_currency": 1,
}
data = execute(filters)
diff --git a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js
index 9e575e6..0f206b1 100644
--- a/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js
+++ b/erpnext/accounts/report/accounts_payable_summary/accounts_payable_summary.js
@@ -110,6 +110,11 @@
"fieldname":"based_on_payment_terms",
"label": __("Based On Payment Terms"),
"fieldtype": "Check",
+ },
+ {
+ "fieldname": "for_revaluation_journals",
+ "label": __("Revaluation Journals"),
+ "fieldtype": "Check",
}
],
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js
index 1073be0..b4bc887 100644
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js
@@ -114,10 +114,13 @@
"reqd": 1
},
{
- "fieldname": "customer_group",
+ "fieldname":"customer_group",
"label": __("Customer Group"),
- "fieldtype": "Link",
- "options": "Customer Group"
+ "fieldtype": "MultiSelectList",
+ "options": "Customer Group",
+ get_data: function(txt) {
+ return frappe.db.get_link_options('Customer Group', txt);
+ }
},
{
"fieldname": "payment_terms_template",
@@ -172,7 +175,24 @@
"fieldname": "show_remarks",
"label": __("Show Remarks"),
"fieldtype": "Check",
+ },
+ {
+ "fieldname": "in_party_currency",
+ "label": __("In Party Currency"),
+ "fieldtype": "Check",
+ },
+ {
+ "fieldname": "for_revaluation_journals",
+ "label": __("Revaluation Journals"),
+ "fieldtype": "Check",
+ },
+ {
+ "fieldname": "ignore_accounts",
+ "label": __("Group by Voucher"),
+ "fieldtype": "Check",
}
+
+
],
"formatter": function(value, row, column, data, default_formatter) {
@@ -205,4 +225,4 @@
});
});
return options;
-}
\ No newline at end of file
+}
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
old mode 100755
new mode 100644
index b9c7a0b..706d743
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
@@ -14,7 +14,7 @@
get_accounting_dimensions,
get_dimension_with_children,
)
-from erpnext.accounts.utils import get_currency_precision
+from erpnext.accounts.utils import get_currency_precision, get_party_types_from_account_type
# This report gives a summary of all Outstanding Invoices considering the following
@@ -28,8 +28,8 @@
# 6. Configurable Ageing Groups (0-30, 30-60 etc) can be set via filters
# 7. For overpayment against an invoice with payment terms, there will be an additional row
# 8. Invoice details like Sales Persons, Delivery Notes are also fetched comma separated
-# 9. Report amounts are in "Party Currency" if party is selected, or company currency for multi-party
-# 10. This reports is based on all GL Entries that are made against account_type "Receivable" or "Payable"
+# 9. Report amounts are in party currency if in_party_currency is selected, otherwise company currency
+# 10. This report is based on Payment Ledger Entries
def execute(filters=None):
@@ -72,9 +72,7 @@
self.currency_precision = get_currency_precision() or 2
self.dr_or_cr = "debit" if self.filters.account_type == "Receivable" else "credit"
self.account_type = self.filters.account_type
- self.party_type = frappe.db.get_all(
- "Party Type", {"account_type": self.account_type}, pluck="name"
- )
+ self.party_type = get_party_types_from_account_type(self.account_type)
self.party_details = {}
self.invoices = set()
self.skip_total_row = 0
@@ -84,6 +82,9 @@
self.total_row_map = {}
self.skip_total_row = 1
+ if self.filters.get("in_party_currency"):
+ self.skip_total_row = 1
+
def get_data(self):
self.get_ple_entries()
self.get_sales_invoices_or_customers_based_on_sales_person()
@@ -116,7 +117,12 @@
# build all keys, since we want to exclude vouchers beyond the report date
for ple in self.ple_entries:
# get the balance object for voucher_type
- key = (ple.account, ple.voucher_type, ple.voucher_no, ple.party)
+
+ if self.filters.get("ignore_accounts"):
+ key = (ple.voucher_type, ple.voucher_no, ple.party)
+ else:
+ key = (ple.account, ple.voucher_type, ple.voucher_no, ple.party)
+
if not key in self.voucher_balance:
self.voucher_balance[key] = frappe._dict(
voucher_type=ple.voucher_type,
@@ -140,7 +146,7 @@
if self.filters.get("group_by_party"):
self.init_subtotal_row(ple.party)
- if self.filters.get("group_by_party"):
+ if self.filters.get("group_by_party") and not self.filters.get("in_party_currency"):
self.init_subtotal_row("Total")
def get_invoices(self, ple):
@@ -183,7 +189,10 @@
):
return
- key = (ple.account, ple.against_voucher_type, ple.against_voucher_no, ple.party)
+ if self.filters.get("ignore_accounts"):
+ key = (ple.against_voucher_type, ple.against_voucher_no, ple.party)
+ else:
+ key = (ple.account, ple.against_voucher_type, ple.against_voucher_no, ple.party)
# If payment is made against credit note
# and credit note is made against a Sales Invoice
@@ -192,13 +201,19 @@
if ple.against_voucher_no in self.return_entries:
return_against = self.return_entries.get(ple.against_voucher_no)
if return_against:
- key = (ple.account, ple.against_voucher_type, return_against, ple.party)
+ if self.filters.get("ignore_accounts"):
+ key = (ple.against_voucher_type, return_against, ple.party)
+ else:
+ key = (ple.account, ple.against_voucher_type, return_against, ple.party)
row = self.voucher_balance.get(key)
if not row:
# no invoice, this is an invoice / stand-alone payment / credit note
- row = self.voucher_balance.get((ple.account, ple.voucher_type, ple.voucher_no, ple.party))
+ if self.filters.get("ignore_accounts"):
+ row = self.voucher_balance.get((ple.voucher_type, ple.voucher_no, ple.party))
+ else:
+ row = self.voucher_balance.get((ple.account, ple.voucher_type, ple.voucher_no, ple.party))
row.party_type = ple.party_type
return row
@@ -210,8 +225,7 @@
if not row:
return
- # amount in "Party Currency", if its supplied. If not, amount in company currency
- if self.filters.get("party_type") and self.filters.get("party"):
+ if self.filters.get("in_party_currency"):
amount = ple.amount_in_account_currency
else:
amount = ple.amount
@@ -242,8 +256,10 @@
def update_sub_total_row(self, row, party):
total_row = self.total_row_map.get(party)
- for field in self.get_currency_fields():
- total_row[field] += row.get(field, 0.0)
+ if total_row:
+ for field in self.get_currency_fields():
+ total_row[field] += row.get(field, 0.0)
+ total_row["currency"] = row.get("currency", "")
def append_subtotal_row(self, party):
sub_total_row = self.total_row_map.get(party)
@@ -267,11 +283,20 @@
row.invoice_grand_total = row.invoiced
- if (abs(row.outstanding) > 1.0 / 10**self.currency_precision) and (
- (abs(row.outstanding_in_account_currency) > 1.0 / 10**self.currency_precision)
- or (row.voucher_no in self.err_journals)
- ):
+ must_consider = False
+ if self.filters.get("for_revaluation_journals"):
+ if (abs(row.outstanding) > 1.0 / 10**self.currency_precision) or (
+ (abs(row.outstanding_in_account_currency) > 1.0 / 10**self.currency_precision)
+ ):
+ must_consider = True
+ else:
+ if (abs(row.outstanding) > 1.0 / 10**self.currency_precision) and (
+ (abs(row.outstanding_in_account_currency) > 1.0 / 10**self.currency_precision)
+ or (row.voucher_no in self.err_journals)
+ ):
+ must_consider = True
+ if must_consider:
# non-zero oustanding, we must consider this row
if self.is_invoice(row) and self.filters.based_on_payment_terms:
@@ -295,7 +320,7 @@
if self.filters.get("group_by_party"):
self.append_subtotal_row(self.previous_party)
if self.data:
- self.data.append(self.total_row_map.get("Total"))
+ self.data.append(self.total_row_map.get("Total", {}))
def append_row(self, row):
self.allocate_future_payments(row)
@@ -426,7 +451,7 @@
party_details = self.get_party_details(row.party) or {}
row.update(party_details)
- if self.filters.get("party_type") and self.filters.get("party"):
+ if self.filters.get("in_party_currency"):
row.currency = row.account_currency
else:
row.currency = self.company_currency
@@ -718,6 +743,7 @@
query = (
qb.from_(ple)
.select(
+ ple.name,
ple.account,
ple.voucher_type,
ple.voucher_no,
@@ -731,13 +757,15 @@
ple.account_currency,
ple.amount,
ple.amount_in_account_currency,
- ple.remarks,
)
.where(ple.delinked == 0)
.where(Criterion.all(self.qb_selection_filter))
.where(Criterion.any(self.or_filters))
)
+ if self.filters.get("show_remarks"):
+ query = query.select(ple.remarks)
+
if self.filters.get("group_by_party"):
query = query.orderby(self.ple.party, self.ple.posting_date)
else:
@@ -823,7 +851,13 @@
self.customer = qb.DocType("Customer")
if self.filters.get("customer_group"):
- self.get_hierarchical_filters("Customer Group", "customer_group")
+ groups = get_customer_group_with_children(self.filters.customer_group)
+ customers = (
+ qb.from_(self.customer)
+ .select(self.customer.name)
+ .where(self.customer["customer_group"].isin(groups))
+ )
+ self.qb_selection_filter.append(self.ple.party.isin(customers))
if self.filters.get("territory"):
self.get_hierarchical_filters("Territory", "territory")
@@ -1115,3 +1149,19 @@
.run()
)
self.err_journals = [x[0] for x in results] if results else []
+
+
+def get_customer_group_with_children(customer_groups):
+ if not isinstance(customer_groups, list):
+ customer_groups = [d.strip() for d in customer_groups.strip().split(",") if d]
+
+ all_customer_groups = []
+ for d in customer_groups:
+ if frappe.db.exists("Customer Group", d):
+ lft, rgt = frappe.db.get_value("Customer Group", d, ["lft", "rgt"])
+ children = frappe.get_all("Customer Group", filters={"lft": [">=", lft], "rgt": ["<=", rgt]})
+ all_customer_groups += [c.name for c in children]
+ else:
+ frappe.throw(_("Customer Group: {0} does not exist").format(d))
+
+ return list(set(all_customer_groups))
diff --git a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py
index cbeb6d3..dd0842d 100644
--- a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py
@@ -475,6 +475,30 @@
report = execute(filters)[1]
self.assertEqual(len(report), 0)
+ def test_multi_customer_group_filter(self):
+ si = self.create_sales_invoice()
+ cus_group = frappe.db.get_value("Customer", self.customer, "customer_group")
+ # Create a list of customer groups, e.g., ["Group1", "Group2"]
+ cus_groups_list = [cus_group, "_Test Customer Group 1"]
+
+ filters = {
+ "company": self.company,
+ "report_date": today(),
+ "range1": 30,
+ "range2": 60,
+ "range3": 90,
+ "range4": 120,
+ "customer_group": cus_groups_list, # Use the list of customer groups
+ }
+ report = execute(filters)[1]
+
+ # Assert that the report contains data for the specified customer groups
+ self.assertTrue(len(report) > 0)
+
+ for row in report:
+ # Assert that the customer group of each row is in the list of customer groups
+ self.assertIn(row.customer_group, cus_groups_list)
+
def test_party_account_filter(self):
si1 = self.create_sales_invoice()
self.customer2 = (
@@ -557,6 +581,7 @@
"range2": 60,
"range3": 90,
"range4": 120,
+ "in_party_currency": 1,
}
si = self.create_sales_invoice(no_payment_schedule=True, do_not_submit=True)
diff --git a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js
index 5ad10c7..2f6d258 100644
--- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js
+++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.js
@@ -139,6 +139,11 @@
"label": __("Show GL Balance"),
"fieldtype": "Check",
},
+ {
+ "fieldname": "for_revaluation_journals",
+ "label": __("Revaluation Journals"),
+ "fieldtype": "Check",
+ }
],
onload: function(report) {
diff --git a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py
index 60274cd..d50cf07 100644
--- a/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py
+++ b/erpnext/accounts/report/accounts_receivable_summary/accounts_receivable_summary.py
@@ -8,6 +8,7 @@
from erpnext.accounts.party import get_partywise_advanced_payment_amount
from erpnext.accounts.report.accounts_receivable.accounts_receivable import ReceivablePayableReport
+from erpnext.accounts.utils import get_party_types_from_account_type
def execute(filters=None):
@@ -22,9 +23,7 @@
class AccountsReceivableSummary(ReceivablePayableReport):
def run(self, args):
self.account_type = args.get("account_type")
- self.party_type = frappe.db.get_all(
- "Party Type", {"account_type": self.account_type}, pluck="name"
- )
+ self.party_type = get_party_types_from_account_type(self.account_type)
self.party_naming_by = frappe.db.get_value(
args.get("naming_by")[0], None, args.get("naming_by")[1]
)
diff --git a/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.js b/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.js
index 126cd03..12b9434 100644
--- a/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.js
+++ b/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.js
@@ -32,16 +32,28 @@
"options": "Asset"
},
{
+ "fieldname":"asset_category",
+ "label": __("Asset Category"),
+ "fieldtype": "Link",
+ "options": "Asset Category"
+ },
+ {
+ "fieldname":"cost_center",
+ "label": __("Cost Center"),
+ "fieldtype": "Link",
+ "options": "Cost Center"
+ },
+ {
"fieldname":"finance_book",
"label": __("Finance Book"),
"fieldtype": "Link",
"options": "Finance Book"
},
{
- "fieldname":"asset_category",
- "label": __("Asset Category"),
- "fieldtype": "Link",
- "options": "Asset Category"
- }
+ "fieldname": "include_default_book_assets",
+ "label": __("Include Default FB Assets"),
+ "fieldtype": "Check",
+ "default": 1
+ },
]
}
diff --git a/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.json b/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.json
index 0ef9d85..9002e23 100644
--- a/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.json
+++ b/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.json
@@ -1,15 +1,15 @@
{
- "add_total_row": 1,
+ "add_total_row": 0,
"columns": [],
"creation": "2016-04-08 14:49:58.133098",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
"filters": [],
- "idx": 2,
+ "idx": 6,
"is_standard": "Yes",
"letterhead": null,
- "modified": "2023-07-26 21:05:33.554778",
+ "modified": "2023-11-08 20:17:05.774211",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Asset Depreciation Ledger",
diff --git a/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py b/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py
index f21c94b..d285f28 100644
--- a/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py
+++ b/erpnext/accounts/report/asset_depreciation_ledger/asset_depreciation_ledger.py
@@ -4,7 +4,7 @@
import frappe
from frappe import _
-from frappe.utils import flt
+from frappe.utils import cstr, flt
def execute(filters=None):
@@ -32,7 +32,6 @@
filters_data.append(["against_voucher", "=", filters.get("asset")])
if filters.get("asset_category"):
-
assets = frappe.db.sql_list(
"""select name from tabAsset
where asset_category = %s and docstatus=1""",
@@ -41,12 +40,27 @@
filters_data.append(["against_voucher", "in", assets])
- if filters.get("finance_book"):
- filters_data.append(["finance_book", "in", ["", filters.get("finance_book")]])
+ company_fb = frappe.get_cached_value("Company", filters.get("company"), "default_finance_book")
+
+ if filters.get("include_default_book_assets") and company_fb:
+ if filters.get("finance_book") and cstr(filters.get("finance_book")) != cstr(company_fb):
+ frappe.throw(_("To use a different finance book, please uncheck 'Include Default FB Assets'"))
+ else:
+ finance_book = company_fb
+ elif filters.get("finance_book"):
+ finance_book = filters.get("finance_book")
+ else:
+ finance_book = None
+
+ if finance_book:
+ or_filters_data = [["finance_book", "in", ["", finance_book]], ["finance_book", "is", "not set"]]
+ else:
+ or_filters_data = [["finance_book", "in", [""]], ["finance_book", "is", "not set"]]
gl_entries = frappe.get_all(
"GL Entry",
filters=filters_data,
+ or_filters=or_filters_data,
fields=["against_voucher", "debit_in_account_currency as debit", "voucher_no", "posting_date"],
order_by="against_voucher, posting_date",
)
@@ -61,7 +75,9 @@
asset_data = assets_details.get(d.against_voucher)
if asset_data:
if not asset_data.get("accumulated_depreciation_amount"):
- asset_data.accumulated_depreciation_amount = d.debit
+ asset_data.accumulated_depreciation_amount = d.debit + asset_data.get(
+ "opening_accumulated_depreciation"
+ )
else:
asset_data.accumulated_depreciation_amount += d.debit
@@ -70,7 +86,7 @@
{
"depreciation_amount": d.debit,
"depreciation_date": d.posting_date,
- "amount_after_depreciation": (
+ "value_after_depreciation": (
flt(row.gross_purchase_amount) - flt(row.accumulated_depreciation_amount)
),
"depreciation_entry": d.voucher_no,
@@ -88,10 +104,12 @@
fields = [
"name as asset",
"gross_purchase_amount",
+ "opening_accumulated_depreciation",
"asset_category",
"status",
"depreciation_method",
"purchase_date",
+ "cost_center",
]
for d in frappe.get_all("Asset", fields=fields, filters={"name": ("in", assets)}):
@@ -122,6 +140,12 @@
"width": 120,
},
{
+ "label": _("Opening Accumulated Depreciation"),
+ "fieldname": "opening_accumulated_depreciation",
+ "fieldtype": "Currency",
+ "width": 140,
+ },
+ {
"label": _("Depreciation Amount"),
"fieldname": "depreciation_amount",
"fieldtype": "Currency",
@@ -134,8 +158,8 @@
"width": 210,
},
{
- "label": _("Amount After Depreciation"),
- "fieldname": "amount_after_depreciation",
+ "label": _("Value After Depreciation"),
+ "fieldname": "value_after_depreciation",
"fieldtype": "Currency",
"width": 180,
},
@@ -153,12 +177,13 @@
"options": "Asset Category",
"width": 120,
},
- {"label": _("Current Status"), "fieldname": "status", "fieldtype": "Data", "width": 120},
{
- "label": _("Depreciation Method"),
- "fieldname": "depreciation_method",
- "fieldtype": "Data",
- "width": 130,
+ "label": _("Cost Center"),
+ "fieldtype": "Link",
+ "fieldname": "cost_center",
+ "options": "Cost Center",
+ "width": 100,
},
+ {"label": _("Current Status"), "fieldname": "status", "fieldtype": "Data", "width": 120},
{"label": _("Purchase Date"), "fieldname": "purchase_date", "fieldtype": "Date", "width": 120},
]
diff --git a/erpnext/accounts/report/balance_sheet/balance_sheet.js b/erpnext/accounts/report/balance_sheet/balance_sheet.js
index c2b57f7..b05e744 100644
--- a/erpnext/accounts/report/balance_sheet/balance_sheet.js
+++ b/erpnext/accounts/report/balance_sheet/balance_sheet.js
@@ -17,7 +17,7 @@
frappe.query_reports["Balance Sheet"]["filters"].push({
fieldname: "include_default_book_entries",
- label: __("Include Default Book Entries"),
+ label: __("Include Default FB Entries"),
fieldtype: "Check",
default: 1,
});
diff --git a/erpnext/accounts/report/cash_flow/cash_flow.js b/erpnext/accounts/report/cash_flow/cash_flow.js
index 6b8ed27..ef17eb1 100644
--- a/erpnext/accounts/report/cash_flow/cash_flow.js
+++ b/erpnext/accounts/report/cash_flow/cash_flow.js
@@ -17,7 +17,7 @@
frappe.query_reports["Cash Flow"]["filters"].push(
{
"fieldname": "include_default_book_entries",
- "label": __("Include Default Book Entries"),
+ "label": __("Include Default FB Entries"),
"fieldtype": "Check",
"default": 1
}
diff --git a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js
index 590408c..0e0c42d 100644
--- a/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js
+++ b/erpnext/accounts/report/consolidated_financial_statement/consolidated_financial_statement.js
@@ -104,7 +104,7 @@
},
{
"fieldname": "include_default_book_entries",
- "label": __("Include Default Book Entries"),
+ "label": __("Include Default FB Entries"),
"fieldtype": "Check",
"default": 1
},
diff --git a/erpnext/accounts/report/financial_statements.py b/erpnext/accounts/report/financial_statements.py
index 693725d..096bb10 100644
--- a/erpnext/accounts/report/financial_statements.py
+++ b/erpnext/accounts/report/financial_statements.py
@@ -561,9 +561,7 @@
company_fb = frappe.get_cached_value("Company", filters.company, "default_finance_book")
if filters.finance_book and company_fb and cstr(filters.finance_book) != cstr(company_fb):
- frappe.throw(
- _("To use a different finance book, please uncheck 'Include Default Book Entries'")
- )
+ frappe.throw(_("To use a different finance book, please uncheck 'Include Default FB Entries'"))
query = query.where(
(gl_entry.finance_book.isin([cstr(filters.finance_book), cstr(company_fb), ""]))
diff --git a/erpnext/accounts/report/general_and_payment_ledger_comparison/general_and_payment_ledger_comparison.py b/erpnext/accounts/report/general_and_payment_ledger_comparison/general_and_payment_ledger_comparison.py
index 099884a..696a03b 100644
--- a/erpnext/accounts/report/general_and_payment_ledger_comparison/general_and_payment_ledger_comparison.py
+++ b/erpnext/accounts/report/general_and_payment_ledger_comparison/general_and_payment_ledger_comparison.py
@@ -79,7 +79,9 @@
.select(
gle.company,
gle.account,
+ gle.voucher_type,
gle.voucher_no,
+ gle.party_type,
gle.party,
outstanding,
)
@@ -89,7 +91,9 @@
& (gle.account.isin(val.accounts))
)
.where(Criterion.all(filter_criterion))
- .groupby(gle.company, gle.account, gle.voucher_no, gle.party)
+ .groupby(
+ gle.company, gle.account, gle.voucher_type, gle.voucher_no, gle.party_type, gle.party
+ )
.run()
)
@@ -112,7 +116,13 @@
self.account_types[acc_type].ple = (
qb.from_(ple)
.select(
- ple.company, ple.account, ple.voucher_no, ple.party, Sum(ple.amount).as_("outstanding")
+ ple.company,
+ ple.account,
+ ple.voucher_type,
+ ple.voucher_no,
+ ple.party_type,
+ ple.party,
+ Sum(ple.amount).as_("outstanding"),
)
.where(
(ple.company == self.filters.company)
@@ -120,7 +130,9 @@
& (ple.account.isin(val.accounts))
)
.where(Criterion.all(filter_criterion))
- .groupby(ple.company, ple.account, ple.voucher_no, ple.party)
+ .groupby(
+ ple.company, ple.account, ple.voucher_type, ple.voucher_no, ple.party_type, ple.party
+ )
.run()
)
@@ -138,12 +150,12 @@
self.diff = frappe._dict({})
for x in self.variation_in_payment_ledger:
- self.diff[(x[0], x[1], x[2], x[3])] = frappe._dict({"gl_balance": x[4]})
+ self.diff[(x[0], x[1], x[2], x[3], x[4], x[5])] = frappe._dict({"gl_balance": x[6]})
for x in self.variation_in_general_ledger:
- self.diff.setdefault((x[0], x[1], x[2], x[3]), frappe._dict({"gl_balance": 0.0})).update(
- frappe._dict({"pl_balance": x[4]})
- )
+ self.diff.setdefault(
+ (x[0], x[1], x[2], x[3], x[4], x[5]), frappe._dict({"gl_balance": 0.0})
+ ).update(frappe._dict({"pl_balance": x[6]}))
def generate_data(self):
self.data = []
@@ -151,8 +163,12 @@
self.data.append(
frappe._dict(
{
- "voucher_no": key[2],
- "party": key[3],
+ "company": key[0],
+ "account": key[1],
+ "voucher_type": key[2],
+ "voucher_no": key[3],
+ "party_type": key[4],
+ "party": key[5],
"gl_balance": val.gl_balance,
"pl_balance": val.pl_balance,
}
@@ -164,10 +180,50 @@
options = None
self.columns.append(
dict(
+ label=_("Company"),
+ fieldname="company",
+ fieldtype="Link",
+ options="Company",
+ width="100",
+ )
+ )
+
+ self.columns.append(
+ dict(
+ label=_("Account"),
+ fieldname="account",
+ fieldtype="Link",
+ options="Account",
+ width="100",
+ )
+ )
+
+ self.columns.append(
+ dict(
+ label=_("Voucher Type"),
+ fieldname="voucher_type",
+ fieldtype="Link",
+ options="DocType",
+ width="100",
+ )
+ )
+
+ self.columns.append(
+ dict(
label=_("Voucher No"),
fieldname="voucher_no",
- fieldtype="Data",
- options=options,
+ fieldtype="Dynamic Link",
+ options="voucher_type",
+ width="100",
+ )
+ )
+
+ self.columns.append(
+ dict(
+ label=_("Party Type"),
+ fieldname="party_type",
+ fieldtype="Link",
+ options="DocType",
width="100",
)
)
@@ -176,8 +232,8 @@
dict(
label=_("Party"),
fieldname="party",
- fieldtype="Data",
- options=options,
+ fieldtype="Dynamic Link",
+ options="party_type",
width="100",
)
)
diff --git a/erpnext/accounts/report/general_and_payment_ledger_comparison/test_general_and_payment_ledger_comparison.py b/erpnext/accounts/report/general_and_payment_ledger_comparison/test_general_and_payment_ledger_comparison.py
index 4b0e99d..59e906b 100644
--- a/erpnext/accounts/report/general_and_payment_ledger_comparison/test_general_and_payment_ledger_comparison.py
+++ b/erpnext/accounts/report/general_and_payment_ledger_comparison/test_general_and_payment_ledger_comparison.py
@@ -50,7 +50,11 @@
self.assertEqual(len(data), 1)
expected = {
+ "company": sinv.company,
+ "account": sinv.debit_to,
+ "voucher_type": sinv.doctype,
"voucher_no": sinv.name,
+ "party_type": "Customer",
"party": sinv.customer,
"gl_balance": sinv.grand_total,
"pl_balance": sinv.grand_total - 1,
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.js b/erpnext/accounts/report/general_ledger/general_ledger.js
index 37d0659..4cb443c 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.js
+++ b/erpnext/accounts/report/general_ledger/general_ledger.js
@@ -175,7 +175,7 @@
},
{
"fieldname": "include_default_book_entries",
- "label": __("Include Default Book Entries"),
+ "label": __("Include Default FB Entries"),
"fieldtype": "Check",
"default": 1
},
@@ -193,7 +193,13 @@
"fieldname": "add_values_in_transaction_currency",
"label": __("Add Columns in Transaction Currency"),
"fieldtype": "Check"
+ },
+ {
+ "fieldname": "show_remarks",
+ "label": __("Show Remarks"),
+ "fieldtype": "Check"
}
+
]
}
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py
index 79bfd78..94cd293 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.py
+++ b/erpnext/accounts/report/general_ledger/general_ledger.py
@@ -163,6 +163,9 @@
select_fields = """, debit, credit, debit_in_account_currency,
credit_in_account_currency """
+ if filters.get("show_remarks"):
+ select_fields += """,remarks"""
+
order_by_statement = "order by posting_date, account, creation"
if filters.get("include_dimensions"):
@@ -195,7 +198,7 @@
voucher_type, voucher_no, {dimension_fields}
cost_center, project, {transaction_currency_fields}
against_voucher_type, against_voucher, account_currency,
- remarks, against, is_opening, creation {select_fields}
+ against, is_opening, creation {select_fields}
from `tabGL Entry`
where company=%(company)s {conditions}
{order_by_statement}
@@ -256,9 +259,7 @@
if filters.get("company_fb") and cstr(filters.get("finance_book")) != cstr(
filters.get("company_fb")
):
- frappe.throw(
- _("To use a different finance book, please uncheck 'Include Default Book Entries'")
- )
+ frappe.throw(_("To use a different finance book, please uncheck 'Include Default FB Entries'"))
else:
conditions.append("(finance_book in (%(finance_book)s, '') OR finance_book IS NULL)")
else:
@@ -631,8 +632,10 @@
"width": 100,
},
{"label": _("Supplier Invoice No"), "fieldname": "bill_no", "fieldtype": "Data", "width": 100},
- {"label": _("Remarks"), "fieldname": "remarks", "width": 400},
]
)
+ if filters.get("show_remarks"):
+ columns.extend([{"label": _("Remarks"), "fieldname": "remarks", "width": 400}])
+
return columns
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 eac5426..06c9e44 100644
--- a/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py
+++ b/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py
@@ -47,6 +47,7 @@
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)
@@ -70,7 +71,10 @@
if net_total_map.get(name):
if voucher_type == "Journal Entry" and tax_amount and rate:
# back calcalute total amount from rate and tax_amount
- total_amount = grand_total = base_total = tax_amount / (rate / 100)
+ if rate:
+ total_amount = grand_total = base_total = tax_amount / (rate / 100)
+ elif voucher_type == "Purchase Invoice":
+ total_amount, grand_total, base_total, bill_no, bill_date = net_total_map.get(name)
else:
total_amount, grand_total, base_total = net_total_map.get(name)
else:
@@ -96,7 +100,7 @@
row.update(
{
- "section_code": tax_withholding_category,
+ "section_code": tax_withholding_category or "",
"entity_type": party_map.get(party, {}).get(party_type),
"rate": rate,
"total_amount": total_amount,
@@ -106,10 +110,14 @@
"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"])
+
return out
@@ -157,14 +165,14 @@
def get_columns(filters):
pan = "pan" if frappe.db.has_column(filters.party_type, "pan") else "tax_id"
columns = [
- {"label": _(frappe.unscrub(pan)), "fieldname": pan, "fieldtype": "Data", "width": 60},
{
- "label": _(filters.get("party_type")),
- "fieldname": "party",
- "fieldtype": "Dynamic Link",
- "options": "party_type",
- "width": 180,
+ "label": _("Section Code"),
+ "options": "Tax Withholding Category",
+ "fieldname": "section_code",
+ "fieldtype": "Link",
+ "width": 90,
},
+ {"label": _(frappe.unscrub(pan)), "fieldname": pan, "fieldtype": "Data", "width": 60},
]
if filters.naming_series == "Naming Series":
@@ -179,51 +187,60 @@
columns.extend(
[
- {
- "label": _("Date of Transaction"),
- "fieldname": "transaction_date",
- "fieldtype": "Date",
- "width": 100,
- },
- {
- "label": _("Section Code"),
- "options": "Tax Withholding Category",
- "fieldname": "section_code",
- "fieldtype": "Link",
- "width": 90,
- },
{"label": _("Entity Type"), "fieldname": "entity_type", "fieldtype": "Data", "width": 100},
- {
- "label": _("Total Amount"),
- "fieldname": "total_amount",
- "fieldtype": "Float",
- "width": 90,
- },
+ ]
+ )
+ if filters.party_type == "Supplier":
+ columns.extend(
+ [
+ {
+ "label": _("Supplier Invoice No"),
+ "fieldname": "supplier_invoice_no",
+ "fieldtype": "Data",
+ "width": 120,
+ },
+ {
+ "label": _("Supplier Invoice Date"),
+ "fieldname": "supplier_invoice_date",
+ "fieldtype": "Date",
+ "width": 120,
+ },
+ ]
+ )
+
+ columns.extend(
+ [
{
"label": _("TDS Rate %") if filters.get("party_type") == "Supplier" else _("TCS Rate %"),
"fieldname": "rate",
"fieldtype": "Percent",
- "width": 90,
+ "width": 60,
},
{
- "label": _("Tax Amount"),
- "fieldname": "tax_amount",
+ "label": _("Total Amount"),
+ "fieldname": "total_amount",
"fieldtype": "Float",
- "width": 90,
- },
- {
- "label": _("Grand Total"),
- "fieldname": "grand_total",
- "fieldtype": "Float",
- "width": 90,
+ "width": 120,
},
{
"label": _("Base Total"),
"fieldname": "base_total",
"fieldtype": "Float",
- "width": 90,
+ "width": 120,
},
- {"label": _("Transaction Type"), "fieldname": "transaction_type", "width": 100},
+ {
+ "label": _("Tax Amount"),
+ "fieldname": "tax_amount",
+ "fieldtype": "Float",
+ "width": 120,
+ },
+ {
+ "label": _("Grand Total"),
+ "fieldname": "grand_total",
+ "fieldtype": "Float",
+ "width": 120,
+ },
+ {"label": _("Transaction Type"), "fieldname": "transaction_type", "width": 130},
{
"label": _("Reference No."),
"fieldname": "ref_no",
@@ -231,6 +248,12 @@
"options": "transaction_type",
"width": 180,
},
+ {
+ "label": _("Date of Transaction"),
+ "fieldname": "transaction_date",
+ "fieldtype": "Date",
+ "width": 100,
+ },
]
)
@@ -253,27 +276,7 @@
"Tax Withholding Account", {"company": filters.get("company")}, pluck="account"
)
- query_filters = {
- "account": ("in", tds_accounts),
- "posting_date": ("between", [filters.get("from_date"), filters.get("to_date")]),
- "is_cancelled": 0,
- "against": ("not in", bank_accounts),
- }
-
- party = frappe.get_all(filters.get("party_type"), pluck="name")
- or_filters.update({"against": ("in", party), "voucher_type": "Journal Entry"})
-
- if filters.get("party"):
- del query_filters["account"]
- del query_filters["against"]
- or_filters = {"against": filters.get("party"), "party": filters.get("party")}
-
- tds_docs = frappe.get_all(
- "GL Entry",
- filters=query_filters,
- or_filters=or_filters,
- fields=["voucher_no", "voucher_type", "against", "party"],
- )
+ tds_docs = get_tds_docs_query(filters, bank_accounts, tds_accounts).run(as_dict=True)
for d in tds_docs:
if d.voucher_type == "Purchase Invoice":
@@ -309,6 +312,47 @@
)
+def get_tds_docs_query(filters, bank_accounts, tds_accounts):
+ if not tds_accounts:
+ frappe.throw(
+ _("No {0} Accounts found for this company.").format(frappe.bold("Tax Withholding")),
+ title=_("Accounts Missing Error"),
+ )
+ gle = frappe.qb.DocType("GL Entry")
+ query = (
+ frappe.qb.from_(gle)
+ .select("voucher_no", "voucher_type", "against", "party")
+ .where((gle.is_cancelled == 0))
+ )
+
+ if filters.get("from_date"):
+ query = query.where(gle.posting_date >= filters.get("from_date"))
+ if filters.get("to_date"):
+ query = query.where(gle.posting_date <= filters.get("to_date"))
+
+ if bank_accounts:
+ query = query.where(gle.against.notin(bank_accounts))
+
+ if filters.get("party"):
+ party = [filters.get("party")]
+ query = query.where(
+ ((gle.account.isin(tds_accounts) & gle.against.isin(party)))
+ | ((gle.voucher_type == "Journal Entry") & (gle.party == filters.get("party")))
+ | gle.party.isin(party)
+ )
+ else:
+ party = frappe.get_all(filters.get("party_type"), pluck="name")
+ query = query.where(
+ ((gle.account.isin(tds_accounts) & gle.against.isin(party)))
+ | (
+ (gle.voucher_type == "Journal Entry")
+ & ((gle.party_type == filters.get("party_type")) | (gle.party_type == ""))
+ )
+ | gle.party.isin(party)
+ )
+ return query
+
+
def get_journal_entry_party_map(journal_entries):
journal_entry_party_map = {}
for d in frappe.db.get_all(
@@ -335,6 +379,8 @@
"base_tax_withholding_net_total",
"grand_total",
"base_total",
+ "bill_no",
+ "bill_date",
],
"Sales Invoice": ["base_net_total", "grand_total", "base_total"],
"Payment Entry": [
@@ -353,7 +399,13 @@
for entry in entries:
tax_category_map.update({entry.name: entry.tax_withholding_category})
if doctype == "Purchase Invoice":
- value = [entry.base_tax_withholding_net_total, entry.grand_total, entry.base_total]
+ value = [
+ entry.base_tax_withholding_net_total,
+ entry.grand_total,
+ entry.base_total,
+ entry.bill_no,
+ entry.bill_date,
+ ]
elif doctype == "Sales Invoice":
value = [entry.base_net_total, entry.grand_total, entry.base_total]
elif doctype == "Payment Entry":
diff --git a/erpnext/accounts/report/trial_balance/trial_balance.js b/erpnext/accounts/report/trial_balance/trial_balance.js
index edd40b6..2c4c762 100644
--- a/erpnext/accounts/report/trial_balance/trial_balance.js
+++ b/erpnext/accounts/report/trial_balance/trial_balance.js
@@ -95,7 +95,7 @@
},
{
"fieldname": "include_default_book_entries",
- "label": __("Include Default Book Entries"),
+ "label": __("Include Default FB Entries"),
"fieldtype": "Check",
"default": 1
},
diff --git a/erpnext/accounts/report/trial_balance/trial_balance.py b/erpnext/accounts/report/trial_balance/trial_balance.py
index 2a8aa0c..8b7f0bb 100644
--- a/erpnext/accounts/report/trial_balance/trial_balance.py
+++ b/erpnext/accounts/report/trial_balance/trial_balance.py
@@ -275,9 +275,7 @@
company_fb = frappe.get_cached_value("Company", filters.company, "default_finance_book")
if filters.finance_book and company_fb and cstr(filters.finance_book) != cstr(company_fb):
- frappe.throw(
- _("To use a different finance book, please uncheck 'Include Default Book Entries'")
- )
+ frappe.throw(_("To use a different finance book, please uncheck 'Include Default FB Entries'"))
opening_balance = opening_balance.where(
(closing_balance.finance_book.isin([cstr(filters.finance_book), cstr(company_fb), ""]))
diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py
index 1c7052f..31bc6fd 100644
--- a/erpnext/accounts/utils.py
+++ b/erpnext/accounts/utils.py
@@ -10,7 +10,7 @@
from frappe import _, qb, throw
from frappe.model.meta import get_field_precision
from frappe.query_builder import AliasedQuery, Criterion, Table
-from frappe.query_builder.functions import Sum
+from frappe.query_builder.functions import Round, Sum
from frappe.query_builder.utils import DocType
from frappe.utils import (
cint,
@@ -536,6 +536,8 @@
)
else:
+ precision = frappe.get_precision("Payment Entry", "unallocated_amount")
+
payment_entry = frappe.qb.DocType("Payment Entry")
payment_ref = frappe.qb.DocType("Payment Entry Reference")
@@ -557,7 +559,10 @@
.where(payment_ref.allocated_amount == args.get("unreconciled_amount"))
)
else:
- q = q.where(payment_entry.unallocated_amount == args.get("unreconciled_amount"))
+ q = q.where(
+ Round(payment_entry.unallocated_amount, precision)
+ == Round(args.get("unreconciled_amount"), precision)
+ )
ret = q.run(as_dict=True)
@@ -2041,3 +2046,7 @@
journal_entry.save()
journal_entry.submit()
return journal_entry.name
+
+
+def get_party_types_from_account_type(account_type):
+ return frappe.db.get_all("Party Type", {"account_type": account_type}, pluck="name")
diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py
index 9d35634..3c570d1 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -818,7 +818,7 @@
"depreciation_method": d.depreciation_method,
"total_number_of_depreciations": d.total_number_of_depreciations,
"frequency_of_depreciation": d.frequency_of_depreciation,
- "daily_depreciation": d.daily_depreciation,
+ "daily_prorata_based": d.daily_prorata_based,
"salvage_value_percentage": d.salvage_value_percentage,
"expected_value_after_useful_life": flt(gross_purchase_amount)
* flt(d.salvage_value_percentage / 100),
diff --git a/erpnext/assets/doctype/asset/depreciation.py b/erpnext/assets/doctype/asset/depreciation.py
index e2a4b29..84a428c 100644
--- a/erpnext/assets/doctype/asset/depreciation.py
+++ b/erpnext/assets/doctype/asset/depreciation.py
@@ -780,6 +780,15 @@
def get_value_after_depreciation_on_disposal_date(asset, disposal_date, finance_book=None):
asset_doc = frappe.get_doc("Asset", asset)
+ if asset_doc.available_for_use_date > getdate(disposal_date):
+ frappe.throw(
+ "Disposal date {0} cannot be before available for use date {1} of the asset.".format(
+ disposal_date, asset_doc.available_for_use_date
+ )
+ )
+ elif asset_doc.available_for_use_date == getdate(disposal_date):
+ return flt(asset_doc.gross_purchase_amount - asset_doc.opening_accumulated_depreciation)
+
if not asset_doc.calculate_depreciation:
return flt(asset_doc.value_after_depreciation)
diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py
index d69f5ef..9e3ec6f 100644
--- a/erpnext/assets/doctype/asset/test_asset.py
+++ b/erpnext/assets/doctype/asset/test_asset.py
@@ -755,7 +755,9 @@
self.assertEqual(schedules, expected_schedules)
- def test_schedule_for_straight_line_method_with_daily_depreciation(self):
+ def test_schedule_for_straight_line_method_with_daily_prorata_based(
+ self,
+ ):
asset = create_asset(
calculate_depreciation=1,
available_for_use_date="2023-01-01",
@@ -764,7 +766,7 @@
depreciation_start_date="2023-01-31",
total_number_of_depreciations=12,
frequency_of_depreciation=1,
- daily_depreciation=1,
+ daily_prorata_based=1,
)
expected_schedules = [
@@ -1760,7 +1762,7 @@
"total_number_of_depreciations": args.total_number_of_depreciations or 5,
"expected_value_after_useful_life": args.expected_value_after_useful_life or 0,
"depreciation_start_date": args.depreciation_start_date,
- "daily_depreciation": args.daily_depreciation or 0,
+ "daily_prorata_based": args.daily_prorata_based or 0,
},
)
diff --git a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json
index 3772ef4..8d8b463 100644
--- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json
+++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.json
@@ -19,7 +19,7 @@
"depreciation_method",
"total_number_of_depreciations",
"rate_of_depreciation",
- "daily_depreciation",
+ "daily_prorata_based",
"column_break_8",
"frequency_of_depreciation",
"expected_value_after_useful_life",
@@ -179,9 +179,9 @@
{
"default": "0",
"depends_on": "eval:doc.depreciation_method == \"Straight Line\" || doc.depreciation_method == \"Manual\"",
- "fieldname": "daily_depreciation",
+ "fieldname": "daily_prorata_based",
"fieldtype": "Check",
- "label": "Daily Depreciation",
+ "label": "Depreciate based on daily pro-rata",
"print_hide": 1,
"read_only": 1
}
@@ -189,7 +189,7 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2023-08-10 22:22:09.722968",
+ "modified": "2023-11-03 21:32:15.021796",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset Depreciation Schedule",
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 7a88ffc..7305691 100644
--- a/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py
+++ b/erpnext/assets/doctype/asset_depreciation_schedule/asset_depreciation_schedule.py
@@ -153,7 +153,7 @@
self.frequency_of_depreciation = row.frequency_of_depreciation
self.rate_of_depreciation = row.rate_of_depreciation
self.expected_value_after_useful_life = row.expected_value_after_useful_life
- self.daily_depreciation = row.daily_depreciation
+ self.daily_prorata_based = row.daily_prorata_based
self.status = "Draft"
def make_depr_schedule(
@@ -573,7 +573,7 @@
)
# if the Depreciation Schedule is being modified after Asset Value Adjustment due to decrease in asset value
elif asset.flags.decrease_in_asset_value_due_to_value_adjustment:
- if row.daily_depreciation:
+ if row.daily_prorata_based:
daily_depr_amount = (
flt(row.value_after_depreciation) - flt(row.expected_value_after_useful_life)
) / date_diff(
@@ -618,7 +618,7 @@
) / number_of_pending_depreciations
# if the Depreciation Schedule is being prepared for the first time
else:
- if row.daily_depreciation:
+ if row.daily_prorata_based:
daily_depr_amount = (
flt(asset.gross_purchase_amount)
- flt(asset.opening_accumulated_depreciation)
diff --git a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json
index 2c27dc9..e597d5f 100644
--- a/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json
+++ b/erpnext/assets/doctype/asset_finance_book/asset_finance_book.json
@@ -8,7 +8,7 @@
"finance_book",
"depreciation_method",
"total_number_of_depreciations",
- "daily_depreciation",
+ "daily_prorata_based",
"column_break_5",
"frequency_of_depreciation",
"depreciation_start_date",
@@ -87,22 +87,22 @@
"label": "Rate of Depreciation"
},
{
- "default": "0",
- "depends_on": "eval:doc.depreciation_method == \"Straight Line\" || doc.depreciation_method == \"Manual\"",
- "fieldname": "daily_depreciation",
- "fieldtype": "Check",
- "label": "Daily Depreciation"
- },
- {
"fieldname": "salvage_value_percentage",
"fieldtype": "Percent",
"label": "Salvage Value Percentage"
+ },
+ {
+ "default": "0",
+ "depends_on": "eval:doc.depreciation_method == \"Straight Line\" || doc.depreciation_method == \"Manual\"",
+ "fieldname": "daily_prorata_based",
+ "fieldtype": "Check",
+ "label": "Depreciate based on daily pro-rata"
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2023-09-29 15:39:52.740594",
+ "modified": "2023-11-03 21:30:24.266601",
"modified_by": "Administrator",
"module": "Assets",
"name": "Asset Finance Book",
diff --git a/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py b/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py
index 23088c9..a33acfd 100644
--- a/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py
+++ b/erpnext/assets/doctype/asset_maintenance/test_asset_maintenance.py
@@ -13,25 +13,22 @@
class TestAssetMaintenance(unittest.TestCase):
def setUp(self):
set_depreciation_settings_in_company()
- create_asset_data()
- create_maintenance_team()
-
- def test_create_asset_maintenance(self):
- pr = make_purchase_receipt(
+ self.pr = make_purchase_receipt(
item_code="Photocopier", qty=1, rate=100000.0, location="Test Location"
)
+ self.asset_name = frappe.db.get_value("Asset", {"purchase_receipt": self.pr.name}, "name")
+ self.asset_doc = frappe.get_doc("Asset", self.asset_name)
- asset_name = frappe.db.get_value("Asset", {"purchase_receipt": pr.name}, "name")
- asset_doc = frappe.get_doc("Asset", asset_name)
+ def test_create_asset_maintenance_with_log(self):
month_end_date = get_last_day(nowdate())
purchase_date = nowdate() if nowdate() != month_end_date else add_days(nowdate(), -15)
- asset_doc.available_for_use_date = purchase_date
- asset_doc.purchase_date = purchase_date
+ self.asset_doc.available_for_use_date = purchase_date
+ self.asset_doc.purchase_date = purchase_date
- asset_doc.calculate_depreciation = 1
- asset_doc.append(
+ self.asset_doc.calculate_depreciation = 1
+ self.asset_doc.append(
"finance_books",
{
"expected_value_after_useful_life": 200,
@@ -42,97 +39,40 @@
},
)
- asset_doc.save()
+ self.asset_doc.save()
- if not frappe.db.exists("Asset Maintenance", "Photocopier"):
- asset_maintenance = frappe.get_doc(
- {
- "doctype": "Asset Maintenance",
- "asset_name": "Photocopier",
- "maintenance_team": "Team Awesome",
- "company": "_Test Company",
- "asset_maintenance_tasks": get_maintenance_tasks(),
- }
- ).insert()
+ asset_maintenance = frappe.get_doc(
+ {
+ "doctype": "Asset Maintenance",
+ "asset_name": self.asset_name,
+ "maintenance_team": "Team Awesome",
+ "company": "_Test Company",
+ "asset_maintenance_tasks": get_maintenance_tasks(),
+ }
+ ).insert()
- next_due_date = calculate_next_due_date(nowdate(), "Monthly")
- self.assertEqual(asset_maintenance.asset_maintenance_tasks[0].next_due_date, next_due_date)
-
- def test_create_asset_maintenance_log(self):
- if not frappe.db.exists("Asset Maintenance Log", "Photocopier"):
- asset_maintenance_log = frappe.get_doc(
- {
- "doctype": "Asset Maintenance Log",
- "asset_maintenance": "Photocopier",
- "task": "Change Oil",
- "completion_date": add_days(nowdate(), 2),
- "maintenance_status": "Completed",
- }
- ).insert()
- asset_maintenance = frappe.get_doc("Asset Maintenance", "Photocopier")
- next_due_date = calculate_next_due_date(asset_maintenance_log.completion_date, "Monthly")
+ next_due_date = calculate_next_due_date(nowdate(), "Monthly")
self.assertEqual(asset_maintenance.asset_maintenance_tasks[0].next_due_date, next_due_date)
+ asset_maintenance_log = frappe.db.get_value(
+ "Asset Maintenance Log",
+ {"asset_maintenance": asset_maintenance.name, "task_name": "Change Oil"},
+ "name",
+ )
-def create_asset_data():
- if not frappe.db.exists("Asset Category", "Equipment"):
- create_asset_category()
-
- if not frappe.db.exists("Location", "Test Location"):
- frappe.get_doc({"doctype": "Location", "location_name": "Test Location"}).insert()
-
- if not frappe.db.exists("Item", "Photocopier"):
- meta = frappe.get_meta("Asset")
- naming_series = meta.get_field("naming_series").options
- frappe.get_doc(
+ asset_maintenance_log_doc = frappe.get_doc("Asset Maintenance Log", asset_maintenance_log)
+ asset_maintenance_log_doc.update(
{
- "doctype": "Item",
- "item_code": "Photocopier",
- "item_name": "Photocopier",
- "item_group": "All Item Groups",
- "company": "_Test Company",
- "is_fixed_asset": 1,
- "is_stock_item": 0,
- "asset_category": "Equipment",
- "auto_create_assets": 1,
- "asset_naming_series": naming_series,
+ "completion_date": add_days(nowdate(), 2),
+ "maintenance_status": "Completed",
}
- ).insert()
+ )
+ asset_maintenance_log_doc.save()
+ next_due_date = calculate_next_due_date(asset_maintenance_log_doc.completion_date, "Monthly")
-def create_maintenance_team():
- user_list = ["marcus@abc.com", "thalia@abc.com", "mathias@abc.com"]
- if not frappe.db.exists("Role", "Technician"):
- frappe.get_doc({"doctype": "Role", "role_name": "Technician"}).insert()
- for user in user_list:
- if not frappe.db.get_value("User", user):
- frappe.get_doc(
- {
- "doctype": "User",
- "email": user,
- "first_name": user,
- "new_password": "password",
- "roles": [{"doctype": "Has Role", "role": "Technician"}],
- }
- ).insert()
-
- if not frappe.db.exists("Asset Maintenance Team", "Team Awesome"):
- frappe.get_doc(
- {
- "doctype": "Asset Maintenance Team",
- "maintenance_manager": "marcus@abc.com",
- "maintenance_team_name": "Team Awesome",
- "company": "_Test Company",
- "maintenance_team_members": get_maintenance_team(user_list),
- }
- ).insert()
-
-
-def get_maintenance_team(user_list):
- return [
- {"team_member": user, "full_name": user, "maintenance_role": "Technician"}
- for user in user_list[1:]
- ]
+ asset_maintenance.reload()
+ self.assertEqual(asset_maintenance.asset_maintenance_tasks[0].next_due_date, next_due_date)
def get_maintenance_tasks():
@@ -156,23 +96,6 @@
]
-def create_asset_category():
- asset_category = frappe.new_doc("Asset Category")
- asset_category.asset_category_name = "Equipment"
- asset_category.total_number_of_depreciations = 3
- asset_category.frequency_of_depreciation = 3
- asset_category.append(
- "accounts",
- {
- "company_name": "_Test Company",
- "fixed_asset_account": "_Test Fixed Asset - _TC",
- "accumulated_depreciation_account": "_Test Accumulated Depreciations - _TC",
- "depreciation_expense_account": "_Test Depreciations - _TC",
- },
- )
- asset_category.insert()
-
-
def set_depreciation_settings_in_company():
company = frappe.get_doc("Company", "_Test Company")
company.accumulated_depreciation_account = "_Test Accumulated Depreciations - _TC"
diff --git a/erpnext/assets/doctype/asset_maintenance/test_records.json b/erpnext/assets/doctype/asset_maintenance/test_records.json
new file mode 100644
index 0000000..8306fad
--- /dev/null
+++ b/erpnext/assets/doctype/asset_maintenance/test_records.json
@@ -0,0 +1,68 @@
+[
+ {
+ "doctype": "Asset Category",
+ "asset_category_name": "Equipment",
+ "total_number_of_depreciations": 3,
+ "frequency_of_depreciation": 3,
+ "accounts": [
+ {
+ "company_name": "_Test Company",
+ "fixed_asset_account": "_Test Fixed Asset - _TC",
+ "accumulated_depreciation_account": "_Test Accumulated Depreciations - _TC",
+ "depreciation_expense_account": "_Test Depreciations - _TC"
+ }
+ ]
+ },
+ {
+ "doctype": "Location",
+ "location_name": "Test Location"
+ },
+ {
+ "doctype": "Role",
+ "role_name": "Technician"
+ },
+ {
+ "doctype": "User",
+ "email": "marcus@abc.com",
+ "first_name": "marcus@abc.com",
+ "new_password": "password",
+ "roles": [{"doctype": "Has Role", "role": "Technician"}]
+ },
+ {
+ "doctype": "User",
+ "email": "thalia@abc.com",
+ "first_name": "thalia@abc.com",
+ "new_password": "password",
+ "roles": [{"doctype": "Has Role", "role": "Technician"}]
+ },
+ {
+ "doctype": "User",
+ "email": "mathias@abc.com",
+ "first_name": "mathias@abc.com",
+ "new_password": "password",
+ "roles": [{"doctype": "Has Role", "role": "Technician"}]
+ },
+ {
+ "doctype": "Asset Maintenance Team",
+ "maintenance_manager": "marcus@abc.com",
+ "maintenance_team_name": "Team Awesome",
+ "company": "_Test Company",
+ "maintenance_team_members": [
+ {"team_member": "marcus@abc.com", "full_name": "marcus@abc.com", "maintenance_role": "Technician"},
+ {"team_member": "thalia@abc.com", "full_name": "thalia@abc.com", "maintenance_role": "Technician"},
+ {"team_member": "mathias@abc.com", "full_name": "mathias@abc.com", "maintenance_role": "Technician"}
+ ]
+ },
+ {
+ "doctype": "Item",
+ "item_code": "Photocopier",
+ "item_name": "Photocopier",
+ "item_group": "All Item Groups",
+ "company": "_Test Company",
+ "is_fixed_asset": 1,
+ "is_stock_item": 0,
+ "asset_category": "Equipment",
+ "auto_create_assets": 1,
+ "asset_naming_series": "ABC.###"
+ }
+]
diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js
index 48d3331..812b7f7 100644
--- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js
+++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.js
@@ -52,7 +52,7 @@
},
{
"fieldname": "include_default_book_assets",
- "label": __("Include Default Book Assets"),
+ "label": __("Include Default FB Assets"),
"fieldtype": "Check",
"default": 1
},
diff --git a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py
index 383be97..45811a9 100644
--- a/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py
+++ b/erpnext/assets/report/fixed_asset_register/fixed_asset_register.py
@@ -223,7 +223,7 @@
company_fb = frappe.get_cached_value("Company", filters.company, "default_finance_book")
if filters.finance_book and company_fb and cstr(filters.finance_book) != cstr(company_fb):
- frappe.throw(_("To use a different finance book, please uncheck 'Include Default Book Assets'"))
+ frappe.throw(_("To use a different finance book, please uncheck 'Include Default FB Assets'"))
query = query.where(
(afb.finance_book.isin([cstr(filters.finance_book), cstr(company_fb), ""]))
diff --git a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
index b1da97d..2b6ffb7 100644
--- a/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
+++ b/erpnext/buying/doctype/purchase_order_item/purchase_order_item.json
@@ -470,6 +470,7 @@
"fieldname": "material_request",
"fieldtype": "Link",
"label": "Material Request",
+ "mandatory_depends_on": "eval: doc.material_request_item",
"no_copy": 1,
"oldfieldname": "prevdoc_docname",
"oldfieldtype": "Link",
@@ -485,6 +486,7 @@
"fieldtype": "Data",
"hidden": 1,
"label": "Material Request Item",
+ "mandatory_depends_on": "eval: doc.material_request",
"no_copy": 1,
"oldfieldname": "prevdoc_detail_docname",
"oldfieldtype": "Data",
@@ -914,7 +916,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2023-10-27 15:50:42.655573",
+ "modified": "2023-11-06 11:00:53.596417",
"modified_by": "Administrator",
"module": "Buying",
"name": "Purchase Order Item",
diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json
index 06dbd86..fd73f77 100644
--- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json
+++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.json
@@ -9,6 +9,8 @@
"field_order": [
"naming_series",
"company",
+ "billing_address",
+ "billing_address_display",
"vendor",
"column_break1",
"transaction_date",
@@ -292,13 +294,25 @@
"fieldtype": "Check",
"label": "Send Document Print",
"print_hide": 1
+ },
+ {
+ "fieldname": "billing_address",
+ "fieldtype": "Link",
+ "label": "Company Billing Address",
+ "options": "Address"
+ },
+ {
+ "fieldname": "billing_address_display",
+ "fieldtype": "Small Text",
+ "label": "Billing Address Details",
+ "read_only": 1
}
],
"icon": "fa fa-shopping-cart",
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2023-08-09 12:20:26.850623",
+ "modified": "2023-11-06 12:45:28.898706",
"modified_by": "Administrator",
"module": "Buying",
"name": "Request for Quotation",
diff --git a/erpnext/buying/doctype/supplier/supplier.json b/erpnext/buying/doctype/supplier/supplier.json
index f37db5f..60dd54c 100644
--- a/erpnext/buying/doctype/supplier/supplier.json
+++ b/erpnext/buying/doctype/supplier/supplier.json
@@ -174,7 +174,7 @@
"fieldname": "supplier_type",
"fieldtype": "Select",
"label": "Supplier Type",
- "options": "Company\nIndividual",
+ "options": "Company\nIndividual\nProprietorship\nPartnership",
"reqd": 1
},
{
@@ -485,7 +485,7 @@
"link_fieldname": "party"
}
],
- "modified": "2023-09-25 12:48:21.869563",
+ "modified": "2023-10-19 16:55:15.148325",
"modified_by": "Administrator",
"module": "Buying",
"name": "Supplier",
diff --git a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
index 7b635b3..ad1aa2b 100644
--- a/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
+++ b/erpnext/buying/doctype/supplier_quotation/supplier_quotation.json
@@ -79,6 +79,7 @@
"pricing_rule_details",
"pricing_rules",
"address_and_contact_tab",
+ "supplier_address_section",
"supplier_address",
"address_display",
"column_break_72",
@@ -86,6 +87,14 @@
"contact_display",
"contact_mobile",
"contact_email",
+ "shipping_address_section",
+ "shipping_address",
+ "column_break_zjaq",
+ "shipping_address_display",
+ "company_billing_address_section",
+ "billing_address",
+ "column_break_gcth",
+ "billing_address_display",
"terms_tab",
"tc_name",
"terms",
@@ -838,6 +847,55 @@
"fieldname": "named_place",
"fieldtype": "Data",
"label": "Named Place"
+ },
+ {
+ "fieldname": "shipping_address",
+ "fieldtype": "Link",
+ "label": "Shipping Address",
+ "options": "Address",
+ "print_hide": 1
+ },
+ {
+ "fieldname": "column_break_zjaq",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "shipping_address_display",
+ "fieldtype": "Small Text",
+ "label": "Shipping Address Details",
+ "print_hide": 1,
+ "read_only": 1
+ },
+ {
+ "fieldname": "shipping_address_section",
+ "fieldtype": "Section Break",
+ "label": "Shipping Address"
+ },
+ {
+ "fieldname": "supplier_address_section",
+ "fieldtype": "Section Break",
+ "label": "Supplier Address"
+ },
+ {
+ "fieldname": "company_billing_address_section",
+ "fieldtype": "Section Break",
+ "label": "Company Billing Address"
+ },
+ {
+ "fieldname": "billing_address",
+ "fieldtype": "Link",
+ "label": "Company Billing Address",
+ "options": "Address"
+ },
+ {
+ "fieldname": "column_break_gcth",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "billing_address_display",
+ "fieldtype": "Small Text",
+ "label": "Billing Address Details",
+ "read_only": 1
}
],
"icon": "fa fa-shopping-cart",
@@ -845,7 +903,7 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2023-06-03 16:20:15.880114",
+ "modified": "2023-11-03 13:21:40.172508",
"modified_by": "Administrator",
"module": "Buying",
"name": "Supplier Quotation",
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 6ea154c..c129b76 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -2275,6 +2275,7 @@
repost_ledger = frappe.new_doc("Repost Accounting Ledger")
repost_ledger.company = self.company
repost_ledger.append("vouchers", {"voucher_type": self.doctype, "voucher_no": self.name})
+ repost_ledger.flags.ignore_permissions = True
repost_ledger.insert()
repost_ledger.submit()
self.db_set("repost_required", 0)
diff --git a/erpnext/controllers/buying_controller.py b/erpnext/controllers/buying_controller.py
index a76abe2..ece08d8 100644
--- a/erpnext/controllers/buying_controller.py
+++ b/erpnext/controllers/buying_controller.py
@@ -4,7 +4,7 @@
import frappe
from frappe import ValidationError, _, msgprint
-from frappe.contacts.doctype.address.address import get_address_display
+from frappe.contacts.doctype.address.address import render_address
from frappe.utils import cint, flt, getdate
from frappe.utils.data import nowtime
@@ -105,26 +105,26 @@
def set_rate_for_standalone_debit_note(self):
if self.get("is_return") and self.get("update_stock") and not self.return_against:
for row in self.items:
+ if row.rate <= 0:
+ # override the rate with valuation rate
+ row.rate = get_incoming_rate(
+ {
+ "item_code": row.item_code,
+ "warehouse": row.warehouse,
+ "posting_date": self.get("posting_date"),
+ "posting_time": self.get("posting_time"),
+ "qty": row.qty,
+ "serial_and_batch_bundle": row.get("serial_and_batch_bundle"),
+ "company": self.company,
+ "voucher_type": self.doctype,
+ "voucher_no": self.name,
+ },
+ raise_error_if_no_rate=False,
+ )
- # override the rate with valuation rate
- row.rate = get_incoming_rate(
- {
- "item_code": row.item_code,
- "warehouse": row.warehouse,
- "posting_date": self.get("posting_date"),
- "posting_time": self.get("posting_time"),
- "qty": row.qty,
- "serial_and_batch_bundle": row.get("serial_and_batch_bundle"),
- "company": self.company,
- "voucher_type": self.doctype,
- "voucher_no": self.name,
- },
- raise_error_if_no_rate=False,
- )
-
- row.discount_percentage = 0.0
- row.discount_amount = 0.0
- row.margin_rate_or_amount = 0.0
+ row.discount_percentage = 0.0
+ row.discount_amount = 0.0
+ row.margin_rate_or_amount = 0.0
def set_missing_values(self, for_validate=False):
super(BuyingController, self).set_missing_values(for_validate)
@@ -246,7 +246,9 @@
for address_field, address_display_field in address_dict.items():
if self.get(address_field):
- self.set(address_display_field, get_address_display(self.get(address_field)))
+ self.set(
+ address_display_field, render_address(self.get(address_field), check_permissions=False)
+ )
def set_total_in_words(self):
from frappe.utils import money_in_words
diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py
index 5ec2474..199732b 100644
--- a/erpnext/controllers/queries.py
+++ b/erpnext/controllers/queries.py
@@ -611,6 +611,8 @@
if filters.get("company"):
condition += "and tabAccount.company = %(company)s"
+ condition += f"and tabAccount.disabled = {filters.get('disabled', 0)}"
+
return frappe.db.sql(
"""select tabAccount.name from `tabAccount`
where (tabAccount.report_type = "Profit and Loss"
diff --git a/erpnext/controllers/status_updater.py b/erpnext/controllers/status_updater.py
index 2b79b89..1317266 100644
--- a/erpnext/controllers/status_updater.py
+++ b/erpnext/controllers/status_updater.py
@@ -47,11 +47,11 @@
],
[
"To Bill",
- "eval:(self.per_delivered == 100 or self.skip_delivery_note) and self.per_billed < 100 and self.docstatus == 1",
+ "eval:(self.per_delivered >= 100 or self.skip_delivery_note) and self.per_billed < 100 and self.docstatus == 1",
],
[
"To Deliver",
- "eval:self.per_delivered < 100 and self.per_billed == 100 and self.docstatus == 1 and not self.skip_delivery_note",
+ "eval:self.per_delivered < 100 and self.per_billed >= 100 and self.docstatus == 1 and not self.skip_delivery_note",
],
[
"To Pay",
@@ -59,7 +59,7 @@
],
[
"Completed",
- "eval:(self.per_delivered == 100 or self.skip_delivery_note) and self.per_billed == 100 and self.docstatus == 1",
+ "eval:(self.per_delivered >= 100 or self.skip_delivery_note) and self.per_billed >= 100 and self.docstatus == 1",
],
["Cancelled", "eval:self.docstatus==2"],
["Closed", "eval:self.status=='Closed' and self.docstatus != 2"],
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index a7330ec..fc45c7a 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -1210,8 +1210,6 @@
repost_entry = frappe.new_doc("Repost Item Valuation")
repost_entry.based_on = "Item and Warehouse"
- repost_entry.voucher_type = voucher_type
- repost_entry.voucher_no = voucher_no
repost_entry.item_code = sle.item_code
repost_entry.warehouse = sle.warehouse
diff --git a/erpnext/crm/doctype/lead/lead.py b/erpnext/crm/doctype/lead/lead.py
index e897ba4..fdec88d 100644
--- a/erpnext/crm/doctype/lead/lead.py
+++ b/erpnext/crm/doctype/lead/lead.py
@@ -7,6 +7,8 @@
delete_contact_and_address,
load_address_and_contact,
)
+from frappe.contacts.doctype.address.address import get_default_address
+from frappe.contacts.doctype.contact.contact import get_default_contact
from frappe.email.inbox import link_communication_to_document
from frappe.model.mapper import get_mapped_doc
from frappe.utils import comma_and, get_link_to_form, has_gravatar, validate_email_address
@@ -251,6 +253,13 @@
target.customer_group = frappe.db.get_default("Customer Group")
+ address = get_default_address("Lead", source.name)
+ contact = get_default_contact("Lead", source.name)
+ if address:
+ target.customer_primary_address = address
+ if contact:
+ target.customer_primary_contact = contact
+
doclist = get_mapped_doc(
"Lead",
source_name,
diff --git a/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json b/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json
index 510317f..dfef223 100644
--- a/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json
+++ b/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json
@@ -195,26 +195,6 @@
{
"hidden": 0,
"is_query_report": 0,
- "label": "GoCardless Settings",
- "link_count": 0,
- "link_to": "GoCardless Settings",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "hidden": 0,
- "is_query_report": 0,
- "label": "Mpesa Settings",
- "link_count": 0,
- "link_to": "Mpesa Settings",
- "link_type": "DocType",
- "onboard": 0,
- "type": "Link"
- },
- {
- "hidden": 0,
- "is_query_report": 0,
"label": "Plaid Settings",
"link_count": 0,
"link_to": "Plaid Settings",
@@ -223,7 +203,7 @@
"type": "Link"
}
],
- "modified": "2023-08-29 15:48:59.010704",
+ "modified": "2023-10-31 19:57:32.748726",
"modified_by": "Administrator",
"module": "ERPNext Integrations",
"name": "ERPNext Integrations",
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.js b/erpnext/manufacturing/doctype/production_plan/production_plan.js
index 72438dd..dd102b0 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.js
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.js
@@ -89,10 +89,6 @@
frm.trigger("show_progress");
if (frm.doc.status !== "Completed") {
- frm.add_custom_button(__("Work Order Tree"), ()=> {
- frappe.set_route('Tree', 'Work Order', {production_plan: frm.doc.name});
- }, __('View'));
-
frm.add_custom_button(__("Production Plan Summary"), ()=> {
frappe.set_route('query-report', 'Production Plan Summary', {production_plan: frm.doc.name});
}, __('View'));
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.json b/erpnext/manufacturing/doctype/production_plan/production_plan.json
index 4a00416..49386c4 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.json
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.json
@@ -36,6 +36,7 @@
"prod_plan_references",
"section_break_24",
"combine_sub_items",
+ "sub_assembly_warehouse",
"section_break_ucc4",
"skip_available_sub_assembly_item",
"column_break_igxl",
@@ -416,13 +417,19 @@
{
"fieldname": "column_break_igxl",
"fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "sub_assembly_warehouse",
+ "fieldtype": "Link",
+ "label": "Sub Assembly Warehouse",
+ "options": "Warehouse"
}
],
"icon": "fa fa-calendar",
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2023-09-29 11:41:03.246059",
+ "modified": "2023-11-03 14:08:11.928027",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Production Plan",
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index ddd9375..6efb762 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -490,6 +490,12 @@
bin = frappe.get_doc("Bin", bin_name, for_update=True)
bin.update_reserved_qty_for_production_plan()
+ for d in self.sub_assembly_items:
+ if d.fg_warehouse and d.type_of_manufacturing == "In House":
+ bin_name = get_or_make_bin(d.production_item, d.fg_warehouse)
+ bin = frappe.get_doc("Bin", bin_name, for_update=True)
+ bin.update_reserved_qty_for_for_sub_assembly()
+
def delete_draft_work_order(self):
for d in frappe.get_all(
"Work Order", fields=["name"], filters={"docstatus": 0, "production_plan": ("=", self.name)}
@@ -809,7 +815,11 @@
bom_data = []
- warehouse = row.warehouse if self.skip_available_sub_assembly_item else None
+ warehouse = (
+ (self.sub_assembly_warehouse or row.warehouse)
+ if self.skip_available_sub_assembly_item
+ else None
+ )
get_sub_assembly_items(row.bom_no, bom_data, row.planned_qty, self.company, warehouse=warehouse)
self.set_sub_assembly_items_based_on_level(row, bom_data, manufacturing_type)
sub_assembly_items_store.extend(bom_data)
@@ -818,8 +828,6 @@
# Combine subassembly items
sub_assembly_items_store = self.combine_subassembly_items(sub_assembly_items_store)
- sub_assembly_items_store.sort(key=lambda d: d.bom_level, reverse=True) # sort by bom level
-
for idx, row in enumerate(sub_assembly_items_store):
row.idx = idx + 1
self.append("sub_assembly_items", row)
@@ -831,7 +839,7 @@
for data in bom_data:
data.qty = data.stock_qty
data.production_plan_item = row.name
- data.fg_warehouse = row.warehouse
+ data.fg_warehouse = self.sub_assembly_warehouse or row.warehouse
data.schedule_date = row.planned_start_date
data.type_of_manufacturing = manufacturing_type or (
"Subcontract" if data.is_sub_contracted_item else "In House"
@@ -1637,8 +1645,8 @@
query = query.run()
- if not query:
- return 0.0
+ if not query or query[0][0] is None:
+ return None
reserved_qty_for_production_plan = flt(query[0][0])
@@ -1735,7 +1743,10 @@
if not item.conversion_factor and item.purchase_uom:
item.conversion_factor = get_uom_conversion_factor(item.item_code, item.purchase_uom)
- item_details.setdefault(item.get("item_code"), item)
+ if details := item_details.get(item.get("item_code")):
+ details.qty += item.get("qty")
+ else:
+ item_details.setdefault(item.get("item_code"), item)
return item_details
@@ -1777,3 +1788,29 @@
query = query.offset(start)
return query.run()
+
+
+def get_reserved_qty_for_sub_assembly(item_code, warehouse):
+ table = frappe.qb.DocType("Production Plan")
+ child = frappe.qb.DocType("Production Plan Sub Assembly Item")
+
+ query = (
+ frappe.qb.from_(table)
+ .inner_join(child)
+ .on(table.name == child.parent)
+ .select(Sum(child.qty - IfNull(child.wo_produced_qty, 0)))
+ .where(
+ (table.docstatus == 1)
+ & (child.production_item == item_code)
+ & (child.fg_warehouse == warehouse)
+ & (table.status.notin(["Completed", "Closed"]))
+ )
+ )
+
+ query = query.run()
+
+ if not query or query[0][0] is None:
+ return None
+
+ qty = flt(query[0][0])
+ return qty if qty > 0 else 0.0
diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
index 6ab9232..dd32c34 100644
--- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
@@ -664,49 +664,6 @@
frappe.db.rollback()
- def test_subassmebly_sorting(self):
- "Test subassembly sorting in case of multiple items with nested BOMs."
- from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom
-
- prefix = "_TestLevel_"
- boms = {
- "Assembly": {
- "SubAssembly1": {
- "ChildPart1": {},
- "ChildPart2": {},
- },
- "ChildPart6": {},
- "SubAssembly4": {"SubSubAssy2": {"ChildPart7": {}}},
- },
- "MegaDeepAssy": {
- "SecretSubassy": {
- "SecretPart": {"VerySecret": {"SuperSecret": {"Classified": {}}}},
- },
- # ^ assert that this is
- # first item in subassy table
- },
- }
- create_nested_bom(boms, prefix=prefix)
-
- items = [prefix + item_code for item_code in boms.keys()]
- plan = create_production_plan(item_code=items[0], do_not_save=True)
- plan.append(
- "po_items",
- {
- "use_multi_level_bom": 1,
- "item_code": items[1],
- "bom_no": frappe.db.get_value("Item", items[1], "default_bom"),
- "planned_qty": 1,
- "planned_start_date": now_datetime(),
- },
- )
- plan.get_sub_assembly_items()
-
- bom_level_order = [d.bom_level for d in plan.sub_assembly_items]
- self.assertEqual(bom_level_order, sorted(bom_level_order, reverse=True))
- # lowest most level of subassembly should be first
- self.assertIn("SuperSecret", plan.sub_assembly_items[0].production_item)
-
def test_multiple_work_order_for_production_plan_item(self):
"Test producing Prod Plan (making WO) in parts."
@@ -1042,13 +999,14 @@
after_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan"))
self.assertEqual(after_qty - before_qty, 1)
-
pln = frappe.get_doc("Production Plan", pln.name)
pln.cancel()
bin_name = get_or_make_bin("Raw Material Item 1", "_Test Warehouse - _TC")
after_qty = flt(frappe.db.get_value("Bin", bin_name, "reserved_qty_for_production_plan"))
+ pln.reload()
+ self.assertEqual(pln.docstatus, 2)
self.assertEqual(after_qty, before_qty)
def test_resered_qty_for_production_plan_for_work_order(self):
@@ -1332,6 +1290,120 @@
self.assertTrue(row.warehouse == mrp_warhouse)
self.assertEqual(row.quantity, 12)
+ def test_mr_qty_for_same_rm_with_different_sub_assemblies(self):
+ from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom
+
+ bom_tree = {
+ "Fininshed Goods2 For SUB Test": {
+ "SubAssembly2 For SUB Test": {"ChildPart2 For SUB Test": {}},
+ "SubAssembly3 For SUB Test": {"ChildPart2 For SUB Test": {}},
+ }
+ }
+
+ parent_bom = create_nested_bom(bom_tree, prefix="")
+ plan = create_production_plan(
+ item_code=parent_bom.item,
+ planned_qty=1,
+ ignore_existing_ordered_qty=1,
+ do_not_submit=1,
+ skip_available_sub_assembly_item=1,
+ warehouse="_Test Warehouse - _TC",
+ )
+
+ plan.get_sub_assembly_items()
+ plan.make_material_request()
+
+ for row in plan.mr_items:
+ if row.item_code == "ChildPart2 For SUB Test":
+ self.assertEqual(row.quantity, 2)
+
+ def test_reserve_sub_assembly_items(self):
+ from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom
+ from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
+
+ bom_tree = {
+ "Fininshed Goods Bicycle": {
+ "Frame Assembly": {"Frame": {}},
+ "Chain Assembly": {"Chain": {}},
+ }
+ }
+ parent_bom = create_nested_bom(bom_tree, prefix="")
+
+ warehouse = "_Test Warehouse - _TC"
+ company = "_Test Company"
+
+ sub_assembly_warehouse = create_warehouse("SUB ASSEMBLY WH", company=company)
+
+ for item_code in ["Frame", "Chain"]:
+ make_stock_entry(item_code=item_code, target=warehouse, qty=2, basic_rate=100)
+
+ before_qty = flt(
+ frappe.db.get_value(
+ "Bin",
+ {"item_code": "Frame Assembly", "warehouse": sub_assembly_warehouse},
+ "reserved_qty_for_production_plan",
+ )
+ )
+
+ plan = create_production_plan(
+ item_code=parent_bom.item,
+ planned_qty=2,
+ ignore_existing_ordered_qty=1,
+ do_not_submit=1,
+ skip_available_sub_assembly_item=1,
+ warehouse=warehouse,
+ sub_assembly_warehouse=sub_assembly_warehouse,
+ )
+
+ plan.get_sub_assembly_items()
+ plan.submit()
+
+ after_qty = flt(
+ frappe.db.get_value(
+ "Bin",
+ {"item_code": "Frame Assembly", "warehouse": sub_assembly_warehouse},
+ "reserved_qty_for_production_plan",
+ )
+ )
+
+ self.assertEqual(after_qty, before_qty + 2)
+
+ plan.make_work_order()
+ work_orders = frappe.get_all(
+ "Work Order",
+ fields=["name", "production_item"],
+ filters={"production_plan": plan.name},
+ order_by="creation desc",
+ )
+
+ for d in work_orders:
+ wo_doc = frappe.get_doc("Work Order", d.name)
+ wo_doc.skip_transfer = 1
+ wo_doc.from_wip_warehouse = 1
+
+ wo_doc.wip_warehouse = (
+ warehouse
+ if d.production_item in ["Frame Assembly", "Chain Assembly"]
+ else sub_assembly_warehouse
+ )
+
+ wo_doc.submit()
+
+ if d.production_item == "Frame Assembly":
+ self.assertEqual(wo_doc.fg_warehouse, sub_assembly_warehouse)
+ se_doc = frappe.get_doc(make_se_from_wo(wo_doc.name, "Manufacture", 2))
+ se_doc.submit()
+
+ after_qty = flt(
+ frappe.db.get_value(
+ "Bin",
+ {"item_code": "Frame Assembly", "warehouse": sub_assembly_warehouse},
+ "reserved_qty_for_production_plan",
+ )
+ )
+
+ self.assertEqual(after_qty, before_qty)
+
def create_production_plan(**args):
"""
@@ -1352,6 +1424,7 @@
"ignore_existing_ordered_qty": args.ignore_existing_ordered_qty or 0,
"get_items_from": "Sales Order",
"skip_available_sub_assembly_item": args.skip_available_sub_assembly_item or 0,
+ "sub_assembly_warehouse": args.sub_assembly_warehouse,
}
)
diff --git a/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json b/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json
index fde0404..aff740b 100644
--- a/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json
+++ b/erpnext/manufacturing/doctype/production_plan_sub_assembly_item/production_plan_sub_assembly_item.json
@@ -17,11 +17,10 @@
"type_of_manufacturing",
"supplier",
"work_order_details_section",
- "work_order",
+ "wo_produced_qty",
"purchase_order",
"production_plan_item",
"column_break_7",
- "produced_qty",
"received_qty",
"indent",
"section_break_19",
@@ -53,13 +52,6 @@
"label": "Reference"
},
{
- "fieldname": "work_order",
- "fieldtype": "Link",
- "label": "Work Order",
- "options": "Work Order",
- "read_only": 1
- },
- {
"fieldname": "column_break_7",
"fieldtype": "Column Break"
},
@@ -81,7 +73,8 @@
{
"fieldname": "received_qty",
"fieldtype": "Float",
- "label": "Received Qty"
+ "label": "Received Qty",
+ "read_only": 1
},
{
"fieldname": "bom_no",
@@ -162,12 +155,6 @@
"options": "Warehouse"
},
{
- "fieldname": "produced_qty",
- "fieldtype": "Data",
- "label": "Produced Quantity",
- "read_only": 1
- },
- {
"default": "In House",
"fieldname": "type_of_manufacturing",
"fieldtype": "Select",
@@ -209,12 +196,18 @@
"label": "Projected Qty",
"no_copy": 1,
"read_only": 1
+ },
+ {
+ "fieldname": "wo_produced_qty",
+ "fieldtype": "Float",
+ "label": "Produced Qty",
+ "read_only": 1
}
],
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2023-05-22 17:52:34.708879",
+ "modified": "2023-11-03 13:33:42.959387",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "Production Plan Sub Assembly Item",
diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py
index c828c87..0ae7657 100644
--- a/erpnext/manufacturing/doctype/work_order/test_work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py
@@ -494,6 +494,7 @@
"from_time": row.from_time,
"to_time": row.to_time,
"time_in_mins": row.time_in_mins,
+ "completed_qty": 0,
},
)
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.js b/erpnext/manufacturing/doctype/work_order/work_order.js
index 58945bb..d9cc212 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.js
+++ b/erpnext/manufacturing/doctype/work_order/work_order.js
@@ -710,7 +710,7 @@
return new Promise((resolve, reject) => {
frappe.prompt({
fieldtype: 'Float',
- label: __('Qty for {0}', [purpose]),
+ label: __('Qty for {0}', [__(purpose)]),
fieldname: 'qty',
description: __('Max: {0}', [max]),
default: max
diff --git a/erpnext/manufacturing/doctype/work_order/work_order.py b/erpnext/manufacturing/doctype/work_order/work_order.py
index f9fddcb..36a0cae 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order.py
@@ -293,6 +293,7 @@
update_produced_qty_in_so_item(self.sales_order, self.sales_order_item)
if self.production_plan:
+ self.set_produced_qty_for_sub_assembly_item()
self.update_production_plan_status()
def get_transferred_or_manufactured_qty(self, purpose):
@@ -569,16 +570,49 @@
)
def update_planned_qty(self):
+ from erpnext.manufacturing.doctype.production_plan.production_plan import (
+ get_reserved_qty_for_sub_assembly,
+ )
+
+ qty_dict = {"planned_qty": get_planned_qty(self.production_item, self.fg_warehouse)}
+
+ if self.production_plan_sub_assembly_item and self.production_plan:
+ qty_dict["reserved_qty_for_production_plan"] = get_reserved_qty_for_sub_assembly(
+ self.production_item, self.fg_warehouse
+ )
+
update_bin_qty(
self.production_item,
self.fg_warehouse,
- {"planned_qty": get_planned_qty(self.production_item, self.fg_warehouse)},
+ qty_dict,
)
if self.material_request:
mr_obj = frappe.get_doc("Material Request", self.material_request)
mr_obj.update_requested_qty([self.material_request_item])
+ def set_produced_qty_for_sub_assembly_item(self):
+ table = frappe.qb.DocType("Work Order")
+
+ query = (
+ frappe.qb.from_(table)
+ .select(Sum(table.produced_qty))
+ .where(
+ (table.production_plan == self.production_plan)
+ & (table.production_plan_sub_assembly_item == self.production_plan_sub_assembly_item)
+ & (table.docstatus == 1)
+ )
+ ).run()
+
+ produced_qty = flt(query[0][0]) if query else 0
+
+ frappe.db.set_value(
+ "Production Plan Sub Assembly Item",
+ self.production_plan_sub_assembly_item,
+ "wo_produced_qty",
+ produced_qty,
+ )
+
def update_ordered_qty(self):
if (
self.production_plan
diff --git a/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.js b/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.js
index 521543a..afe4a6e 100644
--- a/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.js
+++ b/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.js
@@ -22,9 +22,9 @@
"formatter": function(value, row, column, data, default_formatter) {
value = default_formatter(value, row, column, data);
- if (column.fieldname == "document_name") {
+ if (column.fieldname == "item_code") {
var color = data.pending_qty > 0 ? 'red': 'green';
- value = `<a style='color:${color}' href="#Form/${data['document_type']}/${data['document_name']}" data-doctype="${data['document_type']}">${data['document_name']}</a>`;
+ value = `<a style='color:${color}' href="/app/item/${data['item_code']}" data-doctype="Item">${data['item_code']}</a>`;
}
return value;
diff --git a/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.py b/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.py
index 2c8f82f..076690f 100644
--- a/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.py
+++ b/erpnext/manufacturing/report/production_plan_summary/production_plan_summary.py
@@ -44,6 +44,7 @@
{
"indent": 0,
"item_code": row.item_code,
+ "sales_order": row.get("sales_order"),
"item_name": frappe.get_cached_value("Item", row.item_code, "item_name"),
"qty": row.planned_qty,
"document_type": "Work Order",
@@ -80,7 +81,7 @@
data.append(
{
- "indent": 1,
+ "indent": 1 + item.indent,
"item_code": item.production_item,
"item_name": item.item_name,
"qty": item.qty,
@@ -98,7 +99,7 @@
for row in frappe.get_all(
"Work Order",
filters={"production_plan": filters.get("production_plan")},
- fields=["name", "produced_qty", "production_plan", "production_item"],
+ fields=["name", "produced_qty", "production_plan", "production_item", "sales_order"],
):
order_details.setdefault((row.name, row.production_item), row)
@@ -118,10 +119,17 @@
"label": _("Finished Good"),
"fieldtype": "Link",
"fieldname": "item_code",
- "width": 300,
+ "width": 240,
"options": "Item",
},
- {"label": _("Item Name"), "fieldtype": "data", "fieldname": "item_name", "width": 100},
+ {"label": _("Item Name"), "fieldtype": "data", "fieldname": "item_name", "width": 150},
+ {
+ "label": _("Sales Order"),
+ "options": "Sales Order",
+ "fieldtype": "Link",
+ "fieldname": "sales_order",
+ "width": 100,
+ },
{
"label": _("Document Type"),
"fieldtype": "Link",
@@ -133,10 +141,16 @@
"label": _("Document Name"),
"fieldtype": "Dynamic Link",
"fieldname": "document_name",
- "width": 150,
+ "options": "document_type",
+ "width": 180,
},
{"label": _("BOM Level"), "fieldtype": "Int", "fieldname": "bom_level", "width": 100},
{"label": _("Order Qty"), "fieldtype": "Float", "fieldname": "qty", "width": 120},
- {"label": _("Received Qty"), "fieldtype": "Float", "fieldname": "produced_qty", "width": 160},
+ {
+ "label": _("Produced / Received Qty"),
+ "fieldtype": "Float",
+ "fieldname": "produced_qty",
+ "width": 200,
+ },
{"label": _("Pending Qty"), "fieldtype": "Float", "fieldname": "pending_qty", "width": 110},
]
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index 3f9744d..d5aa11d 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -338,11 +338,18 @@
erpnext.patches.v14_0.migrate_deferred_accounts_to_item_defaults
erpnext.patches.v14_0.update_invoicing_period_in_subscription
execute:frappe.delete_doc("Page", "welcome-to-erpnext")
+erpnext.patches.v15_0.migrate_payment_request_status
erpnext.patches.v15_0.delete_payment_gateway_doctypes
erpnext.patches.v14_0.create_accounting_dimensions_in_sales_order_item
erpnext.patches.v15_0.update_sre_from_voucher_details
erpnext.patches.v14_0.rename_over_order_allowance_field
erpnext.patches.v14_0.migrate_delivery_stop_lock_field
+execute:frappe.db.set_single_value("Payment Reconciliation", "invoice_limit", 50)
+execute:frappe.db.set_single_value("Payment Reconciliation", "payment_limit", 50)
+erpnext.patches.v14_0.add_default_for_repost_settings
+erpnext.patches.v15_0.rename_daily_depreciation_to_depreciation_amount_based_on_num_days_in_month
+erpnext.patches.v15_0.rename_depreciation_amount_based_on_num_days_in_month_to_daily_prorata_based
+erpnext.patches.v15_0.set_reserved_stock_in_bin
erpnext.patches.v15_0.create_advance_payment_status
# below migration patch should always run last
erpnext.patches.v14_0.migrate_gl_to_payment_ledger
diff --git a/erpnext/patches/v14_0/add_default_for_repost_settings.py b/erpnext/patches/v14_0/add_default_for_repost_settings.py
new file mode 100644
index 0000000..6cafc66
--- /dev/null
+++ b/erpnext/patches/v14_0/add_default_for_repost_settings.py
@@ -0,0 +1,12 @@
+import frappe
+
+
+def execute():
+ """
+ Update Repost Accounting Ledger Settings with default values
+ """
+ allowed_types = ["Sales Invoice", "Purchase Invoice", "Payment Entry", "Journal Entry"]
+ repost_settings = frappe.get_doc("Repost Accounting Ledger Settings")
+ for x in allowed_types:
+ repost_settings.append("allowed_types", {"document_type": x, "allowed": True})
+ repost_settings.save()
diff --git a/erpnext/patches/v15_0/migrate_payment_request_status.py b/erpnext/patches/v15_0/migrate_payment_request_status.py
new file mode 100644
index 0000000..9f0de56
--- /dev/null
+++ b/erpnext/patches/v15_0/migrate_payment_request_status.py
@@ -0,0 +1,13 @@
+import frappe
+
+
+def execute():
+ """
+ Description:
+ Change Inward Payment Requests from statut 'Initiated' to correct status 'Requested'.
+ Status 'Initiated' is reserved for Outward Payment Requests and was a semantic error in previour versions.
+ """
+ so = frappe.qb.DocType("Payment Request")
+ frappe.qb.update(so).set(so.status, "Requested").where(so.payment_request_type == "Inward").where(
+ so.status == "Initiated"
+ ).run()
diff --git a/erpnext/patches/v15_0/rename_daily_depreciation_to_depreciation_amount_based_on_num_days_in_month.py b/erpnext/patches/v15_0/rename_daily_depreciation_to_depreciation_amount_based_on_num_days_in_month.py
new file mode 100644
index 0000000..63dc0e0
--- /dev/null
+++ b/erpnext/patches/v15_0/rename_daily_depreciation_to_depreciation_amount_based_on_num_days_in_month.py
@@ -0,0 +1,21 @@
+# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+
+from frappe.model.utils.rename_field import rename_field
+
+
+def execute():
+ try:
+ rename_field(
+ "Asset Finance Book", "daily_depreciation", "depreciation_amount_based_on_num_days_in_month"
+ )
+ rename_field(
+ "Asset Depreciation Schedule",
+ "daily_depreciation",
+ "depreciation_amount_based_on_num_days_in_month",
+ )
+
+ except Exception as e:
+ if e.args[0] != 1054:
+ raise
diff --git a/erpnext/patches/v15_0/rename_depreciation_amount_based_on_num_days_in_month_to_daily_prorata_based.py b/erpnext/patches/v15_0/rename_depreciation_amount_based_on_num_days_in_month_to_daily_prorata_based.py
new file mode 100644
index 0000000..2c03c23
--- /dev/null
+++ b/erpnext/patches/v15_0/rename_depreciation_amount_based_on_num_days_in_month_to_daily_prorata_based.py
@@ -0,0 +1,21 @@
+# Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and Contributors
+# License: GNU General Public License v3. See license.txt
+
+
+from frappe.model.utils.rename_field import rename_field
+
+
+def execute():
+ try:
+ rename_field(
+ "Asset Finance Book", "depreciation_amount_based_on_num_days_in_month", "daily_prorata_based"
+ )
+ rename_field(
+ "Asset Depreciation Schedule",
+ "depreciation_amount_based_on_num_days_in_month",
+ "daily_prorata_based",
+ )
+
+ except Exception as e:
+ if e.args[0] != 1054:
+ raise
diff --git a/erpnext/patches/v15_0/set_reserved_stock_in_bin.py b/erpnext/patches/v15_0/set_reserved_stock_in_bin.py
new file mode 100644
index 0000000..fd0a233
--- /dev/null
+++ b/erpnext/patches/v15_0/set_reserved_stock_in_bin.py
@@ -0,0 +1,24 @@
+import frappe
+from frappe.query_builder.functions import Sum
+
+
+def execute():
+ sre = frappe.qb.DocType("Stock Reservation Entry")
+ query = (
+ frappe.qb.from_(sre)
+ .select(
+ sre.item_code,
+ sre.warehouse,
+ Sum(sre.reserved_qty - sre.delivered_qty).as_("reserved_stock"),
+ )
+ .where((sre.docstatus == 1) & (sre.status.notin(["Delivered", "Cancelled"])))
+ .groupby(sre.item_code, sre.warehouse)
+ )
+
+ for d in query.run(as_dict=True):
+ frappe.db.set_value(
+ "Bin",
+ {"item_code": d.item_code, "warehouse": d.warehouse},
+ "reserved_stock",
+ d.reserved_stock,
+ )
diff --git a/erpnext/projects/report/billing_summary.py b/erpnext/projects/report/billing_summary.py
deleted file mode 100644
index ac1524a..0000000
--- a/erpnext/projects/report/billing_summary.py
+++ /dev/null
@@ -1,155 +0,0 @@
-# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-import frappe
-from frappe import _
-from frappe.utils import flt, time_diff_in_hours
-
-
-def get_columns():
- return [
- {
- "label": _("Employee ID"),
- "fieldtype": "Link",
- "fieldname": "employee",
- "options": "Employee",
- "width": 300,
- },
- {
- "label": _("Employee Name"),
- "fieldtype": "data",
- "fieldname": "employee_name",
- "hidden": 1,
- "width": 200,
- },
- {
- "label": _("Timesheet"),
- "fieldtype": "Link",
- "fieldname": "timesheet",
- "options": "Timesheet",
- "width": 150,
- },
- {"label": _("Working Hours"), "fieldtype": "Float", "fieldname": "total_hours", "width": 150},
- {
- "label": _("Billable Hours"),
- "fieldtype": "Float",
- "fieldname": "total_billable_hours",
- "width": 150,
- },
- {"label": _("Billing Amount"), "fieldtype": "Currency", "fieldname": "amount", "width": 150},
- ]
-
-
-def get_data(filters):
- data = []
- if filters.from_date > filters.to_date:
- frappe.msgprint(_("From Date can not be greater than To Date"))
- return data
-
- timesheets = get_timesheets(filters)
-
- filters.from_date = frappe.utils.get_datetime(filters.from_date)
- filters.to_date = frappe.utils.add_to_date(
- frappe.utils.get_datetime(filters.to_date), days=1, seconds=-1
- )
-
- timesheet_details = get_timesheet_details(filters, timesheets.keys())
-
- for ts, ts_details in timesheet_details.items():
- total_hours = 0
- total_billing_hours = 0
- total_amount = 0
-
- for row in ts_details:
- from_time, to_time = filters.from_date, filters.to_date
-
- if row.to_time < from_time or row.from_time > to_time:
- continue
-
- if row.from_time > from_time:
- from_time = row.from_time
-
- if row.to_time < to_time:
- to_time = row.to_time
-
- activity_duration, billing_duration = get_billable_and_total_duration(row, from_time, to_time)
-
- total_hours += activity_duration
- total_billing_hours += billing_duration
- total_amount += billing_duration * flt(row.billing_rate)
-
- if total_hours:
- data.append(
- {
- "employee": timesheets.get(ts).employee,
- "employee_name": timesheets.get(ts).employee_name,
- "timesheet": ts,
- "total_billable_hours": total_billing_hours,
- "total_hours": total_hours,
- "amount": total_amount,
- }
- )
-
- return data
-
-
-def get_timesheets(filters):
- record_filters = [
- ["start_date", "<=", filters.to_date],
- ["end_date", ">=", filters.from_date],
- ]
- if not filters.get("include_draft_timesheets"):
- record_filters.append(["docstatus", "=", 1])
- else:
- record_filters.append(["docstatus", "!=", 2])
- if "employee" in filters:
- record_filters.append(["employee", "=", filters.employee])
-
- timesheets = frappe.get_all(
- "Timesheet", filters=record_filters, fields=["employee", "employee_name", "name"]
- )
- timesheet_map = frappe._dict()
- for d in timesheets:
- timesheet_map.setdefault(d.name, d)
-
- return timesheet_map
-
-
-def get_timesheet_details(filters, timesheet_list):
- timesheet_details_filter = {"parent": ["in", timesheet_list]}
-
- if "project" in filters:
- timesheet_details_filter["project"] = filters.project
-
- timesheet_details = frappe.get_all(
- "Timesheet Detail",
- filters=timesheet_details_filter,
- fields=[
- "from_time",
- "to_time",
- "hours",
- "is_billable",
- "billing_hours",
- "billing_rate",
- "parent",
- ],
- )
-
- timesheet_details_map = frappe._dict()
- for d in timesheet_details:
- timesheet_details_map.setdefault(d.parent, []).append(d)
-
- return timesheet_details_map
-
-
-def get_billable_and_total_duration(activity, start_time, end_time):
- precision = frappe.get_precision("Timesheet Detail", "hours")
- activity_duration = time_diff_in_hours(end_time, start_time)
- billing_duration = 0.0
- if activity.is_billable:
- billing_duration = activity.billing_hours
- if activity_duration != activity.billing_hours:
- billing_duration = activity_duration * activity.billing_hours / activity.hours
-
- return flt(activity_duration, precision), flt(billing_duration, precision)
diff --git a/erpnext/projects/report/employee_billing_summary/employee_billing_summary.js b/erpnext/projects/report/employee_billing_summary/employee_billing_summary.js
deleted file mode 100644
index 2c25465..0000000
--- a/erpnext/projects/report/employee_billing_summary/employee_billing_summary.js
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-
-frappe.query_reports["Employee Billing Summary"] = {
- "filters": [
- {
- fieldname: "employee",
- label: __("Employee"),
- fieldtype: "Link",
- options: "Employee",
- reqd: 1
- },
- {
- fieldname:"from_date",
- label: __("From Date"),
- fieldtype: "Date",
- default: frappe.datetime.add_months(frappe.datetime.month_start(), -1),
- reqd: 1
- },
- {
- fieldname:"to_date",
- label: __("To Date"),
- fieldtype: "Date",
- default: frappe.datetime.add_days(frappe.datetime.month_start(), -1),
- reqd: 1
- },
- {
- fieldname:"include_draft_timesheets",
- label: __("Include Timesheets in Draft Status"),
- fieldtype: "Check",
- },
- ]
-}
diff --git a/erpnext/projects/report/employee_billing_summary/employee_billing_summary.py b/erpnext/projects/report/employee_billing_summary/employee_billing_summary.py
deleted file mode 100644
index a2f7378..0000000
--- a/erpnext/projects/report/employee_billing_summary/employee_billing_summary.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-import frappe
-
-from erpnext.projects.report.billing_summary import get_columns, get_data
-
-
-def execute(filters=None):
- filters = frappe._dict(filters or {})
- columns = get_columns()
-
- data = get_data(filters)
- return columns, data
diff --git a/erpnext/projects/report/project_billing_summary/project_billing_summary.js b/erpnext/projects/report/project_billing_summary/project_billing_summary.js
deleted file mode 100644
index fce0c68..0000000
--- a/erpnext/projects/report/project_billing_summary/project_billing_summary.js
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors
-// For license information, please see license.txt
-
-
-frappe.query_reports["Project Billing Summary"] = {
- "filters": [
- {
- fieldname: "project",
- label: __("Project"),
- fieldtype: "Link",
- options: "Project",
- reqd: 1
- },
- {
- fieldname:"from_date",
- label: __("From Date"),
- fieldtype: "Date",
- default: frappe.datetime.add_months(frappe.datetime.month_start(), -1),
- reqd: 1
- },
- {
- fieldname:"to_date",
- label: __("To Date"),
- fieldtype: "Date",
- default: frappe.datetime.add_days(frappe.datetime.month_start(),-1),
- reqd: 1
- },
- {
- fieldname:"include_draft_timesheets",
- label: __("Include Timesheets in Draft Status"),
- fieldtype: "Check",
- },
- ]
-}
diff --git a/erpnext/projects/report/project_billing_summary/project_billing_summary.json b/erpnext/projects/report/project_billing_summary/project_billing_summary.json
deleted file mode 100644
index 817d0cd..0000000
--- a/erpnext/projects/report/project_billing_summary/project_billing_summary.json
+++ /dev/null
@@ -1,36 +0,0 @@
-{
- "add_total_row": 1,
- "creation": "2019-03-11 16:22:39.460524",
- "disable_prepared_report": 0,
- "disabled": 0,
- "docstatus": 0,
- "doctype": "Report",
- "idx": 0,
- "is_standard": "Yes",
- "modified": "2019-06-13 15:54:55.255947",
- "modified_by": "Administrator",
- "module": "Projects",
- "name": "Project Billing Summary",
- "owner": "Administrator",
- "prepared_report": 0,
- "ref_doctype": "Timesheet",
- "report_name": "Project Billing Summary",
- "report_type": "Script Report",
- "roles": [
- {
- "role": "Projects User"
- },
- {
- "role": "HR User"
- },
- {
- "role": "Manufacturing User"
- },
- {
- "role": "Employee"
- },
- {
- "role": "Accounts User"
- }
- ]
-}
\ No newline at end of file
diff --git a/erpnext/projects/report/project_billing_summary/project_billing_summary.py b/erpnext/projects/report/project_billing_summary/project_billing_summary.py
deleted file mode 100644
index a2f7378..0000000
--- a/erpnext/projects/report/project_billing_summary/project_billing_summary.py
+++ /dev/null
@@ -1,15 +0,0 @@
-# Copyright (c) 2013, Frappe Technologies Pvt. Ltd. and contributors
-# For license information, please see license.txt
-
-
-import frappe
-
-from erpnext.projects.report.billing_summary import get_columns, get_data
-
-
-def execute(filters=None):
- filters = frappe._dict(filters or {})
- columns = get_columns()
-
- data = get_data(filters)
- return columns, data
diff --git a/erpnext/projects/report/project_billing_summary/__init__.py b/erpnext/projects/report/timesheet_billing_summary/__init__.py
similarity index 100%
rename from erpnext/projects/report/project_billing_summary/__init__.py
rename to erpnext/projects/report/timesheet_billing_summary/__init__.py
diff --git a/erpnext/projects/report/timesheet_billing_summary/timesheet_billing_summary.js b/erpnext/projects/report/timesheet_billing_summary/timesheet_billing_summary.js
new file mode 100644
index 0000000..1efd0c6
--- /dev/null
+++ b/erpnext/projects/report/timesheet_billing_summary/timesheet_billing_summary.js
@@ -0,0 +1,67 @@
+// Copyright (c) 2023, Frappe Technologies Pvt. Ltd. and contributors
+// For license information, please see license.txt
+
+frappe.query_reports["Timesheet Billing Summary"] = {
+ tree: true,
+ initial_depth: 0,
+ filters: [
+ {
+ fieldname: "employee",
+ label: __("Employee"),
+ fieldtype: "Link",
+ options: "Employee",
+ on_change: function (report) {
+ unset_group_by(report, "employee");
+ },
+ },
+ {
+ fieldname: "project",
+ label: __("Project"),
+ fieldtype: "Link",
+ options: "Project",
+ on_change: function (report) {
+ unset_group_by(report, "project");
+ },
+ },
+ {
+ fieldname: "from_date",
+ label: __("From Date"),
+ fieldtype: "Date",
+ default: frappe.datetime.add_months(
+ frappe.datetime.month_start(),
+ -1
+ ),
+ },
+ {
+ fieldname: "to_date",
+ label: __("To Date"),
+ fieldtype: "Date",
+ default: frappe.datetime.add_days(
+ frappe.datetime.month_start(),
+ -1
+ ),
+ },
+ { // NOTE: `update_group_by_options` expects this filter to be the fifth in the list
+ fieldname: "group_by",
+ label: __("Group By"),
+ fieldtype: "Select",
+ options: [
+ "",
+ { value: "employee", label: __("Employee") },
+ { value: "project", label: __("Project") },
+ { value: "date", label: __("Start Date") },
+ ],
+ },
+ {
+ fieldname: "include_draft_timesheets",
+ label: __("Include Timesheets in Draft Status"),
+ fieldtype: "Check",
+ },
+ ],
+};
+
+function unset_group_by(report, fieldname) {
+ if (report.get_filter_value(fieldname) && report.get_filter_value("group_by") == fieldname) {
+ report.set_filter_value("group_by", "");
+ }
+}
diff --git a/erpnext/projects/report/employee_billing_summary/employee_billing_summary.json b/erpnext/projects/report/timesheet_billing_summary/timesheet_billing_summary.json
similarity index 61%
rename from erpnext/projects/report/employee_billing_summary/employee_billing_summary.json
rename to erpnext/projects/report/timesheet_billing_summary/timesheet_billing_summary.json
index e5626a0..0f070cb 100644
--- a/erpnext/projects/report/employee_billing_summary/employee_billing_summary.json
+++ b/erpnext/projects/report/timesheet_billing_summary/timesheet_billing_summary.json
@@ -1,36 +1,42 @@
{
"add_total_row": 1,
- "creation": "2019-03-08 15:08:19.929728",
- "disable_prepared_report": 0,
+ "columns": [],
+ "creation": "2023-10-10 23:53:43.692067",
"disabled": 0,
"docstatus": 0,
"doctype": "Report",
+ "filters": [],
"idx": 0,
"is_standard": "Yes",
- "modified": "2019-06-13 15:54:49.213973",
+ "letter_head": "ALYF GmbH",
+ "letterhead": null,
+ "modified": "2023-10-11 00:58:30.639078",
"modified_by": "Administrator",
"module": "Projects",
- "name": "Employee Billing Summary",
+ "name": "Timesheet Billing Summary",
"owner": "Administrator",
"prepared_report": 0,
"ref_doctype": "Timesheet",
- "report_name": "Employee Billing Summary",
+ "report_name": "Timesheet Billing Summary",
"report_type": "Script Report",
"roles": [
{
"role": "Projects User"
},
{
- "role": "HR User"
+ "role": "Employee"
+ },
+ {
+ "role": "Accounts User"
},
{
"role": "Manufacturing User"
},
{
- "role": "Employee"
+ "role": "HR User"
},
{
- "role": "Accounts User"
+ "role": "Employee Self Service"
}
]
}
\ No newline at end of file
diff --git a/erpnext/projects/report/timesheet_billing_summary/timesheet_billing_summary.py b/erpnext/projects/report/timesheet_billing_summary/timesheet_billing_summary.py
new file mode 100644
index 0000000..a6e7150
--- /dev/null
+++ b/erpnext/projects/report/timesheet_billing_summary/timesheet_billing_summary.py
@@ -0,0 +1,146 @@
+import frappe
+from frappe import _
+from frappe.model.docstatus import DocStatus
+
+
+def execute(filters=None):
+ group_fieldname = filters.pop("group_by", None)
+
+ filters = frappe._dict(filters or {})
+ columns = get_columns(filters, group_fieldname)
+
+ data = get_data(filters, group_fieldname)
+ return columns, data
+
+
+def get_columns(filters, group_fieldname=None):
+ group_columns = {
+ "date": {
+ "label": _("Date"),
+ "fieldtype": "Date",
+ "fieldname": "date",
+ "width": 150,
+ },
+ "project": {
+ "label": _("Project"),
+ "fieldtype": "Link",
+ "fieldname": "project",
+ "options": "Project",
+ "width": 200,
+ "hidden": int(bool(filters.get("project"))),
+ },
+ "employee": {
+ "label": _("Employee ID"),
+ "fieldtype": "Link",
+ "fieldname": "employee",
+ "options": "Employee",
+ "width": 200,
+ "hidden": int(bool(filters.get("employee"))),
+ },
+ }
+ columns = []
+ if group_fieldname:
+ columns.append(group_columns.get(group_fieldname))
+ columns.extend(
+ column for column in group_columns.values() if column.get("fieldname") != group_fieldname
+ )
+ else:
+ columns.extend(group_columns.values())
+
+ columns.extend(
+ [
+ {
+ "label": _("Employee Name"),
+ "fieldtype": "data",
+ "fieldname": "employee_name",
+ "hidden": 1,
+ },
+ {
+ "label": _("Timesheet"),
+ "fieldtype": "Link",
+ "fieldname": "timesheet",
+ "options": "Timesheet",
+ "width": 150,
+ },
+ {"label": _("Working Hours"), "fieldtype": "Float", "fieldname": "hours", "width": 150},
+ {
+ "label": _("Billing Hours"),
+ "fieldtype": "Float",
+ "fieldname": "billing_hours",
+ "width": 150,
+ },
+ {
+ "label": _("Billing Amount"),
+ "fieldtype": "Currency",
+ "fieldname": "billing_amount",
+ "width": 150,
+ },
+ ]
+ )
+
+ return columns
+
+
+def get_data(filters, group_fieldname=None):
+ _filters = []
+ if filters.get("employee"):
+ _filters.append(("employee", "=", filters.get("employee")))
+ if filters.get("project"):
+ _filters.append(("Timesheet Detail", "project", "=", filters.get("project")))
+ if filters.get("from_date"):
+ _filters.append(("Timesheet Detail", "from_time", ">=", filters.get("from_date")))
+ if filters.get("to_date"):
+ _filters.append(("Timesheet Detail", "to_time", "<=", filters.get("to_date")))
+ if not filters.get("include_draft_timesheets"):
+ _filters.append(("docstatus", "=", DocStatus.submitted()))
+ else:
+ _filters.append(("docstatus", "in", (DocStatus.submitted(), DocStatus.draft())))
+
+ data = frappe.get_list(
+ "Timesheet",
+ fields=[
+ "name as timesheet",
+ "`tabTimesheet`.employee",
+ "`tabTimesheet`.employee_name",
+ "`tabTimesheet Detail`.from_time as date",
+ "`tabTimesheet Detail`.project",
+ "`tabTimesheet Detail`.hours",
+ "`tabTimesheet Detail`.billing_hours",
+ "`tabTimesheet Detail`.billing_amount",
+ ],
+ filters=_filters,
+ order_by="`tabTimesheet Detail`.from_time",
+ )
+
+ return group_by(data, group_fieldname) if group_fieldname else data
+
+
+def group_by(data, fieldname):
+ groups = {row.get(fieldname) for row in data}
+ grouped_data = []
+ for group in sorted(groups):
+ group_row = {
+ fieldname: group,
+ "hours": sum(row.get("hours") for row in data if row.get(fieldname) == group),
+ "billing_hours": sum(row.get("billing_hours") for row in data if row.get(fieldname) == group),
+ "billing_amount": sum(row.get("billing_amount") for row in data if row.get(fieldname) == group),
+ "indent": 0,
+ "is_group": 1,
+ }
+ if fieldname == "employee":
+ group_row["employee_name"] = next(
+ row.get("employee_name") for row in data if row.get(fieldname) == group
+ )
+
+ grouped_data.append(group_row)
+ for row in data:
+ if row.get(fieldname) != group:
+ continue
+
+ _row = row.copy()
+ _row[fieldname] = None
+ _row["indent"] = 1
+ _row["is_group"] = 0
+ grouped_data.append(_row)
+
+ return grouped_data
diff --git a/erpnext/projects/workspace/projects/projects.json b/erpnext/projects/workspace/projects/projects.json
index 94ae9c0..e6bead9 100644
--- a/erpnext/projects/workspace/projects/projects.json
+++ b/erpnext/projects/workspace/projects/projects.json
@@ -155,9 +155,9 @@
"dependencies": "Project",
"hidden": 0,
"is_query_report": 1,
- "label": "Project Billing Summary",
+ "label": "Timesheet Billing Summary",
"link_count": 0,
- "link_to": "Project Billing Summary",
+ "link_to": "Timesheet Billing Summary",
"link_type": "Report",
"onboard": 0,
"type": "Link"
@@ -192,7 +192,7 @@
"type": "Link"
}
],
- "modified": "2023-07-04 14:39:08.935853",
+ "modified": "2023-10-10 23:54:33.082108",
"modified_by": "Administrator",
"module": "Projects",
"name": "Projects",
@@ -234,8 +234,8 @@
"type": "DocType"
},
{
- "label": "Project Billing Summary",
- "link_to": "Project Billing Summary",
+ "label": "Timesheet Billing Summary",
+ "link_to": "Timesheet Billing Summary",
"type": "Report"
},
{
diff --git a/erpnext/public/js/controllers/transaction.js b/erpnext/public/js/controllers/transaction.js
index b0a9e40..2c40f49 100644
--- a/erpnext/public/js/controllers/transaction.js
+++ b/erpnext/public/js/controllers/transaction.js
@@ -1772,7 +1772,7 @@
if(frappe.meta.has_field(me.frm.doc.doctype, fieldname) && !["Purchase Order","Purchase Invoice"].includes(me.frm.doc.doctype)) {
if (!me.frm.doc[fieldname]) {
frappe.msgprint(__("Please specify") + ": " +
- frappe.meta.get_label(me.frm.doc.doctype, fieldname, me.frm.doc.name) +
+ __(frappe.meta.get_label(me.frm.doc.doctype, fieldname, me.frm.doc.name)) +
". " + __("It is needed to fetch Item Details."));
valid = false;
}
diff --git a/erpnext/public/js/utils/party.js b/erpnext/public/js/utils/party.js
index 5c41aa0..cba615c 100644
--- a/erpnext/public/js/utils/party.js
+++ b/erpnext/public/js/utils/party.js
@@ -4,7 +4,7 @@
frappe.provide("erpnext.utils");
const SALES_DOCTYPES = ['Quotation', 'Sales Order', 'Delivery Note', 'Sales Invoice'];
-const PURCHASE_DOCTYPES = ['Purchase Order', 'Purchase Receipt', 'Purchase Invoice'];
+const PURCHASE_DOCTYPES = ['Supplier Quotation','Purchase Order', 'Purchase Receipt', 'Purchase Invoice'];
erpnext.utils.get_party_details = function(frm, method, args, callback) {
if (!method) {
diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js
index fd2b6a4..79fd2eb 100644
--- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js
+++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.js
@@ -3,10 +3,10 @@
frappe.ui.form.on('Quality Procedure', {
refresh: function(frm) {
- frm.set_query("procedure","processes", (frm) =>{
+ frm.set_query('procedure', 'processes', (frm) =>{
return {
filters: {
- name: ["not in", [frm.parent_quality_procedure, frm.name]]
+ name: ['not in', [frm.parent_quality_procedure, frm.name]]
}
};
});
@@ -14,7 +14,8 @@
frm.set_query('parent_quality_procedure', function(){
return {
filters: {
- is_group: 1
+ is_group: 1,
+ name: ['!=', frm.doc.name]
}
};
});
diff --git a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py
index e860408..6834abc 100644
--- a/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py
+++ b/erpnext/quality_management/doctype/quality_procedure/quality_procedure.py
@@ -16,16 +16,13 @@
def on_update(self):
NestedSet.on_update(self)
self.set_parent()
+ self.remove_parent_from_old_child()
+ self.add_child_to_parent()
+ self.remove_child_from_old_parent()
def after_insert(self):
self.set_parent()
-
- # add child to parent if missing
- if self.parent_quality_procedure:
- parent = frappe.get_doc("Quality Procedure", self.parent_quality_procedure)
- if not [d for d in parent.processes if d.procedure == self.name]:
- parent.append("processes", {"procedure": self.name, "process_description": self.name})
- parent.save()
+ self.add_child_to_parent()
def on_trash(self):
# clear from child table (sub procedures)
@@ -36,15 +33,6 @@
)
NestedSet.on_trash(self, allow_root_deletion=True)
- def set_parent(self):
- for process in self.processes:
- # Set parent for only those children who don't have a parent
- has_parent = frappe.db.get_value(
- "Quality Procedure", process.procedure, "parent_quality_procedure"
- )
- if not has_parent and process.procedure:
- frappe.db.set_value(self.doctype, process.procedure, "parent_quality_procedure", self.name)
-
def check_for_incorrect_child(self):
for process in self.processes:
if process.procedure:
@@ -61,6 +49,48 @@
title=_("Invalid Child Procedure"),
)
+ def set_parent(self):
+ """Set `Parent Procedure` in `Child Procedures`"""
+
+ for process in self.processes:
+ if process.procedure:
+ if not frappe.db.get_value("Quality Procedure", process.procedure, "parent_quality_procedure"):
+ frappe.db.set_value(
+ "Quality Procedure", process.procedure, "parent_quality_procedure", self.name
+ )
+
+ def remove_parent_from_old_child(self):
+ """Remove `Parent Procedure` from `Old Child Procedures`"""
+
+ if old_doc := self.get_doc_before_save():
+ if old_child_procedures := set([d.procedure for d in old_doc.processes if d.procedure]):
+ current_child_procedures = set([d.procedure for d in self.processes if d.procedure])
+
+ if removed_child_procedures := list(old_child_procedures.difference(current_child_procedures)):
+ for child_procedure in removed_child_procedures:
+ frappe.db.set_value("Quality Procedure", child_procedure, "parent_quality_procedure", None)
+
+ def add_child_to_parent(self):
+ """Add `Child Procedure` to `Parent Procedure`"""
+
+ if self.parent_quality_procedure:
+ parent = frappe.get_doc("Quality Procedure", self.parent_quality_procedure)
+ if not [d for d in parent.processes if d.procedure == self.name]:
+ parent.append("processes", {"procedure": self.name, "process_description": self.name})
+ parent.save()
+
+ def remove_child_from_old_parent(self):
+ """Remove `Child Procedure` from `Old Parent Procedure`"""
+
+ if old_doc := self.get_doc_before_save():
+ if old_parent := old_doc.parent_quality_procedure:
+ if self.parent_quality_procedure != old_parent:
+ parent = frappe.get_doc("Quality Procedure", old_parent)
+ for process in parent.processes:
+ if process.procedure == self.name:
+ parent.remove(process)
+ parent.save()
+
@frappe.whitelist()
def get_children(doctype, parent=None, parent_quality_procedure=None, is_root=False):
diff --git a/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py b/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py
index 04e8211..467186d 100644
--- a/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py
+++ b/erpnext/quality_management/doctype/quality_procedure/test_quality_procedure.py
@@ -1,56 +1,107 @@
# Copyright (c) 2018, Frappe and Contributors
# See license.txt
-import unittest
-
import frappe
+from frappe.tests.utils import FrappeTestCase
from .quality_procedure import add_node
-class TestQualityProcedure(unittest.TestCase):
+class TestQualityProcedure(FrappeTestCase):
def test_add_node(self):
- try:
- procedure = frappe.get_doc(
- dict(
- doctype="Quality Procedure",
- quality_procedure_name="Test Procedure 1",
- processes=[dict(process_description="Test Step 1")],
- )
- ).insert()
-
- frappe.local.form_dict = frappe._dict(
- doctype="Quality Procedure",
- quality_procedure_name="Test Child 1",
- parent_quality_procedure=procedure.name,
- cmd="test",
- is_root="false",
- )
- node = add_node()
-
- procedure.reload()
-
- self.assertEqual(procedure.is_group, 1)
-
- # child row created
- self.assertTrue([d for d in procedure.processes if d.procedure == node.name])
-
- node.delete()
- procedure.reload()
-
- # child unset
- self.assertFalse([d for d in procedure.processes if d.name == node.name])
-
- finally:
- procedure.delete()
-
-
-def create_procedure():
- return frappe.get_doc(
- dict(
- doctype="Quality Procedure",
- quality_procedure_name="Test Procedure 1",
- is_group=1,
- processes=[dict(process_description="Test Step 1")],
+ procedure = create_procedure(
+ {
+ "quality_procedure_name": "Test Procedure 1",
+ "is_group": 1,
+ "processes": [dict(process_description="Test Step 1")],
+ }
)
- ).insert()
+
+ frappe.local.form_dict = frappe._dict(
+ doctype="Quality Procedure",
+ quality_procedure_name="Test Child 1",
+ parent_quality_procedure=procedure.name,
+ cmd="test",
+ is_root="false",
+ )
+ node = add_node()
+
+ procedure.reload()
+
+ self.assertEqual(procedure.is_group, 1)
+
+ # child row created
+ self.assertTrue([d for d in procedure.processes if d.procedure == node.name])
+
+ node.delete()
+ procedure.reload()
+
+ # child unset
+ self.assertFalse([d for d in procedure.processes if d.name == node.name])
+
+ def test_remove_parent_from_old_child(self):
+ child_qp = create_procedure(
+ {
+ "quality_procedure_name": "Test Child 1",
+ "is_group": 0,
+ }
+ )
+ group_qp = create_procedure(
+ {
+ "quality_procedure_name": "Test Group",
+ "is_group": 1,
+ "processes": [dict(procedure=child_qp.name)],
+ }
+ )
+
+ child_qp.reload()
+ self.assertEqual(child_qp.parent_quality_procedure, group_qp.name)
+
+ group_qp.reload()
+ del group_qp.processes[0]
+ group_qp.save()
+
+ child_qp.reload()
+ self.assertEqual(child_qp.parent_quality_procedure, None)
+
+ def remove_child_from_old_parent(self):
+ child_qp = create_procedure(
+ {
+ "quality_procedure_name": "Test Child 1",
+ "is_group": 0,
+ }
+ )
+ group_qp = create_procedure(
+ {
+ "quality_procedure_name": "Test Group",
+ "is_group": 1,
+ "processes": [dict(procedure=child_qp.name)],
+ }
+ )
+
+ group_qp.reload()
+ self.assertTrue([d for d in group_qp.processes if d.procedure == child_qp.name])
+
+ child_qp.reload()
+ self.assertEqual(child_qp.parent_quality_procedure, group_qp.name)
+
+ child_qp.parent_quality_procedure = None
+ child_qp.save()
+
+ group_qp.reload()
+ self.assertFalse([d for d in group_qp.processes if d.procedure == child_qp.name])
+
+
+def create_procedure(kwargs=None):
+ kwargs = frappe._dict(kwargs or {})
+
+ doc = frappe.new_doc("Quality Procedure")
+ doc.quality_procedure_name = kwargs.quality_procedure_name or "_Test Procedure"
+ doc.is_group = kwargs.is_group or 0
+
+ for process in kwargs.processes or []:
+ doc.append("processes", process)
+
+ doc.insert()
+
+ return doc
diff --git a/erpnext/selling/doctype/customer/customer.js b/erpnext/selling/doctype/customer/customer.js
index 42932ad..ddc7e2a 100644
--- a/erpnext/selling/doctype/customer/customer.js
+++ b/erpnext/selling/doctype/customer/customer.js
@@ -3,17 +3,32 @@
frappe.ui.form.on("Customer", {
setup: function(frm) {
-
+ frm.custom_make_buttons = {
+ "Opportunity": "Opportunity",
+ "Quotation": "Quotation",
+ "Sales Order": "Sales Order",
+ "Pricing Rule": "Pricing Rule",
+ };
frm.make_methods = {
- 'Quotation': () => frappe.model.open_mapped_doc({
- method: "erpnext.selling.doctype.customer.customer.make_quotation",
- frm: cur_frm
- }),
- 'Opportunity': () => frappe.model.open_mapped_doc({
- method: "erpnext.selling.doctype.customer.customer.make_opportunity",
- frm: cur_frm
- })
- }
+ "Quotation": () =>
+ frappe.model.open_mapped_doc({
+ method: "erpnext.selling.doctype.customer.customer.make_quotation",
+ frm: frm,
+ }),
+ "Sales Order": () =>
+ frappe.model.with_doctype("Sales Order", function () {
+ var so = frappe.model.get_new_doc("Sales Order");
+ so.customer = frm.doc.name; // Set the current customer as the SO customer
+ frappe.set_route("Form", "Sales Order", so.name);
+ }),
+ "Opportunity": () =>
+ frappe.model.open_mapped_doc({
+ method: "erpnext.selling.doctype.customer.customer.make_opportunity",
+ frm: frm,
+ }),
+ "Pricing Rule": () =>
+ erpnext.utils.make_pricing_rule(frm.doc.doctype, frm.doc.name),
+ };
frm.add_fetch('lead_name', 'company_name', 'customer_name');
frm.add_fetch('default_sales_partner','commission_rate','default_commission_rate');
@@ -146,9 +161,9 @@
{party_type: 'Customer', party: frm.doc.name, party_name: frm.doc.customer_name});
}, __('View'));
- frm.add_custom_button(__('Pricing Rule'), function () {
- erpnext.utils.make_pricing_rule(frm.doc.doctype, frm.doc.name);
- }, __('Create'));
+ for (const doctype in frm.make_methods) {
+ frm.add_custom_button(__(doctype), frm.make_methods[doctype], __("Create"));
+ }
frm.add_custom_button(__('Get Customer Group Details'), function () {
frm.trigger("get_customer_group_details");
diff --git a/erpnext/selling/doctype/customer/customer.json b/erpnext/selling/doctype/customer/customer.json
index 40cab9f..3b97123 100644
--- a/erpnext/selling/doctype/customer/customer.json
+++ b/erpnext/selling/doctype/customer/customer.json
@@ -134,7 +134,7 @@
"label": "Customer Type",
"oldfieldname": "customer_type",
"oldfieldtype": "Select",
- "options": "Company\nIndividual",
+ "options": "Company\nIndividual\nProprietorship\nPartnership",
"reqd": 1
},
{
@@ -584,7 +584,7 @@
"link_fieldname": "party"
}
],
- "modified": "2023-09-21 12:23:20.706020",
+ "modified": "2023-10-19 16:56:27.327035",
"modified_by": "Administrator",
"module": "Selling",
"name": "Customer",
diff --git a/erpnext/selling/doctype/customer/customer.py b/erpnext/selling/doctype/customer/customer.py
index a7a1aa2..459fc9f 100644
--- a/erpnext/selling/doctype/customer/customer.py
+++ b/erpnext/selling/doctype/customer/customer.py
@@ -186,6 +186,8 @@
self.db_set("customer_primary_contact", contact.name)
self.db_set("mobile_no", self.mobile_no)
self.db_set("email_id", self.email_id)
+ elif self.customer_primary_contact:
+ frappe.set_value("Contact", self.customer_primary_contact, "is_primary_contact", 1) # ensure
def create_primary_address(self):
from frappe.contacts.doctype.address.address import get_address_display
@@ -196,6 +198,8 @@
self.db_set("customer_primary_address", address.name)
self.db_set("primary_address", address_display)
+ elif self.customer_primary_address:
+ frappe.set_value("Address", self.customer_primary_address, "is_primary_address", 1) # ensure
def update_lead_status(self):
"""If Customer created from Lead, update lead status to "Converted"
@@ -303,22 +307,6 @@
)
-def create_contact(contact, party_type, party, email):
- """Create contact based on given contact name"""
- contact = contact.split(" ")
-
- contact = frappe.get_doc(
- {
- "doctype": "Contact",
- "first_name": contact[0],
- "last_name": len(contact) > 1 and contact[1] or "",
- }
- )
- contact.append("email_ids", dict(email_id=email, is_primary=1))
- contact.append("links", dict(link_doctype=party_type, link_name=party))
- contact.insert()
-
-
@frappe.whitelist()
def make_quotation(source_name, target_doc=None):
def set_missing_values(source, target):
@@ -495,6 +483,7 @@
primary_action={
"label": "Send Email",
"server_action": "erpnext.selling.doctype.customer.customer.send_emails",
+ "hide_on_success": True,
"args": {
"customer": customer,
"customer_outstanding": customer_outstanding,
@@ -635,24 +624,42 @@
def make_contact(args, is_primary_contact=1):
- contact = frappe.get_doc(
- {
- "doctype": "Contact",
- "first_name": args.get("name"),
- "is_primary_contact": is_primary_contact,
- "links": [{"link_doctype": args.get("doctype"), "link_name": args.get("name")}],
- }
- )
+ values = {
+ "doctype": "Contact",
+ "is_primary_contact": is_primary_contact,
+ "links": [{"link_doctype": args.get("doctype"), "link_name": args.get("name")}],
+ }
+ if args.customer_type == "Individual":
+ first, middle, last = parse_full_name(args.get("customer_name"))
+ values.update(
+ {
+ "first_name": first,
+ "middle_name": middle,
+ "last_name": last,
+ }
+ )
+ else:
+ values.update(
+ {
+ "company_name": args.get("customer_name"),
+ }
+ )
+ contact = frappe.get_doc(values)
+
if args.get("email_id"):
contact.add_email(args.get("email_id"), is_primary=True)
if args.get("mobile_no"):
contact.add_phone(args.get("mobile_no"), is_primary_mobile_no=True)
- contact.insert()
+
+ if flags := args.get("flags"):
+ contact.insert(ignore_permissions=flags.get("ignore_permissions"))
+ else:
+ contact.insert()
return contact
-def make_address(args, is_primary_address=1):
+def make_address(args, is_primary_address=1, is_shipping_address=1):
reqd_fields = []
for field in ["city", "country"]:
if not args.get(field):
@@ -668,16 +675,23 @@
address = frappe.get_doc(
{
"doctype": "Address",
- "address_title": args.get("name"),
+ "address_title": args.get("customer_name"),
"address_line1": args.get("address_line1"),
"address_line2": args.get("address_line2"),
"city": args.get("city"),
"state": args.get("state"),
"pincode": args.get("pincode"),
"country": args.get("country"),
+ "is_primary_address": is_primary_address,
+ "is_shipping_address": is_shipping_address,
"links": [{"link_doctype": args.get("doctype"), "link_name": args.get("name")}],
}
- ).insert()
+ )
+
+ if flags := args.get("flags"):
+ address.insert(ignore_permissions=flags.get("ignore_permissions"))
+ else:
+ address.insert()
return address
@@ -698,3 +712,13 @@
.where((dlink.link_name == customer) & (con.name.like(f"%{txt}%")))
.run()
)
+
+
+def parse_full_name(full_name: str) -> tuple[str, str | None, str | None]:
+ """Parse full name into first name, middle name and last name"""
+ names = full_name.split()
+ first_name = names[0]
+ middle_name = " ".join(names[1:-1]) if len(names) > 2 else None
+ last_name = names[-1] if len(names) > 1 else None
+
+ return first_name, middle_name, last_name
diff --git a/erpnext/selling/doctype/customer/test_customer.py b/erpnext/selling/doctype/customer/test_customer.py
index 6e737e4..29dbd4f 100644
--- a/erpnext/selling/doctype/customer/test_customer.py
+++ b/erpnext/selling/doctype/customer/test_customer.py
@@ -10,7 +10,11 @@
from erpnext.accounts.party import get_due_date
from erpnext.exceptions import PartyDisabled, PartyFrozen
-from erpnext.selling.doctype.customer.customer import get_credit_limit, get_customer_outstanding
+from erpnext.selling.doctype.customer.customer import (
+ get_credit_limit,
+ get_customer_outstanding,
+ parse_full_name,
+)
from erpnext.tests.utils import create_test_contact_and_address
test_ignore = ["Price List"]
@@ -373,6 +377,22 @@
frappe.db.set_single_value("Selling Settings", "cust_master_name", "Customer Name")
+ def test_parse_full_name(self):
+ first, middle, last = parse_full_name("John")
+ self.assertEqual(first, "John")
+ self.assertEqual(middle, None)
+ self.assertEqual(last, None)
+
+ first, middle, last = parse_full_name("John Doe")
+ self.assertEqual(first, "John")
+ self.assertEqual(middle, None)
+ self.assertEqual(last, "Doe")
+
+ first, middle, last = parse_full_name("John Michael Doe")
+ self.assertEqual(first, "John")
+ self.assertEqual(middle, "Michael")
+ self.assertEqual(last, "Doe")
+
def get_customer_dict(customer_name):
return {
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index eb6d63e..56ae196 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -219,7 +219,15 @@
def validate_with_previous_doc(self):
super(SalesOrder, self).validate_with_previous_doc(
- {"Quotation": {"ref_dn_field": "prevdoc_docname", "compare_fields": [["company", "="]]}}
+ {
+ "Quotation": {"ref_dn_field": "prevdoc_docname", "compare_fields": [["company", "="]]},
+ "Quotation Item": {
+ "ref_dn_field": "quotation_item",
+ "compare_fields": [["item_code", "="], ["uom", "="], ["conversion_factor", "="]],
+ "is_child_table": True,
+ "allow_duplicate_prev_row_id": True,
+ },
+ }
)
if cint(frappe.db.get_single_value("Selling Settings", "maintain_same_sales_rate")):
@@ -761,6 +769,8 @@
if target.company_address:
target.update(get_fetch_values("Delivery Note", "company_address", target.company_address))
+ # set target items names to ensure proper linking with packed_items
+ target.set_new_name()
make_packing_list(target)
def condition(doc):
diff --git a/erpnext/selling/page/point_of_sale/pos_controller.js b/erpnext/selling/page/point_of_sale/pos_controller.js
index db6255a..9bba4eb 100644
--- a/erpnext/selling/page/point_of_sale/pos_controller.js
+++ b/erpnext/selling/page/point_of_sale/pos_controller.js
@@ -548,6 +548,14 @@
if (!item_code)
return;
+ if (rate == undefined || rate == 0) {
+ frappe.show_alert({
+ message: __('Price is not set for the item.'),
+ indicator: 'orange'
+ });
+ frappe.utils.play_sound("error");
+ return;
+ }
const new_item = { item_code, batch_no, rate, uom, [field]: value };
if (serial_no) {
diff --git a/erpnext/selling/page/point_of_sale/pos_payment.js b/erpnext/selling/page/point_of_sale/pos_payment.js
index 89ce61ab..63711c5 100644
--- a/erpnext/selling/page/point_of_sale/pos_payment.js
+++ b/erpnext/selling/page/point_of_sale/pos_payment.js
@@ -203,7 +203,7 @@
const paid_amount = doc.paid_amount;
const items = doc.items;
- if (paid_amount == 0 || !items.length) {
+ if (!items.length || (paid_amount == 0 && doc.additional_discount_percentage != 100)) {
const message = items.length ? __("You cannot submit the order without payment.") : __("You cannot submit empty order.");
frappe.show_alert({ message, indicator: "orange" });
frappe.utils.play_sound("error");
diff --git a/erpnext/selling/report/address_and_contacts/address_and_contacts.py b/erpnext/selling/report/address_and_contacts/address_and_contacts.py
index 4542bdf..0a29d43 100644
--- a/erpnext/selling/report/address_and_contacts/address_and_contacts.py
+++ b/erpnext/selling/report/address_and_contacts/address_and_contacts.py
@@ -26,7 +26,7 @@
def get_columns(filters):
party_type = filters.get("party_type")
party_type_value = get_party_group(party_type)
- return [
+ columns = [
"{party_type}:Link/{party_type}".format(party_type=party_type),
"{party_value_type}::150".format(party_value_type=frappe.unscrub(str(party_type_value))),
"Address Line 1",
@@ -43,6 +43,15 @@
"Email Id",
"Is Primary Contact:Check",
]
+ if filters.get("party_type") == "Supplier" and frappe.db.get_single_value(
+ "Buying Settings", "supp_master_name"
+ ) == ["Naming Series", "Auto Name"]:
+ columns.insert(1, "Supplier Name:Data:150")
+ if filters.get("party_type") == "Customer" and frappe.db.get_single_value(
+ "Selling Settings", "cust_master_name"
+ ) == ["Naming Series", "Auto Name"]:
+ columns.insert(1, "Customer Name:Data:150")
+ return columns
def get_data(filters):
@@ -50,27 +59,33 @@
party = filters.get("party_name")
party_group = get_party_group(party_type)
- return get_party_addresses_and_contact(party_type, party, party_group)
+ return get_party_addresses_and_contact(party_type, party, party_group, filters)
-def get_party_addresses_and_contact(party_type, party, party_group):
+def get_party_addresses_and_contact(party_type, party, party_group, filters):
data = []
- filters = None
+ query_filters = None
party_details = frappe._dict()
if not party_type:
return []
if party:
- filters = {"name": party}
+ query_filters = {"name": party}
+ if filters.get("party_type") in ["Customer", "Supplier"]:
+ field = filters.get("party_type").lower() + "_name"
+ else:
+ field = "partner_name"
fetch_party_list = frappe.get_list(
- party_type, filters=filters, fields=["name", party_group], as_list=True
+ party_type, filters=query_filters, fields=["name", party_group, field], as_list=True
)
party_list = [d[0] for d in fetch_party_list]
party_groups = {}
+ party_name_map = {}
for d in fetch_party_list:
party_groups[d[0]] = d[1]
+ party_name_map[d[0]] = d[2]
for d in party_list:
party_details.setdefault(d, frappe._dict())
@@ -84,6 +99,8 @@
if not any([addresses, contacts]):
result = [party]
result.append(party_groups[party])
+ if filters.get("party_type") in ["Customer", "Supplier"]:
+ result.append(party_name_map[party])
result.extend(add_blank_columns_for("Contact"))
result.extend(add_blank_columns_for("Address"))
data.append(result)
@@ -95,11 +112,12 @@
for idx in range(0, max_length):
result = [party]
result.append(party_groups[party])
+ if filters.get("party_type") in ["Customer", "Supplier"]:
+ result.append(party_name_map[party])
address = addresses[idx] if idx < len(addresses) else add_blank_columns_for("Address")
contact = contacts[idx] if idx < len(contacts) else add_blank_columns_for("Contact")
result.extend(address)
result.extend(contact)
-
data.append(result)
return data
@@ -115,7 +133,6 @@
for d in records:
details = party_details.get(d[0])
details.setdefault(frappe.scrub(doctype), []).append(d[1:])
-
return party_details
diff --git a/erpnext/selling/report/territory_wise_sales/territory_wise_sales.py b/erpnext/selling/report/territory_wise_sales/territory_wise_sales.py
index 5dfc1db..ecb63d8 100644
--- a/erpnext/selling/report/territory_wise_sales/territory_wise_sales.py
+++ b/erpnext/selling/report/territory_wise_sales/territory_wise_sales.py
@@ -80,7 +80,7 @@
territory_orders = []
if t_quotation_names and sales_orders:
- list(filter(lambda x: x.quotation in t_quotation_names, sales_orders))
+ territory_orders = list(filter(lambda x: x.quotation in t_quotation_names, sales_orders))
t_order_names = []
if territory_orders:
t_order_names = [t.name for t in territory_orders]
diff --git a/erpnext/setup/doctype/company/company.js b/erpnext/setup/doctype/company/company.js
index 23b93dc..1bd469b 100644
--- a/erpnext/setup/doctype/company/company.js
+++ b/erpnext/setup/doctype/company/company.js
@@ -40,7 +40,7 @@
filters:{
'warehouse_type' : 'Transit',
'is_group': 0,
- 'company': frm.doc.company
+ 'company': frm.doc.company_name
}
};
});
diff --git a/erpnext/stock/doctype/batch/batch.json b/erpnext/stock/doctype/batch/batch.json
index e6cb351..e20030a 100644
--- a/erpnext/stock/doctype/batch/batch.json
+++ b/erpnext/stock/doctype/batch/batch.json
@@ -61,6 +61,7 @@
"oldfieldname": "item",
"oldfieldtype": "Link",
"options": "Item",
+ "read_only_depends_on": "eval:!doc.__islocal",
"reqd": 1
},
{
@@ -207,7 +208,7 @@
"image_field": "image",
"links": [],
"max_attachments": 5,
- "modified": "2023-03-12 15:56:09.516586",
+ "modified": "2023-11-09 12:17:28.339975",
"modified_by": "Administrator",
"module": "Stock",
"name": "Batch",
@@ -224,7 +225,6 @@
"read": 1,
"report": 1,
"role": "Item Manager",
- "set_user_permissions": 1,
"share": 1,
"write": 1
}
diff --git a/erpnext/stock/doctype/bin/bin.json b/erpnext/stock/doctype/bin/bin.json
index a115727..312470d 100644
--- a/erpnext/stock/doctype/bin/bin.json
+++ b/erpnext/stock/doctype/bin/bin.json
@@ -5,20 +5,24 @@
"doctype": "DocType",
"engine": "InnoDB",
"field_order": [
- "warehouse",
"item_code",
- "reserved_qty",
+ "column_break_yreo",
+ "warehouse",
+ "section_break_stag",
"actual_qty",
- "ordered_qty",
- "indented_qty",
"planned_qty",
+ "indented_qty",
+ "ordered_qty",
"projected_qty",
+ "column_break_xn5j",
+ "reserved_qty",
"reserved_qty_for_production",
"reserved_qty_for_sub_contract",
"reserved_qty_for_production_plan",
- "ma_rate",
+ "reserved_stock",
+ "section_break_pmrs",
"stock_uom",
- "fcfs_rate",
+ "column_break_0slj",
"valuation_rate",
"stock_value"
],
@@ -56,7 +60,7 @@
"fieldname": "reserved_qty",
"fieldtype": "Float",
"in_list_view": 1,
- "label": "Reserved Quantity",
+ "label": "Reserved Qty",
"oldfieldname": "reserved_qty",
"oldfieldtype": "Currency",
"read_only": 1
@@ -67,7 +71,7 @@
"fieldtype": "Float",
"in_filter": 1,
"in_list_view": 1,
- "label": "Actual Quantity",
+ "label": "Actual Qty",
"oldfieldname": "actual_qty",
"oldfieldtype": "Currency",
"read_only": 1
@@ -77,7 +81,7 @@
"fieldname": "ordered_qty",
"fieldtype": "Float",
"in_list_view": 1,
- "label": "Ordered Quantity",
+ "label": "Ordered Qty",
"oldfieldname": "ordered_qty",
"oldfieldtype": "Currency",
"read_only": 1
@@ -86,7 +90,7 @@
"default": "0.00",
"fieldname": "indented_qty",
"fieldtype": "Float",
- "label": "Requested Quantity",
+ "label": "Requested Qty",
"oldfieldname": "indented_qty",
"oldfieldtype": "Currency",
"read_only": 1
@@ -116,21 +120,10 @@
{
"fieldname": "reserved_qty_for_sub_contract",
"fieldtype": "Float",
- "label": "Reserved Qty for sub contract",
+ "label": "Reserved Qty for Subcontract",
"read_only": 1
},
{
- "fieldname": "ma_rate",
- "fieldtype": "Float",
- "hidden": 1,
- "label": "Moving Average Rate",
- "oldfieldname": "ma_rate",
- "oldfieldtype": "Currency",
- "print_hide": 1,
- "read_only": 1,
- "report_hide": 1
- },
- {
"fieldname": "stock_uom",
"fieldtype": "Link",
"in_filter": 1,
@@ -141,17 +134,6 @@
"read_only": 1
},
{
- "fieldname": "fcfs_rate",
- "fieldtype": "Float",
- "hidden": 1,
- "label": "FCFS Rate",
- "oldfieldname": "fcfs_rate",
- "oldfieldtype": "Currency",
- "print_hide": 1,
- "read_only": 1,
- "report_hide": 1
- },
- {
"fieldname": "valuation_rate",
"fieldtype": "Float",
"label": "Valuation Rate",
@@ -172,13 +154,40 @@
"fieldtype": "Float",
"label": "Reserved Qty for Production Plan",
"read_only": 1
+ },
+ {
+ "fieldname": "section_break_stag",
+ "fieldtype": "Section Break"
+ },
+ {
+ "fieldname": "column_break_yreo",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "column_break_xn5j",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "section_break_pmrs",
+ "fieldtype": "Section Break"
+ },
+ {
+ "fieldname": "column_break_0slj",
+ "fieldtype": "Column Break"
+ },
+ {
+ "default": "0",
+ "fieldname": "reserved_stock",
+ "fieldtype": "Float",
+ "label": "Reserved Stock",
+ "read_only": 1
}
],
"hide_toolbar": 1,
"idx": 1,
"in_create": 1,
"links": [],
- "modified": "2023-05-02 23:26:21.806965",
+ "modified": "2023-11-01 16:51:17.079107",
"modified_by": "Administrator",
"module": "Stock",
"name": "Bin",
diff --git a/erpnext/stock/doctype/bin/bin.py b/erpnext/stock/doctype/bin/bin.py
index 5abea9e..8b2e5cf 100644
--- a/erpnext/stock/doctype/bin/bin.py
+++ b/erpnext/stock/doctype/bin/bin.py
@@ -34,10 +34,15 @@
get_reserved_qty_for_production_plan,
)
- self.reserved_qty_for_production_plan = get_reserved_qty_for_production_plan(
+ reserved_qty_for_production_plan = get_reserved_qty_for_production_plan(
self.item_code, self.warehouse
)
+ if reserved_qty_for_production_plan is None and not self.reserved_qty_for_production_plan:
+ return
+
+ self.reserved_qty_for_production_plan = flt(reserved_qty_for_production_plan)
+
self.db_set(
"reserved_qty_for_production_plan",
flt(self.reserved_qty_for_production_plan),
@@ -48,6 +53,29 @@
self.set_projected_qty()
self.db_set("projected_qty", self.projected_qty, update_modified=True)
+ def update_reserved_qty_for_for_sub_assembly(self):
+ from erpnext.manufacturing.doctype.production_plan.production_plan import (
+ get_reserved_qty_for_sub_assembly,
+ )
+
+ reserved_qty_for_production_plan = get_reserved_qty_for_sub_assembly(
+ self.item_code, self.warehouse
+ )
+
+ if reserved_qty_for_production_plan is None and not self.reserved_qty_for_production_plan:
+ return
+
+ self.reserved_qty_for_production_plan = flt(reserved_qty_for_production_plan)
+ self.set_projected_qty()
+
+ self.db_set(
+ {
+ "projected_qty": self.projected_qty,
+ "reserved_qty_for_production_plan": flt(self.reserved_qty_for_production_plan),
+ },
+ update_modified=True,
+ )
+
def update_reserved_qty_for_production(self):
"""Update qty reserved for production from Production Item tables
in open work orders"""
@@ -148,6 +176,17 @@
self.set_projected_qty()
self.db_set("projected_qty", self.projected_qty, update_modified=True)
+ def update_reserved_stock(self):
+ """Update `Reserved Stock` on change in Reserved Qty of Stock Reservation Entry"""
+
+ from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import (
+ get_sre_reserved_qty_for_item_and_warehouse,
+ )
+
+ reserved_stock = get_sre_reserved_qty_for_item_and_warehouse(self.item_code, self.warehouse)
+
+ self.db_set("reserved_stock", flt(reserved_stock), update_modified=True)
+
def on_doctype_update():
frappe.db.add_unique("Bin", ["item_code", "warehouse"], constraint_name="unique_item_warehouse")
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.py b/erpnext/stock/doctype/delivery_note/delivery_note.py
index 190575e..66dd33a 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.py
@@ -365,6 +365,9 @@
# Update Stock Reservation Entry `Status` based on `Delivered Qty`.
sre_doc.update_status()
+ # Update Reserved Stock in Bin.
+ sre_doc.update_reserved_stock_in_bin()
+
qty_to_deliver -= qty_can_be_deliver
if self._action == "cancel":
@@ -427,6 +430,9 @@
# Update Stock Reservation Entry `Status` based on `Delivered Qty`.
sre_doc.update_status()
+ # Update Reserved Stock in Bin.
+ sre_doc.update_reserved_stock_in_bin()
+
qty_to_undelivered -= qty_can_be_undelivered
def validate_against_stock_reservation_entries(self):
diff --git a/erpnext/stock/doctype/delivery_note/test_delivery_note.py b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
index 1eecf6d..137c352 100644
--- a/erpnext/stock/doctype/delivery_note/test_delivery_note.py
+++ b/erpnext/stock/doctype/delivery_note/test_delivery_note.py
@@ -1029,6 +1029,7 @@
dn1 = create_delivery_note(is_return=1, return_against=dn.name, qty=-3)
si1 = make_sales_invoice(dn1.name)
+ si1.update_billed_amount_in_delivery_note = True
si1.insert()
si1.submit()
dn1.reload()
@@ -1037,6 +1038,7 @@
dn2 = create_delivery_note(is_return=1, return_against=dn.name, qty=-4)
si2 = make_sales_invoice(dn2.name)
+ si2.update_billed_amount_in_delivery_note = True
si2.insert()
si2.submit()
dn2.reload()
diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py
index 09d3dd1..a942f58 100644
--- a/erpnext/stock/doctype/item/test_item.py
+++ b/erpnext/stock/doctype/item/test_item.py
@@ -163,7 +163,7 @@
{
"item_code": "_Test Item With Item Tax Template",
"tax_category": "_Test Tax Category 2",
- "item_tax_template": "",
+ "item_tax_template": None,
},
{
"item_code": "_Test Item Inherit Group Item Tax Template 1",
@@ -178,7 +178,7 @@
{
"item_code": "_Test Item Inherit Group Item Tax Template 1",
"tax_category": "_Test Tax Category 2",
- "item_tax_template": "",
+ "item_tax_template": None,
},
{
"item_code": "_Test Item Inherit Group Item Tax Template 2",
@@ -193,7 +193,7 @@
{
"item_code": "_Test Item Inherit Group Item Tax Template 2",
"tax_category": "_Test Tax Category 2",
- "item_tax_template": "",
+ "item_tax_template": None,
},
{
"item_code": "_Test Item Override Group Item Tax Template",
@@ -208,12 +208,12 @@
{
"item_code": "_Test Item Override Group Item Tax Template",
"tax_category": "_Test Tax Category 2",
- "item_tax_template": "",
+ "item_tax_template": None,
},
]
expected_item_tax_map = {
- "": {},
+ None: {},
"_Test Account Excise Duty @ 10 - _TC": {"_Test Account Excise Duty - _TC": 10},
"_Test Account Excise Duty @ 12 - _TC": {"_Test Account Excise Duty - _TC": 12},
"_Test Account Excise Duty @ 15 - _TC": {"_Test Account Excise Duty - _TC": 15},
diff --git a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
index 7f0dc2d..8bbc660 100644
--- a/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
+++ b/erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py
@@ -205,7 +205,11 @@
)
docs = frappe.db.get_all(
"Asset",
- filters={receipt_document_type: item.receipt_document, "item_code": item.item_code},
+ filters={
+ receipt_document_type: item.receipt_document,
+ "item_code": item.item_code,
+ "docstatus": ["!=", 2],
+ },
fields=["name", "docstatus"],
)
if not docs or len(docs) != item.qty:
diff --git a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
index 2a4b6f3..d905fe5 100644
--- a/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
+++ b/erpnext/stock/doctype/purchase_receipt/purchase_receipt.py
@@ -7,7 +7,7 @@
from frappe.desk.notifications import clear_doctype_notifications
from frappe.model.mapper import get_mapped_doc
from frappe.query_builder.functions import CombineDatetime
-from frappe.utils import cint, flt, getdate, nowdate
+from frappe.utils import cint, flt, get_datetime, getdate, nowdate
from pypika import functions as fn
import erpnext
@@ -600,11 +600,10 @@
make_rate_difference_entry(d)
make_sub_contracting_gl_entries(d)
make_divisional_loss_gl_entry(d, outgoing_amount)
- elif (
- d.warehouse not in warehouse_with_no_account
- or d.rejected_warehouse not in warehouse_with_no_account
+ elif (d.warehouse and d.warehouse not in warehouse_with_no_account) or (
+ d.rejected_warehouse and d.rejected_warehouse not in warehouse_with_no_account
):
- warehouse_with_no_account.append(d.warehouse)
+ warehouse_with_no_account.append(d.warehouse or d.rejected_warehouse)
if d.is_fixed_asset:
self.update_assets(d, d.valuation_rate)
@@ -761,8 +760,12 @@
update_billing_percentage(pr_doc, update_modified=update_modified)
def reserve_stock_for_sales_order(self):
- if self.is_return or not cint(
- frappe.db.get_single_value("Stock Settings", "auto_reserve_stock_for_sales_order_on_purchase")
+ if (
+ self.is_return
+ or not frappe.db.get_single_value("Stock Settings", "enable_stock_reservation")
+ or not frappe.db.get_single_value(
+ "Stock Settings", "auto_reserve_stock_for_sales_order_on_purchase"
+ )
):
return
@@ -783,6 +786,11 @@
so_items_details_map.setdefault(item.sales_order, []).append(item_details)
if so_items_details_map:
+ if get_datetime("{} {}".format(self.posting_date, self.posting_time)) > get_datetime():
+ return frappe.msgprint(
+ _("Cannot create Stock Reservation Entries for future dated Purchase Receipts.")
+ )
+
for so, items_details in so_items_details_map.items():
so_doc = frappe.get_doc("Sales Order", so)
so_doc.create_stock_reservation_entries(
diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
index f5240a6..718f007 100644
--- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
+++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
@@ -900,7 +900,8 @@
"label": "Delivery Note Item",
"no_copy": 1,
"print_hide": 1,
- "read_only": 1
+ "read_only": 1,
+ "search_index": 1
},
{
"collapsible": 1,
@@ -1089,7 +1090,7 @@
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2023-10-19 10:50:58.071735",
+ "modified": "2023-10-30 17:32:24.560337",
"modified_by": "Administrator",
"module": "Stock",
"name": "Purchase Receipt Item",
diff --git a/erpnext/stock/doctype/quality_inspection_template/test_records.json b/erpnext/stock/doctype/quality_inspection_template/test_records.json
index 980f49a..2da99f6 100644
--- a/erpnext/stock/doctype/quality_inspection_template/test_records.json
+++ b/erpnext/stock/doctype/quality_inspection_template/test_records.json
@@ -1,5 +1,9 @@
[
{
+ "doctype": "Quality Inspection Parameter",
+ "parameter" : "_Test Param"
+ },
+ {
"quality_inspection_template_name" : "_Test Quality Inspection Template",
"doctype": "Quality Inspection Template",
"item_quality_inspection_parameter" : [
diff --git a/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py
index 1853f45..5b76e44 100644
--- a/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py
+++ b/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py
@@ -5,7 +5,7 @@
from unittest.mock import MagicMock, call
import frappe
-from frappe.tests.utils import FrappeTestCase
+from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import add_days, add_to_date, now, nowdate, today
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
@@ -137,8 +137,6 @@
item_code="_Test Item",
warehouse="_Test Warehouse - _TC",
based_on="Item and Warehouse",
- voucher_type="Sales Invoice",
- voucher_no="SI-1",
posting_date="2021-01-02",
posting_time="00:01:00",
)
@@ -148,8 +146,6 @@
riv1.flags.dont_run_in_test = True
riv1.submit()
_assert_status(riv1, "Queued")
- self.assertEqual(riv1.voucher_type, "Sales Invoice") # traceability
- self.assertEqual(riv1.voucher_no, "SI-1")
# newer than existing duplicate - riv1
riv2 = frappe.get_doc(riv_args.update({"posting_date": "2021-01-03"}))
@@ -200,6 +196,7 @@
riv.set_status("Skipped")
+ @change_settings("Stock Reposting Settings", {"item_based_reposting": 0})
def test_prevention_of_cancelled_transaction_riv(self):
frappe.flags.dont_execute_stock_reposts = True
@@ -377,6 +374,7 @@
accounts_settings.acc_frozen_upto = ""
accounts_settings.save()
+ @change_settings("Stock Reposting Settings", {"item_based_reposting": 0})
def test_create_repost_entry_for_cancelled_document(self):
pr = make_purchase_receipt(
company="_Test Company with perpetual inventory",
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 f96c184..f2bbf2b 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
@@ -121,7 +121,7 @@
def throw_error_message(self, message, exception=frappe.ValidationError):
frappe.throw(_(message), exception, title=_("Error"))
- def set_incoming_rate(self, row=None, save=False):
+ def set_incoming_rate(self, row=None, save=False, allow_negative_stock=False):
if self.type_of_transaction not in ["Inward", "Outward"] or self.voucher_type in [
"Installation Note",
"Job Card",
@@ -131,7 +131,9 @@
return
if self.type_of_transaction == "Outward":
- self.set_incoming_rate_for_outward_transaction(row, save)
+ self.set_incoming_rate_for_outward_transaction(
+ row, save, allow_negative_stock=allow_negative_stock
+ )
else:
self.set_incoming_rate_for_inward_transaction(row, save)
@@ -152,7 +154,9 @@
def get_serial_nos(self):
return [d.serial_no for d in self.entries if d.serial_no]
- def set_incoming_rate_for_outward_transaction(self, row=None, save=False):
+ def set_incoming_rate_for_outward_transaction(
+ self, row=None, save=False, allow_negative_stock=False
+ ):
sle = self.get_sle_for_outward_transaction()
if self.has_serial_no:
@@ -181,7 +185,8 @@
if self.docstatus == 1:
available_qty += flt(d.qty)
- self.validate_negative_batch(d.batch_no, available_qty)
+ if not allow_negative_stock:
+ self.validate_negative_batch(d.batch_no, available_qty)
d.stock_value_difference = flt(d.qty) * flt(d.incoming_rate)
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index c41349f..b06de2e 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -161,7 +161,7 @@
if self.is_enqueue_action():
frappe.msgprint(
_(
- "The task has been enqueued as a background job. In case there is any issue on processing in background, the system will add a comment about the error on this Stock Reconciliation and revert to the Draft stage"
+ "The task has been enqueued as a background job. In case there is any issue on processing in background, the system will add a comment about the error on this Stock Entry and revert to the Draft stage"
)
)
self.queue_action("submit", timeout=2000)
@@ -172,7 +172,7 @@
if self.is_enqueue_action():
frappe.msgprint(
_(
- "The task has been enqueued as a background job. In case there is any issue on processing in background, the system will add a comment about the error on this Stock Reconciliation and revert to the Submitted stage"
+ "The task has been enqueued as a background job. In case there is any issue on processing in background, the system will add a comment about the error on this Stock Entry and revert to the Submitted stage"
)
)
self.queue_action("cancel", timeout=2000)
diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
index 3e0610e..b640983 100644
--- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
@@ -1467,6 +1467,7 @@
self.assertEqual(se.items[0].item_name, item.item_name)
self.assertEqual(se.items[0].stock_uom, item.stock_uom)
+ @change_settings("Stock Reposting Settings", {"item_based_reposting": 0})
def test_reposting_for_depedent_warehouse(self):
from erpnext.stock.doctype.repost_item_valuation.repost_item_valuation import repost_sl_entries
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
diff --git a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
index 569f58a..dcbd9b2 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
+++ b/erpnext/stock/doctype/stock_ledger_entry/stock_ledger_entry.json
@@ -11,6 +11,7 @@
"warehouse",
"posting_date",
"posting_time",
+ "is_adjustment_entry",
"column_break_6",
"voucher_type",
"voucher_no",
@@ -333,6 +334,12 @@
"fieldname": "has_serial_no",
"fieldtype": "Check",
"label": "Has Serial No"
+ },
+ {
+ "default": "0",
+ "fieldname": "is_adjustment_entry",
+ "fieldtype": "Check",
+ "label": "Is Adjustment Entry"
}
],
"hide_toolbar": 1,
@@ -341,7 +348,7 @@
"in_create": 1,
"index_web_pages_for_search": 1,
"links": [],
- "modified": "2023-04-03 16:33:16.270722",
+ "modified": "2023-10-23 18:07:42.063615",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Ledger Entry",
diff --git a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index 98b4ffd..1bf143b 100644
--- a/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
@@ -6,7 +6,7 @@
import frappe
from frappe import _, bold, msgprint
from frappe.query_builder.functions import CombineDatetime, Sum
-from frappe.utils import cint, cstr, flt
+from frappe.utils import add_to_date, cint, cstr, flt
import erpnext
from erpnext.accounts.utils import get_company_default
@@ -88,9 +88,12 @@
self.repost_future_sle_and_gle()
self.delete_auto_created_batches()
- def set_current_serial_and_batch_bundle(self):
+ def set_current_serial_and_batch_bundle(self, voucher_detail_no=None, save=False) -> None:
"""Set Serial and Batch Bundle for each item"""
for item in self.items:
+ if voucher_detail_no and voucher_detail_no != item.name:
+ continue
+
item_details = frappe.get_cached_value(
"Item", item.item_code, ["has_serial_no", "has_batch_no"], as_dict=1
)
@@ -148,6 +151,7 @@
"warehouse": item.warehouse,
"posting_date": self.posting_date,
"posting_time": self.posting_time,
+ "ignore_voucher_nos": [self.name],
}
)
)
@@ -163,11 +167,36 @@
)
if not serial_and_batch_bundle.entries:
+ if voucher_detail_no:
+ return
+
continue
- item.current_serial_and_batch_bundle = serial_and_batch_bundle.save().name
+ serial_and_batch_bundle.save()
+ item.current_serial_and_batch_bundle = serial_and_batch_bundle.name
item.current_qty = abs(serial_and_batch_bundle.total_qty)
item.current_valuation_rate = abs(serial_and_batch_bundle.avg_rate)
+ if save:
+ sle_creation = frappe.db.get_value(
+ "Serial and Batch Bundle", item.serial_and_batch_bundle, "creation"
+ )
+ creation = add_to_date(sle_creation, seconds=-1)
+ item.db_set(
+ {
+ "current_serial_and_batch_bundle": item.current_serial_and_batch_bundle,
+ "current_qty": item.current_qty,
+ "current_valuation_rate": item.current_valuation_rate,
+ "creation": creation,
+ }
+ )
+
+ serial_and_batch_bundle.db_set(
+ {
+ "creation": creation,
+ "voucher_no": self.name,
+ "voucher_detail_no": voucher_detail_no,
+ }
+ )
def set_new_serial_and_batch_bundle(self):
for item in self.items:
@@ -413,6 +442,11 @@
sl_entries = []
for row in self.items:
+
+ if not row.qty and not row.valuation_rate and not row.current_qty:
+ self.make_adjustment_entry(row, sl_entries)
+ continue
+
item = frappe.get_cached_value(
"Item", row.item_code, ["has_serial_no", "has_batch_no"], as_dict=1
)
@@ -463,6 +497,21 @@
)
self.make_sl_entries(sl_entries, allow_negative_stock=allow_negative_stock)
+ def make_adjustment_entry(self, row, sl_entries):
+ from erpnext.stock.stock_ledger import get_stock_value_difference
+
+ difference_amount = get_stock_value_difference(
+ row.item_code, row.warehouse, self.posting_date, self.posting_time
+ )
+
+ if not difference_amount:
+ return
+
+ args = self.get_sle_for_items(row)
+ args.update({"stock_value_difference": -1 * difference_amount, "is_adjustment_entry": 1})
+
+ sl_entries.append(args)
+
def get_sle_for_serialized_items(self, row, sl_entries):
if row.current_serial_and_batch_bundle:
args = self.get_sle_for_items(row)
@@ -689,56 +738,84 @@
else:
self._cancel()
- def recalculate_current_qty(self, item_code, batch_no):
+ def recalculate_current_qty(self, voucher_detail_no, sle_creation, add_new_sle=False):
from erpnext.stock.stock_ledger import get_valuation_rate
sl_entries = []
+
for row in self.items:
- if (
- not (row.item_code == item_code and row.batch_no == batch_no)
- and not row.serial_and_batch_bundle
- ):
+ if voucher_detail_no != row.name:
continue
+ current_qty = 0.0
if row.current_serial_and_batch_bundle:
- self.recalculate_qty_for_serial_and_batch_bundle(row)
- continue
-
- current_qty = get_batch_qty_for_stock_reco(
- item_code, row.warehouse, batch_no, self.posting_date, self.posting_time, self.name
- )
+ current_qty = self.get_qty_for_serial_and_batch_bundle(row)
+ elif row.batch_no:
+ current_qty = get_batch_qty_for_stock_reco(
+ row.item_code, row.warehouse, row.batch_no, self.posting_date, self.posting_time, self.name
+ )
precesion = row.precision("current_qty")
- if flt(current_qty, precesion) == flt(row.current_qty, precesion):
- continue
+ if flt(current_qty, precesion) != flt(row.current_qty, precesion):
+ val_rate = get_valuation_rate(
+ row.item_code,
+ row.warehouse,
+ self.doctype,
+ self.name,
+ company=self.company,
+ batch_no=row.batch_no,
+ serial_and_batch_bundle=row.current_serial_and_batch_bundle,
+ )
- val_rate = get_valuation_rate(
- item_code, row.warehouse, self.doctype, self.name, company=self.company, batch_no=batch_no
- )
+ row.current_valuation_rate = val_rate
+ row.current_qty = current_qty
+ row.db_set(
+ {
+ "current_qty": row.current_qty,
+ "current_valuation_rate": row.current_valuation_rate,
+ "current_amount": flt(row.current_qty * row.current_valuation_rate),
+ }
+ )
- row.current_valuation_rate = val_rate
- if not row.current_qty and current_qty:
- sle = self.get_sle_for_items(row)
- sle.actual_qty = current_qty * -1
- sle.valuation_rate = val_rate
- sl_entries.append(sle)
+ if (
+ add_new_sle
+ and not frappe.db.get_value(
+ "Stock Ledger Entry",
+ {"voucher_detail_no": row.name, "actual_qty": ("<", 0), "is_cancelled": 0},
+ "name",
+ )
+ and (not row.current_serial_and_batch_bundle and not row.batch_no)
+ ):
+ self.set_current_serial_and_batch_bundle(voucher_detail_no, save=True)
+ row.reload()
- row.current_qty = current_qty
- row.db_set(
- {
- "current_qty": row.current_qty,
- "current_valuation_rate": row.current_valuation_rate,
- "current_amount": flt(row.current_qty * row.current_valuation_rate),
- }
- )
+ if row.current_qty > 0 and row.current_serial_and_batch_bundle:
+ new_sle = self.get_sle_for_items(row)
+ new_sle.actual_qty = row.current_qty * -1
+ new_sle.valuation_rate = row.current_valuation_rate
+ new_sle.creation_time = add_to_date(sle_creation, seconds=-1)
+ new_sle.serial_and_batch_bundle = row.current_serial_and_batch_bundle
+ new_sle.qty_after_transaction = 0.0
+ sl_entries.append(new_sle)
if sl_entries:
- self.make_sl_entries(sl_entries, allow_negative_stock=True)
+ self.make_sl_entries(sl_entries, allow_negative_stock=self.has_negative_stock_allowed())
+ if not frappe.db.exists("Repost Item Valuation", {"voucher_no": self.name, "status": "Queued"}):
+ self.repost_future_sle_and_gle(force=True)
- def recalculate_qty_for_serial_and_batch_bundle(self, row):
+ def has_negative_stock_allowed(self):
+ allow_negative_stock = cint(frappe.db.get_single_value("Stock Settings", "allow_negative_stock"))
+
+ if all(d.serial_and_batch_bundle and flt(d.qty) == flt(d.current_qty) for d in self.items):
+ allow_negative_stock = True
+
+ return allow_negative_stock
+
+ def get_qty_for_serial_and_batch_bundle(self, row):
doc = frappe.get_doc("Serial and Batch Bundle", row.current_serial_and_batch_bundle)
precision = doc.entries[0].precision("qty")
+ current_qty = 0
for d in doc.entries:
qty = (
get_batch_qty(
@@ -751,10 +828,12 @@
or 0
) * -1
- if flt(d.qty, precision) == flt(qty, precision):
- continue
+ if flt(d.qty, precision) != flt(qty, precision):
+ d.db_set("qty", qty)
- d.db_set("qty", qty)
+ current_qty += qty
+
+ return abs(current_qty)
def get_batch_qty_for_stock_reco(
diff --git a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
index 4817c8d..1ec99bf 100644
--- a/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
+++ b/erpnext/stock/doctype/stock_reconciliation/test_stock_reconciliation.py
@@ -674,6 +674,7 @@
self.assertEqual(flt(sl_entry.actual_qty), 1.0)
self.assertEqual(flt(sl_entry.qty_after_transaction), 1.0)
+ @change_settings("Stock Reposting Settings", {"item_based_reposting": 0})
def test_backdated_stock_reco_entry(self):
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
@@ -741,13 +742,6 @@
se2.cancel()
- self.assertTrue(frappe.db.exists("Repost Item Valuation", {"voucher_no": stock_reco.name}))
-
- self.assertEqual(
- frappe.db.get_value("Repost Item Valuation", {"voucher_no": stock_reco.name}, "status"),
- "Completed",
- )
-
sle = frappe.get_all(
"Stock Ledger Entry",
filters={"item_code": item_code, "warehouse": warehouse, "is_cancelled": 0},
@@ -765,6 +759,68 @@
self.assertEqual(flt(sle[0].actual_qty), flt(-100.0))
+ def test_backdated_stock_reco_entry_with_batch(self):
+ from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
+
+ item_code = self.make_item(
+ "Test New Batch Item ABCVSD",
+ {
+ "is_stock_item": 1,
+ "has_batch_no": 1,
+ "batch_number_series": "BNS9.####",
+ "create_new_batch": 1,
+ },
+ ).name
+
+ warehouse = "_Test Warehouse - _TC"
+
+ # Stock Reco for 100, Balace Qty 100
+ stock_reco = create_stock_reconciliation(
+ item_code=item_code,
+ posting_date=nowdate(),
+ posting_time="11:00:00",
+ warehouse=warehouse,
+ qty=100,
+ rate=100,
+ )
+
+ sles = frappe.get_all(
+ "Stock Ledger Entry",
+ fields=["actual_qty"],
+ filters={"voucher_no": stock_reco.name, "is_cancelled": 0},
+ )
+
+ self.assertEqual(len(sles), 1)
+
+ stock_reco.reload()
+ batch_no = get_batch_from_bundle(stock_reco.items[0].serial_and_batch_bundle)
+
+ # Stock Reco for 100, Balace Qty 100
+ stock_reco1 = create_stock_reconciliation(
+ item_code=item_code,
+ posting_date=add_days(nowdate(), -1),
+ posting_time="11:00:00",
+ batch_no=batch_no,
+ warehouse=warehouse,
+ qty=60,
+ rate=100,
+ )
+
+ sles = frappe.get_all(
+ "Stock Ledger Entry",
+ fields=["actual_qty"],
+ filters={"voucher_no": stock_reco.name, "is_cancelled": 0},
+ )
+
+ stock_reco1.reload()
+ new_batch_no = get_batch_from_bundle(stock_reco1.items[0].serial_and_batch_bundle)
+
+ self.assertEqual(len(sles), 2)
+
+ for row in sles:
+ if row.actual_qty < 0:
+ self.assertEqual(row.actual_qty, -60)
+
def test_update_stock_reconciliation_while_reposting(self):
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
diff --git a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
index ca19bbb..d9cbf95 100644
--- a/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
+++ b/erpnext/stock/doctype/stock_reconciliation_item/stock_reconciliation_item.json
@@ -205,6 +205,7 @@
"fieldname": "current_serial_and_batch_bundle",
"fieldtype": "Link",
"label": "Current Serial / Batch Bundle",
+ "no_copy": 1,
"options": "Serial and Batch Bundle",
"read_only": 1
},
@@ -216,7 +217,7 @@
],
"istable": 1,
"links": [],
- "modified": "2023-07-26 12:54:34.011915",
+ "modified": "2023-11-02 15:47:07.929550",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Reconciliation Item",
diff --git a/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json
index 7c712ce..68afd99 100644
--- a/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json
+++ b/erpnext/stock/doctype/stock_reposting_settings/stock_reposting_settings.json
@@ -50,7 +50,7 @@
"label": "Limit timeslot for Stock Reposting"
},
{
- "default": "0",
+ "default": "1",
"fieldname": "item_based_reposting",
"fieldtype": "Check",
"label": "Use Item based reposting"
@@ -70,7 +70,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
- "modified": "2023-05-04 16:14:29.080697",
+ "modified": "2023-11-01 16:14:29.080697",
"modified_by": "Administrator",
"module": "Stock",
"name": "Stock Reposting Settings",
diff --git a/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py b/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py
index 6b39965..0954282 100644
--- a/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py
+++ b/erpnext/stock/doctype/stock_reservation_entry/stock_reservation_entry.py
@@ -9,6 +9,8 @@
from frappe.query_builder.functions import Sum
from frappe.utils import cint, flt
+from erpnext.stock.utils import get_or_make_bin
+
class StockReservationEntry(Document):
def validate(self) -> None:
@@ -31,6 +33,7 @@
self.update_reserved_qty_in_voucher()
self.update_reserved_qty_in_pick_list()
self.update_status()
+ self.update_reserved_stock_in_bin()
def on_update_after_submit(self) -> None:
self.can_be_updated()
@@ -40,12 +43,14 @@
self.validate_reservation_based_on_serial_and_batch()
self.update_reserved_qty_in_voucher()
self.update_status()
+ self.update_reserved_stock_in_bin()
self.reload()
def on_cancel(self) -> None:
self.update_reserved_qty_in_voucher()
self.update_reserved_qty_in_pick_list()
self.update_status()
+ self.update_reserved_stock_in_bin()
def validate_amended_doc(self) -> None:
"""Raises an exception if document is amended."""
@@ -341,6 +346,13 @@
update_modified=update_modified,
)
+ def update_reserved_stock_in_bin(self) -> None:
+ """Updates `Reserved Stock` in Bin."""
+
+ bin_name = get_or_make_bin(self.item_code, self.warehouse)
+ bin_doc = frappe.get_cached_doc("Bin", bin_name)
+ bin_doc.update_reserved_stock()
+
def update_status(self, status: str = None, update_modified: bool = True) -> None:
"""Updates status based on Voucher Qty, Reserved Qty and Delivered Qty."""
@@ -681,6 +693,68 @@
return flt(reserved_qty[0][0])
+def get_sre_reserved_serial_nos_details(
+ item_code: str, warehouse: str, serial_nos: list = None
+) -> dict:
+ """Returns a dict of `Serial No` reserved in Stock Reservation Entry. The dict is like {serial_no: sre_name, ...}"""
+
+ sre = frappe.qb.DocType("Stock Reservation Entry")
+ sb_entry = frappe.qb.DocType("Serial and Batch Entry")
+ query = (
+ frappe.qb.from_(sre)
+ .inner_join(sb_entry)
+ .on(sre.name == sb_entry.parent)
+ .select(sb_entry.serial_no, sre.name)
+ .where(
+ (sre.docstatus == 1)
+ & (sre.item_code == item_code)
+ & (sre.warehouse == warehouse)
+ & (sre.reserved_qty > sre.delivered_qty)
+ & (sre.status.notin(["Delivered", "Cancelled"]))
+ & (sre.reservation_based_on == "Serial and Batch")
+ )
+ .orderby(sb_entry.creation)
+ )
+
+ if serial_nos:
+ query = query.where(sb_entry.serial_no.isin(serial_nos))
+
+ return frappe._dict(query.run())
+
+
+def get_sre_reserved_batch_nos_details(
+ item_code: str, warehouse: str, batch_nos: list = None
+) -> dict:
+ """Returns a dict of `Batch Qty` reserved in Stock Reservation Entry. The dict is like {batch_no: qty, ...}"""
+
+ sre = frappe.qb.DocType("Stock Reservation Entry")
+ sb_entry = frappe.qb.DocType("Serial and Batch Entry")
+ query = (
+ frappe.qb.from_(sre)
+ .inner_join(sb_entry)
+ .on(sre.name == sb_entry.parent)
+ .select(
+ sb_entry.batch_no,
+ Sum(sb_entry.qty - sb_entry.delivered_qty),
+ )
+ .where(
+ (sre.docstatus == 1)
+ & (sre.item_code == item_code)
+ & (sre.warehouse == warehouse)
+ & ((sre.reserved_qty - sre.delivered_qty) > 0)
+ & (sre.status.notin(["Delivered", "Cancelled"]))
+ & (sre.reservation_based_on == "Serial and Batch")
+ )
+ .groupby(sb_entry.batch_no)
+ .orderby(sb_entry.creation)
+ )
+
+ if batch_nos:
+ query = query.where(sb_entry.batch_no.isin(batch_nos))
+
+ return frappe._dict(query.run())
+
+
def get_sre_details_for_voucher(voucher_type: str, voucher_no: str) -> list[dict]:
"""Returns a list of SREs for the provided voucher."""
diff --git a/erpnext/stock/doctype/stock_reservation_entry/test_stock_reservation_entry.py b/erpnext/stock/doctype/stock_reservation_entry/test_stock_reservation_entry.py
index f4c74a8..dd023e2 100644
--- a/erpnext/stock/doctype/stock_reservation_entry/test_stock_reservation_entry.py
+++ b/erpnext/stock/doctype/stock_reservation_entry/test_stock_reservation_entry.py
@@ -286,6 +286,7 @@
self.assertEqual(item.stock_reserved_qty, sre_details.reserved_qty)
self.assertEqual(sre_details.status, "Partially Reserved")
+ cancel_stock_reservation_entries("Sales Order", so.name)
se.cancel()
# Test - 3: Stock should be fully Reserved if the Available Qty to Reserve is greater than the Un-reserved Qty.
@@ -493,7 +494,7 @@
"pick_serial_and_batch_based_on": "FIFO",
},
)
- def test_stock_reservation_from_pick_list(self):
+ def test_stock_reservation_from_pick_list(self) -> None:
items_details = create_items()
create_material_receipt(items_details, self.warehouse, qty=100)
@@ -575,7 +576,7 @@
"auto_reserve_stock_for_sales_order_on_purchase": 1,
},
)
- def test_stock_reservation_from_purchase_receipt(self):
+ def test_stock_reservation_from_purchase_receipt(self) -> None:
from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt
from erpnext.selling.doctype.sales_order.sales_order import make_material_request
from erpnext.stock.doctype.material_request.material_request import make_purchase_order
@@ -645,6 +646,40 @@
# Test - 3: Reserved Serial/Batch Nos should be equal to PR Item Serial/Batch Nos.
self.assertEqual(set(sb_details), set(reserved_sb_details))
+ @change_settings(
+ "Stock Settings",
+ {
+ "allow_negative_stock": 0,
+ "enable_stock_reservation": 1,
+ "auto_reserve_serial_and_batch": 1,
+ "pick_serial_and_batch_based_on": "FIFO",
+ },
+ )
+ def test_consider_reserved_stock_while_cancelling_an_inward_transaction(self) -> None:
+ items_details = create_items()
+ se = create_material_receipt(items_details, self.warehouse, qty=100)
+
+ item_list = []
+ for item_code, properties in items_details.items():
+ item_list.append(
+ {
+ "item_code": item_code,
+ "warehouse": self.warehouse,
+ "qty": randint(11, 100),
+ "uom": properties.stock_uom,
+ "rate": randint(10, 400),
+ }
+ )
+
+ so = make_sales_order(
+ item_list=item_list,
+ warehouse=self.warehouse,
+ )
+ so.create_stock_reservation_entries()
+
+ # Test - 1: ValidationError should be thrown as the inwarded stock is reserved.
+ self.assertRaises(frappe.ValidationError, se.cancel)
+
def tearDown(self) -> None:
cancel_all_stock_reservation_entries()
return super().tearDown()
diff --git a/erpnext/stock/get_item_details.py b/erpnext/stock/get_item_details.py
index e29fc88..c766cab 100644
--- a/erpnext/stock/get_item_details.py
+++ b/erpnext/stock/get_item_details.py
@@ -610,7 +610,6 @@
# all templates have validity and no template is valid
if not taxes_with_validity and (not taxes_with_no_validity):
- out["item_tax_template"] = ""
return None
# do not change if already a valid template
diff --git a/erpnext/stock/page/stock_balance/stock_balance.js b/erpnext/stock/page/stock_balance/stock_balance.js
index f00dd3e..90b8d45 100644
--- a/erpnext/stock/page/stock_balance/stock_balance.js
+++ b/erpnext/stock/page/stock_balance/stock_balance.js
@@ -11,6 +11,7 @@
label: __('Warehouse'),
fieldtype:'Link',
options:'Warehouse',
+ default: frappe.route_options && frappe.route_options.warehouse,
change: function() {
page.item_dashboard.start = 0;
page.item_dashboard.refresh();
@@ -22,6 +23,7 @@
label: __('Item'),
fieldtype:'Link',
options:'Item',
+ default: frappe.route_options && frappe.route_options.item_code,
change: function() {
page.item_dashboard.start = 0;
page.item_dashboard.refresh();
@@ -33,6 +35,7 @@
label: __('Item Group'),
fieldtype:'Link',
options:'Item Group',
+ default: frappe.route_options && frappe.route_options.item_group,
change: function() {
page.item_dashboard.start = 0;
page.item_dashboard.refresh();
diff --git a/erpnext/stock/report/stock_ledger/stock_ledger.py b/erpnext/stock/report/stock_ledger/stock_ledger.py
index eeef396..e59f2fe 100644
--- a/erpnext/stock/report/stock_ledger/stock_ledger.py
+++ b/erpnext/stock/report/stock_ledger/stock_ledger.py
@@ -249,6 +249,13 @@
"options": "Serial No",
"width": 100,
},
+ {
+ "label": _("Serial and Batch Bundle"),
+ "fieldname": "serial_and_batch_bundle",
+ "fieldtype": "Link",
+ "options": "Serial and Batch Bundle",
+ "width": 100,
+ },
{"label": _("Balance Serial No"), "fieldname": "balance_serial_no", "width": 100},
{
"label": _("Project"),
@@ -287,6 +294,7 @@
sle.voucher_type,
sle.qty_after_transaction,
sle.stock_value_difference,
+ sle.serial_and_batch_bundle,
sle.voucher_no,
sle.stock_value,
sle.batch_no,
diff --git a/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py b/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py
index ca15afe..fb392f7 100644
--- a/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py
+++ b/erpnext/stock/report/stock_ledger_invariant_check/stock_ledger_invariant_check.py
@@ -24,6 +24,7 @@
"stock_value_difference",
"valuation_rate",
"voucher_detail_no",
+ "serial_and_batch_bundle",
)
@@ -64,7 +65,11 @@
balance_qty += sle.actual_qty
balance_stock_value += sle.stock_value_difference
- if sle.voucher_type == "Stock Reconciliation" and not sle.batch_no:
+ if (
+ sle.voucher_type == "Stock Reconciliation"
+ and not sle.batch_no
+ and not sle.serial_and_batch_bundle
+ ):
balance_qty = frappe.db.get_value("Stock Reconciliation Item", sle.voucher_detail_no, "qty")
if balance_qty is None:
balance_qty = sle.qty_after_transaction
@@ -144,6 +149,12 @@
"options": "Batch",
},
{
+ "fieldname": "serial_and_batch_bundle",
+ "fieldtype": "Link",
+ "label": _("Serial and Batch Bundle"),
+ "options": "Serial and Batch Bundle",
+ },
+ {
"fieldname": "use_batchwise_valuation",
"fieldtype": "Check",
"label": _("Batchwise Valuation"),
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index b950f18..6390894 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -11,17 +11,22 @@
from frappe.model.meta import get_field_precision
from frappe.query_builder import Case
from frappe.query_builder.functions import CombineDatetime, Sum
-from frappe.utils import cint, flt, get_link_to_form, getdate, now, nowdate, parse_json
+from frappe.utils import cint, flt, get_link_to_form, getdate, now, nowdate, nowtime, parse_json
import erpnext
from erpnext.stock.doctype.bin.bin import update_qty as update_bin_qty
from erpnext.stock.doctype.inventory_dimension.inventory_dimension import get_inventory_dimensions
+from erpnext.stock.doctype.serial_and_batch_bundle.serial_and_batch_bundle import (
+ get_available_batches,
+)
from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import (
- get_sre_reserved_qty_for_item_and_warehouse as get_reserved_stock,
+ get_sre_reserved_batch_nos_details,
+ get_sre_reserved_serial_nos_details,
)
from erpnext.stock.utils import (
get_incoming_outgoing_rate_for_cancel,
get_or_make_bin,
+ get_stock_balance,
get_valuation_method,
)
from erpnext.stock.valuation import FIFOValuation, LIFOValuation, round_off_if_near_zero
@@ -88,6 +93,7 @@
is_stock_item = frappe.get_cached_value("Item", args.get("item_code"), "is_stock_item")
if is_stock_item:
bin_name = get_or_make_bin(args.get("item_code"), args.get("warehouse"))
+ args.reserved_stock = flt(frappe.db.get_value("Bin", bin_name, "reserved_stock"))
repost_current_voucher(args, allow_negative_stock, via_landed_cost_voucher)
update_bin_qty(bin_name, args)
else:
@@ -114,6 +120,7 @@
"voucher_no": args.get("voucher_no"),
"sle_id": args.get("name"),
"creation": args.get("creation"),
+ "reserved_stock": args.get("reserved_stock"),
},
allow_negative_stock=allow_negative_stock,
via_landed_cost_voucher=via_landed_cost_voucher,
@@ -203,6 +210,11 @@
sle.allow_negative_stock = allow_negative_stock
sle.via_landed_cost_voucher = via_landed_cost_voucher
sle.submit()
+
+ # Added to handle the case when the stock ledger entry is created from the repostig
+ if args.get("creation_time") and args.get("voucher_type") == "Stock Reconciliation":
+ sle.db_set("creation", args.get("creation_time"))
+
return sle
@@ -506,7 +518,7 @@
self.new_items_found = False
self.distinct_item_warehouses = args.get("distinct_item_warehouses", frappe._dict())
self.affected_transactions: Set[Tuple[str, str]] = set()
- self.reserved_stock = get_reserved_stock(self.args.item_code, self.args.warehouse)
+ self.reserved_stock = flt(self.args.reserved_stock)
self.data = frappe._dict()
self.initialize_previous_data(self.args)
@@ -689,9 +701,11 @@
if (
sle.voucher_type == "Stock Reconciliation"
- and (sle.batch_no or (sle.has_batch_no and sle.serial_and_batch_bundle))
+ and (
+ sle.batch_no or (sle.has_batch_no and sle.serial_and_batch_bundle and not sle.has_serial_no)
+ )
and sle.voucher_detail_no
- and sle.actual_qty < 0
+ and not self.args.get("sle_id")
):
self.reset_actual_qty_for_stock_reco(sle)
@@ -745,36 +759,33 @@
sle.valuation_rate = self.wh_data.valuation_rate
sle.stock_value = self.wh_data.stock_value
sle.stock_queue = json.dumps(self.wh_data.stock_queue)
- sle.stock_value_difference = stock_value_difference
- sle.doctype = "Stock Ledger Entry"
+ if not sle.is_adjustment_entry or not self.args.get("sle_id"):
+ sle.stock_value_difference = stock_value_difference
+
+ sle.doctype = "Stock Ledger Entry"
frappe.get_doc(sle).db_update()
if not self.args.get("sle_id"):
self.update_outgoing_rate_on_transaction(sle)
def reset_actual_qty_for_stock_reco(self, sle):
- if sle.serial_and_batch_bundle:
- current_qty = frappe.get_cached_value(
- "Serial and Batch Bundle", sle.serial_and_batch_bundle, "total_qty"
+ doc = frappe.get_cached_doc("Stock Reconciliation", sle.voucher_no)
+ doc.recalculate_current_qty(sle.voucher_detail_no, sle.creation, sle.actual_qty > 0)
+
+ if sle.actual_qty < 0:
+ sle.actual_qty = (
+ flt(frappe.db.get_value("Stock Reconciliation Item", sle.voucher_detail_no, "current_qty"))
+ * -1
)
- if current_qty is not None:
- current_qty = abs(current_qty)
- else:
- current_qty = frappe.get_cached_value(
- "Stock Reconciliation Item", sle.voucher_detail_no, "current_qty"
- )
-
- if current_qty:
- sle.actual_qty = current_qty * -1
- elif current_qty == 0:
- sle.is_cancelled = 1
+ if abs(sle.actual_qty) == 0.0:
+ sle.is_cancelled = 1
def calculate_valuation_for_serial_batch_bundle(self, sle):
doc = frappe.get_cached_doc("Serial and Batch Bundle", sle.serial_and_batch_bundle)
- doc.set_incoming_rate(save=True)
+ doc.set_incoming_rate(save=True, allow_negative_stock=self.allow_negative_stock)
doc.calculate_qty_and_amount(save=True)
self.wh_data.stock_value = round_off_if_near_zero(self.wh_data.stock_value + doc.total_amount)
@@ -1461,6 +1472,7 @@
currency=None,
company=None,
raise_error_if_no_rate=True,
+ batch_no=None,
serial_and_batch_bundle=None,
):
@@ -1469,6 +1481,25 @@
if not company:
company = frappe.get_cached_value("Warehouse", warehouse, "company")
+ if warehouse and batch_no and frappe.db.get_value("Batch", batch_no, "use_batchwise_valuation"):
+ table = frappe.qb.DocType("Stock Ledger Entry")
+ query = (
+ frappe.qb.from_(table)
+ .select(Sum(table.stock_value_difference) / Sum(table.actual_qty))
+ .where(
+ (table.item_code == item_code)
+ & (table.warehouse == warehouse)
+ & (table.batch_no == batch_no)
+ & (table.is_cancelled == 0)
+ & (table.voucher_no != voucher_no)
+ & (table.voucher_type != voucher_type)
+ )
+ )
+
+ last_valuation_rate = query.run()
+ if last_valuation_rate:
+ return flt(last_valuation_rate[0][0])
+
# Get moving average rate of a specific batch number
if warehouse and serial_and_batch_bundle:
batch_obj = BatchNoValuation(
@@ -1563,8 +1594,6 @@
next_stock_reco_detail = get_next_stock_reco(args)
if next_stock_reco_detail:
detail = next_stock_reco_detail[0]
- if detail.batch_no or (detail.serial_and_batch_bundle and detail.has_batch_no):
- regenerate_sle_for_batch_stock_reco(detail)
# add condition to update SLEs before this date & time
datetime_limit_condition = get_datetime_limit_condition(detail)
@@ -1593,16 +1622,6 @@
validate_negative_qty_in_future_sle(args, allow_negative_stock)
-def regenerate_sle_for_batch_stock_reco(detail):
- doc = frappe.get_cached_doc("Stock Reconciliation", detail.voucher_no)
- doc.recalculate_current_qty(detail.item_code, detail.batch_no)
-
- if not frappe.db.exists(
- "Repost Item Valuation", {"voucher_no": doc.name, "status": "Queued", "docstatus": "1"}
- ):
- doc.repost_future_sle_and_gle(force=True)
-
-
def get_stock_reco_qty_shift(args):
stock_reco_qty_shift = 0
if args.get("is_cancelled"):
@@ -1709,22 +1728,23 @@
frappe.throw(message, NegativeStockError, title=_("Insufficient Stock"))
- if not args.batch_no:
- return
+ if args.batch_no:
+ neg_batch_sle = get_future_sle_with_negative_batch_qty(args)
+ if is_negative_with_precision(neg_batch_sle, is_batch=True):
+ message = _(
+ "{0} units of {1} needed in {2} on {3} {4} for {5} to complete this transaction."
+ ).format(
+ abs(neg_batch_sle[0]["cumulative_total"]),
+ frappe.get_desk_link("Batch", args.batch_no),
+ frappe.get_desk_link("Warehouse", args.warehouse),
+ neg_batch_sle[0]["posting_date"],
+ neg_batch_sle[0]["posting_time"],
+ frappe.get_desk_link(neg_batch_sle[0]["voucher_type"], neg_batch_sle[0]["voucher_no"]),
+ )
+ frappe.throw(message, NegativeStockError, title=_("Insufficient Stock for Batch"))
- neg_batch_sle = get_future_sle_with_negative_batch_qty(args)
- if is_negative_with_precision(neg_batch_sle, is_batch=True):
- message = _(
- "{0} units of {1} needed in {2} on {3} {4} for {5} to complete this transaction."
- ).format(
- abs(neg_batch_sle[0]["cumulative_total"]),
- frappe.get_desk_link("Batch", args.batch_no),
- frappe.get_desk_link("Warehouse", args.warehouse),
- neg_batch_sle[0]["posting_date"],
- neg_batch_sle[0]["posting_time"],
- frappe.get_desk_link(neg_batch_sle[0]["voucher_type"], neg_batch_sle[0]["voucher_no"]),
- )
- frappe.throw(message, NegativeStockError, title=_("Insufficient Stock for Batch"))
+ if args.reserved_stock:
+ validate_reserved_stock(args)
def is_negative_with_precision(neg_sle, is_batch=False):
@@ -1791,6 +1811,96 @@
)
+def validate_reserved_stock(kwargs):
+ if kwargs.serial_no:
+ serial_nos = kwargs.serial_no.split("\n")
+ validate_reserved_serial_nos(kwargs.item_code, kwargs.warehouse, serial_nos)
+
+ elif kwargs.batch_no:
+ validate_reserved_batch_nos(kwargs.item_code, kwargs.warehouse, [kwargs.batch_no])
+
+ elif kwargs.serial_and_batch_bundle:
+ sbb_entries = frappe.db.get_all(
+ "Serial and Batch Entry",
+ {
+ "parenttype": "Serial and Batch Bundle",
+ "parent": kwargs.serial_and_batch_bundle,
+ "docstatus": 1,
+ },
+ ["batch_no", "serial_no"],
+ )
+
+ if serial_nos := [entry.serial_no for entry in sbb_entries if entry.serial_no]:
+ validate_reserved_serial_nos(kwargs.item_code, kwargs.warehouse, serial_nos)
+ elif batch_nos := [entry.batch_no for entry in sbb_entries if entry.batch_no]:
+ validate_reserved_batch_nos(kwargs.item_code, kwargs.warehouse, batch_nos)
+
+ # Qty based validation for non-serial-batch items OR SRE with Reservation Based On Qty.
+ precision = cint(frappe.db.get_default("float_precision")) or 2
+ balance_qty = get_stock_balance(kwargs.item_code, kwargs.warehouse)
+
+ diff = flt(balance_qty - kwargs.get("reserved_stock", 0), precision)
+ if diff < 0 and abs(diff) > 0.0001:
+ msg = _("{0} units of {1} needed in {2} on {3} {4} to complete this transaction.").format(
+ abs(diff),
+ frappe.get_desk_link("Item", kwargs.item_code),
+ frappe.get_desk_link("Warehouse", kwargs.warehouse),
+ nowdate(),
+ nowtime(),
+ )
+ frappe.throw(msg, title=_("Reserved Stock"))
+
+
+def validate_reserved_serial_nos(item_code, warehouse, serial_nos):
+ if reserved_serial_nos_details := get_sre_reserved_serial_nos_details(
+ item_code, warehouse, serial_nos
+ ):
+ if common_serial_nos := list(
+ set(serial_nos).intersection(set(reserved_serial_nos_details.keys()))
+ ):
+ msg = _(
+ "Serial Nos are reserved in Stock Reservation Entries, you need to unreserve them before proceeding."
+ )
+ msg += "<br />"
+ msg += _("Example: Serial No {0} reserved in {1}.").format(
+ frappe.bold(common_serial_nos[0]),
+ frappe.get_desk_link(
+ "Stock Reservation Entry", reserved_serial_nos_details[common_serial_nos[0]]
+ ),
+ )
+ frappe.throw(msg, title=_("Reserved Serial No."))
+
+
+def validate_reserved_batch_nos(item_code, warehouse, batch_nos):
+ if reserved_batches_map := get_sre_reserved_batch_nos_details(item_code, warehouse, batch_nos):
+ available_batches = get_available_batches(
+ frappe._dict(
+ {
+ "item_code": item_code,
+ "warehouse": warehouse,
+ "posting_date": nowdate(),
+ "posting_time": nowtime(),
+ }
+ )
+ )
+ available_batches_map = {row.batch_no: row.qty for row in available_batches}
+ precision = cint(frappe.db.get_default("float_precision")) or 2
+
+ for batch_no in batch_nos:
+ diff = flt(
+ available_batches_map.get(batch_no, 0) - reserved_batches_map.get(batch_no, 0), precision
+ )
+ if diff < 0 and abs(diff) > 0.0001:
+ msg = _("{0} units of {1} needed in {2} on {3} {4} to complete this transaction.").format(
+ abs(diff),
+ frappe.get_desk_link("Batch", batch_no),
+ frappe.get_desk_link("Warehouse", warehouse),
+ nowdate(),
+ nowtime(),
+ )
+ frappe.throw(msg, title=_("Reserved Stock for Batch"))
+
+
def is_negative_stock_allowed(*, item_code: Optional[str] = None) -> bool:
if cint(frappe.db.get_single_value("Stock Settings", "allow_negative_stock", cache=True)):
return True
@@ -1831,3 +1941,27 @@
if data.is_internal_supplier and data.represents_company == data.company:
return True
+
+
+def get_stock_value_difference(item_code, warehouse, posting_date, posting_time, voucher_no=None):
+ table = frappe.qb.DocType("Stock Ledger Entry")
+
+ query = (
+ frappe.qb.from_(table)
+ .select(Sum(table.stock_value_difference).as_("value"))
+ .where(
+ (table.is_cancelled == 0)
+ & (table.item_code == item_code)
+ & (table.warehouse == warehouse)
+ & (
+ (table.posting_date < posting_date)
+ | ((table.posting_date == posting_date) & (table.posting_time <= posting_time))
+ )
+ )
+ )
+
+ if voucher_no:
+ query = query.where(table.voucher_no != voucher_no)
+
+ difference_amount = query.run()
+ return flt(difference_amount[0][0]) if difference_amount else 0
diff --git a/erpnext/templates/pages/projects.html b/erpnext/templates/pages/projects.html
index 9fe4338..3b8698f 100644
--- a/erpnext/templates/pages/projects.html
+++ b/erpnext/templates/pages/projects.html
@@ -14,18 +14,16 @@
{% block style %}
<style>
- {
- % include "templates/includes/projects.css"%
- }
+ {% include "templates/includes/projects.css" %}
</style>
{% endblock %}
{% block page_content %}
<div class="web-list-item transaction-list-item">
<div class="row align-items-center">
- <div class="col-sm-4 "><b>Status: {{ doc.status }}</b></div>
- <div class="col-sm-4 "><b>Progress: {{ doc.percent_complete }}%</b></div>
- <div class="col-sm-4 "><b>Hours Spent: {{ doc.actual_time | round }}</b></div>
+ <div class="col-sm-4 "><b>{{ _("Status") }}: {{ _(doc.status) }}</b></div>
+ <div class="col-sm-4 "><b>{{ _("Progress") }}: {{ doc.get_formatted("percent_complete") }}</b></div>
+ <div class="col-sm-4 "><b>{{ _("Hours Spent") }}: {{ doc.get_formatted("actual_time") }}</b></div>
</div>
</div>
@@ -34,7 +32,7 @@
<hr>
<div class="row align-items-center">
- <div class="col-sm-6 my-account-header"> <h4>Tasks</h4></div>
+ <div class="col-sm-6 my-account-header"> <h4>{{ _("Tasks") }}</h4></div>
<div class="col-sm-6 text-right">
<a class="btn btn-secondary btn-light btn-sm" href='/tasks/new?project={{ doc.project_name }}'>{{ _("New task") }}</a>
</div>
@@ -44,39 +42,39 @@
<div class="result">
<div class="web-list-item transaction-list-item">
<div class="row align-items-center">
- <div class="col-sm-4"><b>Tasks</b></div>
- <div class="col-sm-2"><b>Status</b></div>
- <div class="col-sm-2"><b>End Date</b></div>
- <div class="col-sm-2"><b>Assignment</b></div>
- <div class="col-sm-2"><b>Modified On</b></div>
+ <div class="col-sm-4"><b>{{ _("Tasks") }}</b></div>
+ <div class="col-sm-2"><b>{{ _("Status") }}</b></div>
+ <div class="col-sm-2"><b>{{ _("End Date") }}</b></div>
+ <div class="col-sm-2"><b>{{ _("Assignment") }}</b></div>
+ <div class="col-sm-2"><b>{{ _("Modified On") }}</b></div>
</div>
</div>
{% include "erpnext/templates/includes/projects/project_tasks.html" %}
</div>
</div>
{% else %}
- {{ empty_state('Task')}}
+ {{ empty_state(_("Task")) }}
{% endif %}
- <h4 class="my-account-header">Timesheets</h4>
+ <h4 class="my-account-header">{{ _("Timesheets") }}</h4>
{% if doc.timesheets %}
<div class="website-list">
<div class="result">
<div class="web-list-item transaction-list-item">
<div class="row align-items-center">
- <div class="col-xs-2"><b>Timesheet</b></div>
- <div class="col-xs-2"><b>Status</b></div>
- <div class="col-xs-2"><b>From</b></div>
- <div class="col-xs-2"><b>To</b></div>
- <div class="col-xs-2"><b>Modified By</b></div>
- <div class="col-xs-2"><b>Modified On</b></div>
+ <div class="col-xs-2"><b>{{ _("Timesheet") }}</b></div>
+ <div class="col-xs-2"><b>{{ _("Status") }}</b></div>
+ <div class="col-xs-2"><b>{{ _("From") }}</b></div>
+ <div class="col-xs-2"><b>{{ _("To") }}</b></div>
+ <div class="col-xs-2"><b>{{ _("Modified By") }}</b></div>
+ <div class="col-xs-2"><b>{{ _("Modified On") }}</b></div>
</div>
</div>
{% include "erpnext/templates/includes/projects/project_timesheets.html" %}
</div>
</div>
{% else %}
- {{ empty_state('Timesheet')}}
+ {{ empty_state(_("Timesheet")) }}
{% endif %}
{% if doc.attachments %}
@@ -113,7 +111,7 @@
{% macro progress_bar(percent_complete) %}
{% if percent_complete %}
- <span class="small py-2">Project Progress:</span>
+ <span class="small py-2">{{ _("Project Progress:") }}</span>
<div class="progress progress-hg" style="height: 15px;">
<div
class="progress-bar progress-bar-{{ 'warning' if percent_complete|round < 100 else 'success' }} active"\
@@ -133,7 +131,7 @@
<div>
<img src="/assets/frappe/images/ui-states/list-empty-state.svg" alt="Generic Empty State" class="null-state">
</div>
- <p>You haven't created a {{ section_name }} yet</p>
+ <p>{{ _("You haven't created a {0} yet").format(section_name) }}</p>
</div>
</div>
</div>
diff --git a/erpnext/translations/af.csv b/erpnext/translations/af.csv
index d4b823d..ee77d98 100644
--- a/erpnext/translations/af.csv
+++ b/erpnext/translations/af.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",In die geval van 'n multi-vlak program sal kliënte outomaties toegewys word aan die betrokke vlak volgens hul besteding,
Inactive,onaktiewe,
Incentives,aansporings,
-Include Default Book Entries,Sluit standaardboekinskrywings in,
+Include Default FB Entries,Sluit standaardboekinskrywings in,
Include Exploded Items,Sluit ontplofte items in,
Include POS Transactions,Sluit POS-transaksies in,
Include UOM,Sluit UOM in,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),Garantie Periode (in dae),
Auto re-order,Outo herbestel,
Reorder level based on Warehouse,Herbestel vlak gebaseer op Warehouse,
-Will also apply for variants unless overrridden,Sal ook aansoek doen vir variante tensy dit oortree word,
+Will also apply for variants unless overridden,Sal ook aansoek doen vir variante tensy dit oortree word,
Units of Measure,Eenhede van maatreël,
Will also apply for variants,Sal ook aansoek doen vir variante,
Serial Nos and Batches,Serial Nos and Batches,
diff --git a/erpnext/translations/am.csv b/erpnext/translations/am.csv
index 764868d..a5f09a7 100644
--- a/erpnext/translations/am.csv
+++ b/erpnext/translations/am.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","በባለብዙ ደረጃ መርሃግብር ሁኔታ, ደንበኞች በተጠቀሱት ወጪ መሰረት ለተሰጣቸው ደረጃ ደረጃ በራስ መተላለፍ ይኖራቸዋል",
Inactive,ገባሪ አይደለም,
Incentives,ማበረታቻዎች,
-Include Default Book Entries,ነባሪ የመጽሐፍ ግቤቶችን አካትት።,
+Include Default FB Entries,ነባሪ የመጽሐፍ ግቤቶችን አካትት።,
Include Exploded Items,የተበተኑ ንጥሎችን አካት,
Include POS Transactions,የ POS ሽግግሮችን አክል,
Include UOM,UOM አካት,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),(ቀናት ውስጥ) የዋስትና ክፍለ ጊዜ,
Auto re-order,ራስ-ዳግም-ትዕዛዝ,
Reorder level based on Warehouse,መጋዘን ላይ የተመሠረተ አስይዝ ደረጃ,
-Will also apply for variants unless overrridden,overrridden በስተቀር ደግሞ ተለዋጮች ማመልከት ይሆን,
+Will also apply for variants unless overridden,overridden በስተቀር ደግሞ ተለዋጮች ማመልከት ይሆን,
Units of Measure,ይለኩ አሃዶች,
Will also apply for variants,በተጨማሪም ተለዋጮች ማመልከት ይሆን,
Serial Nos and Batches,ተከታታይ ቁጥሮች እና ቡድኖች,
diff --git a/erpnext/translations/ar.csv b/erpnext/translations/ar.csv
index 4e03a18..195b9c7 100644
--- a/erpnext/translations/ar.csv
+++ b/erpnext/translations/ar.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",في حالة البرنامج متعدد المستويات ، سيتم تعيين العملاء تلقائيًا إلى الطبقة المعنية وفقًا للإنفاق,
Inactive,غير نشط,
Incentives,الحوافز,
-Include Default Book Entries,تضمين إدخالات دفتر افتراضي,
+Include Default FB Entries,تضمين إدخالات دفتر افتراضي,
Include Exploded Items,تشمل البنود المستبعدة,
Include POS Transactions,تشمل معاملات نقطه البيع,
Include UOM,تضمين UOM,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),فترة الضمان (بالأيام),
Auto re-order,إعادة ترتيب تلقائي,
Reorder level based on Warehouse,مستوى إعادة الطلب بناء على مستودع,
-Will also apply for variants unless overrridden,سوف تطبق أيضا على المتغيرات الا اذا تم التغير فوقها,
+Will also apply for variants unless overridden,سوف تطبق أيضا على المتغيرات الا اذا تم التغير فوقها,
Units of Measure,وحدات القياس,
Will also apply for variants,سوف تطبق أيضا على المتغيرات,
Serial Nos and Batches,الرقم التسلسلي ودفعات,
diff --git a/erpnext/translations/bg.csv b/erpnext/translations/bg.csv
index 8dff755..c2bacf4 100644
--- a/erpnext/translations/bg.csv
+++ b/erpnext/translations/bg.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","В случай на многостепенна програма, клиентите ще бъдат автоматично зададени на съответния подреждан по тяхна сметка",
Inactive,неактивен,
Incentives,Стимули,
-Include Default Book Entries,Включете записи по подразбиране на книги,
+Include Default FB Entries,Включете записи по подразбиране на книги,
Include Exploded Items,Включете експлодираните елементи,
Include POS Transactions,Включете POS транзакции,
Include UOM,Включете UOM,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),Гаранционен срок (в дни),
Auto re-order,Автоматична повторна поръчка,
Reorder level based on Warehouse,Пренареждане равнище въз основа на Warehouse,
-Will also apply for variants unless overrridden,"Ще се прилага и за варианти, освен ако overrridden",
+Will also apply for variants unless overridden,"Ще се прилага и за варианти, освен ако overridden",
Units of Measure,Мерни единици за измерване,
Will also apply for variants,Ще се прилага и за варианти,
Serial Nos and Batches,Серийни номера и партиди,
diff --git a/erpnext/translations/bn.csv b/erpnext/translations/bn.csv
index 8a698df..d7366e1 100644
--- a/erpnext/translations/bn.csv
+++ b/erpnext/translations/bn.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","মাল্টি-টিয়ার প্রোগ্রামের ক্ষেত্রে, গ্রাহকরা তাদের ব্যয় অনুযায়ী সংশ্লিষ্ট টায়ারে স্বয়ংক্রিয়ভাবে নিয়োগ পাবেন",
Inactive,নিষ্ক্রিয়,
Incentives,ইনসেনটিভ,
-Include Default Book Entries,ডিফল্ট বুক এন্ট্রি অন্তর্ভুক্ত করুন,
+Include Default FB Entries,ডিফল্ট বুক এন্ট্রি অন্তর্ভুক্ত করুন,
Include Exploded Items,বিস্ফোরিত আইটেম অন্তর্ভুক্ত করুন,
Include POS Transactions,পিওএস লেনদেন অন্তর্ভুক্ত করুন,
Include UOM,UOM অন্তর্ভুক্ত করুন,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),(দিন) ওয়্যারেন্টি সময়কাল,
Auto re-order,অটো পুনরায় আদেশ,
Reorder level based on Warehouse,গুদাম উপর ভিত্তি রেকর্ডার স্তর,
-Will also apply for variants unless overrridden,Overrridden তবে এছাড়াও ভিন্নতা জন্য আবেদন করতে হবে,
+Will also apply for variants unless overridden,Overrridden তবে এছাড়াও ভিন্নতা জন্য আবেদন করতে হবে,
Units of Measure,পরিমাপ ইউনিট,
Will also apply for variants,এছাড়াও ভিন্নতা জন্য আবেদন করতে হবে,
Serial Nos and Batches,সিরিয়াল আমরা এবং ব্যাচ,
diff --git a/erpnext/translations/bs.csv b/erpnext/translations/bs.csv
index 7ba4a88..df4083e 100644
--- a/erpnext/translations/bs.csv
+++ b/erpnext/translations/bs.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","U slučaju višeslojnog programa, Korisnici će automatski biti dodeljeni za dotičnu grupu po njihovom trošenju",
Inactive,Neaktivan,
Incentives,Poticaji,
-Include Default Book Entries,Uključite zadane unose knjiga,
+Include Default FB Entries,Uključite zadane unose knjiga,
Include Exploded Items,Uključite eksplodirane predmete,
Include POS Transactions,Uključite POS transakcije,
Include UOM,Uključite UOM,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),Jamstveni period (u danima),
Auto re-order,Autorefiniš reda,
Reorder level based on Warehouse,Nivo Ponovno red zasnovan na Skladište,
-Will also apply for variants unless overrridden,Primjenjivat će se i za varijante osim overrridden,
+Will also apply for variants unless overridden,Primjenjivat će se i za varijante osim overridden,
Units of Measure,Jedinice mjere,
Will also apply for variants,Primjenjivat će se i za varijante,
Serial Nos and Batches,Serijski brojevi i Paketi,
diff --git a/erpnext/translations/ca.csv b/erpnext/translations/ca.csv
index cce1f3a..b3cf2c5 100644
--- a/erpnext/translations/ca.csv
+++ b/erpnext/translations/ca.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","En el cas del programa de diversos nivells, els clients seran assignats automàticament al nivell corresponent segons el seu gastat",
Inactive,Inactiu,
Incentives,Incentius,
-Include Default Book Entries,Inclou les entrades de llibres predeterminats,
+Include Default FB Entries,Inclou les entrades de llibres predeterminats,
Include Exploded Items,Inclou articles explotats,
Include POS Transactions,Inclou transaccions de POS,
Include UOM,Inclou UOM,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),Període de garantia (en dies),
Auto re-order,Acte reordenar,
Reorder level based on Warehouse,Nivell de comanda basat en Magatzem,
-Will also apply for variants unless overrridden,També s'aplicarà per a les variants menys overrridden,
+Will also apply for variants unless overridden,També s'aplicarà per a les variants menys overridden,
Units of Measure,Unitats de mesura,
Will also apply for variants,També s'aplicarà per a les variants,
Serial Nos and Batches,Nº de sèrie i lots,
diff --git a/erpnext/translations/cs.csv b/erpnext/translations/cs.csv
index 1072ed3..b6deaa4 100644
--- a/erpnext/translations/cs.csv
+++ b/erpnext/translations/cs.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",V případě víceúrovňového programu budou zákazníci automaticky přiděleni danému vrstvě podle svých vynaložených nákladů,
Inactive,Neaktivní,
Incentives,Pobídky,
-Include Default Book Entries,Zahrnout výchozí položky knihy,
+Include Default FB Entries,Zahrnout výchozí položky knihy,
Include Exploded Items,Zahrnout výbušné položky,
Include POS Transactions,Zahrnout POS transakce,
Include UOM,Zahrnout UOM,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),Záruční doba (ve dnech),
Auto re-order,Automatické znovuobjednání,
Reorder level based on Warehouse,Úroveň Změna pořadí na základě Warehouse,
-Will also apply for variants unless overrridden,"Bude platit i pro varianty, pokud nebude přepsáno",
+Will also apply for variants unless overridden,"Bude platit i pro varianty, pokud nebude přepsáno",
Units of Measure,Jednotky měření,
Will also apply for variants,Bude platit i pro varianty,
Serial Nos and Batches,Sériové čísla a dávky,
diff --git a/erpnext/translations/cz.csv b/erpnext/translations/cz.csv
index 270a710..96de062 100644
--- a/erpnext/translations/cz.csv
+++ b/erpnext/translations/cz.csv
@@ -1991,7 +1991,7 @@
Actual End Date,Skutečné datum ukončen$1,
Applicable To (Role),Vztahující se na (Role)
Purpose,Účel,
-Will also apply for variants unless overrridden,"Bude platit i pro varianty, pokud nebude přepsáno"
+Will also apply for variants unless overridden,"Bude platit i pro varianty, pokud nebude přepsáno"
Advances,Zálohy,
Approving User cannot be same as user the rule is Applicable To,Schválení Uživatel nemůže být stejná jako uživatel pravidlo se vztahuje na,
No of Requested SMS,Počet žádaným SMS,
diff --git a/erpnext/translations/da.csv b/erpnext/translations/da.csv
index 138da5d..4bcc307 100644
--- a/erpnext/translations/da.csv
+++ b/erpnext/translations/da.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","I tilfælde af multi-tier program, vil kunder automatisk blive tildelt den pågældende tier som per deres brugt",
Inactive,inaktive,
Incentives,Incitamenter,
-Include Default Book Entries,Inkluder standardbogsindlæg,
+Include Default FB Entries,Inkluder standardbogsindlæg,
Include Exploded Items,Inkluder eksploderede elementer,
Include POS Transactions,Inkluder POS-transaktioner,
Include UOM,Inkluder UOM,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),Garantiperiode (i dage),
Auto re-order,Auto genbestil,
Reorder level based on Warehouse,Genbestil niveau baseret på Warehouse,
-Will also apply for variants unless overrridden,"Vil også gælde for varianter, medmindre overrridden",
+Will also apply for variants unless overridden,"Vil også gælde for varianter, medmindre overridden",
Units of Measure,Måleenheder,
Will also apply for variants,Vil også gælde for varianter,
Serial Nos and Batches,Serienummer og partier,
diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv
index 79b9574..73755be 100644
--- a/erpnext/translations/de.csv
+++ b/erpnext/translations/de.csv
@@ -533,6 +533,7 @@
Company is manadatory for company account,Bitte gib ein Unternehmen für dieses Unternehmenskonto an.,
Company name not same,Firma nicht gleich,
Company {0} does not exist,Unternehmen {0} existiert nicht,
+Competitor,Konkurrent,
Compensatory leave request days not in valid holidays,"Tage des Ausgleichsurlaubs, die nicht in den gültigen Feiertagen sind",
Complaint,Beschwerde,
Completion Date,Fertigstellungstermin,
@@ -1160,7 +1161,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",Im Falle eines mehrstufigen Programms werden Kunden automatisch der betroffenen Ebene entsprechend ihrer Ausgaben zugewiesen,
Inactive,Inaktiv,
Incentives,Anreize,
-Include Default Book Entries,Standardbucheinträge einschließen,
+Include Default FB Entries,Standardbucheinträge einschließen,
Include Exploded Items,Unterartikel einbeziehen,
Include POS Transactions,POS-Transaktionen einschließen,
Include UOM,Fügen Sie UOM hinzu,
@@ -3340,7 +3341,7 @@
Cannot Optimize Route as Driver Address is Missing.,"Route kann nicht optimiert werden, da die Fahreradresse fehlt.",
Cannot complete task {0} as its dependant task {1} are not ccompleted / cancelled.,"Aufgabe {0} kann nicht abgeschlossen werden, da die abhängige Aufgabe {1} nicht abgeschlossen / abgebrochen wurde.",
Cannot find a matching Item. Please select some other value for {0}.,Ein passender Artikel kann nicht gefunden werden. Bitte einen anderen Wert für {0} auswählen.,
-"Cannot overbill for Item {0} in row {1} more than {2}. To allow over-billing, please set allowance in Accounts Settings","Artikel {0} in Zeile {1} kann nicht mehr als {2} in Rechnung gestellt werden. Um eine Überberechnung zuzulassen, legen Sie die Überberechnung in den Kontoeinstellungen fest",
+"Cannot overbill for Item {0} in row {1} more than {2}. To allow over-billing, please set allowance in Accounts Settings","Für Artikel {0} in Zeile {1} kann nicht mehr als {2} zusätzlich in Rechnung gestellt werden. Um diese Überfakturierung zuzulassen, passen Sie bitte die Grenzwerte in den Buchhaltungseinstellungen an.",
"Capacity Planning Error, planned start time can not be same as end time","Kapazitätsplanungsfehler, die geplante Startzeit darf nicht mit der Endzeit übereinstimmen",
Categories,Kategorien,
Changes in {0},Änderungen in {0},
@@ -3746,7 +3747,7 @@
This page keeps track of your items in which buyers have showed some interest.,"Diese Seite verfolgt Ihre Artikel, an denen Käufer Interesse gezeigt haben.",
Thursday,Donnerstag,
Title,Bezeichnung,
-"To allow over billing, update ""Over Billing Allowance"" in Accounts Settings or the Item.","Aktualisieren Sie "Over Billing Allowance" in den Kontoeinstellungen oder im Artikel, um eine Überberechnung zuzulassen.",
+"To allow over billing, update ""Over Billing Allowance"" in Accounts Settings or the Item.","Aktualisieren Sie "Over Billing Allowance" in den Buchhaltungseinstellungen oder im Artikel, um eine Überberechnung zuzulassen.",
"To allow over receipt / delivery, update ""Over Receipt/Delivery Allowance"" in Stock Settings or the Item.","Um eine Überbestätigung / Überlieferung zu ermöglichen, aktualisieren Sie "Überbestätigung / Überlieferung" in den Lagereinstellungen oder im Artikel.",
Total,Summe,
Total Payment Request amount cannot be greater than {0} amount,Der Gesamtbetrag der Zahlungsanforderung darf nicht größer als {0} sein,
@@ -4113,8 +4114,8 @@
Accounting Period,Abrechnungszeitraum,
Period Name,Zeitraumname,
Closed Documents,Geschlossene Dokumente,
-Accounts Settings,Konteneinstellungen,
-Settings for Accounts,Konteneinstellungen,
+Accounts Settings,Buchhaltungseinstellungen,
+Settings for Accounts,Einstellungen für die Buchhaltung,
Make Accounting Entry For Every Stock Movement,Eine Buchung für jede Lagerbewegung erstellen,
Users with this role are allowed to set frozen accounts and create / modify accounting entries against frozen accounts,Benutzer mit dieser Rolle sind berechtigt Konten zu sperren und Buchungen zu gesperrten Konten zu erstellen/verändern,
Determine Address Tax Category From,Adresssteuerkategorie bestimmen von,
@@ -5073,7 +5074,7 @@
PUR-ORD-.YYYY.-,PUR-ORD-.YYYY.-,
Get Items from Open Material Requests,Hole Artikel von offenen Material Anfragen,
Fetch items based on Default Supplier.,Abrufen von Elementen basierend auf dem Standardlieferanten.,
-Required By,Benötigt von,
+Required By,Benötigt bis,
Order Confirmation No,Auftragsbestätigung Nr,
Order Confirmation Date,Auftragsbestätigungsdatum,
Customer Mobile No,Mobilnummer des Kunden,
@@ -7074,7 +7075,7 @@
Warranty Period (in days),Garantiefrist (in Tagen),
Auto re-order,Automatische Nachbestellung,
Reorder level based on Warehouse,Meldebestand auf Basis des Lagers,
-Will also apply for variants unless overrridden,"Gilt auch für Varianten, sofern nicht außer Kraft gesetzt",
+Will also apply for variants unless overridden,"Gilt auch für Varianten, sofern nicht außer Kraft gesetzt",
Units of Measure,Maßeinheiten,
Will also apply for variants,Gilt auch für Varianten,
Serial Nos and Batches,Seriennummern und Chargen,
@@ -7617,7 +7618,7 @@
Journal Entry Type,Buchungssatz-Typ,
Journal Entry Template Account,Buchungssatzvorlagenkonto,
Process Deferred Accounting,Aufgeschobene Buchhaltung verarbeiten,
-Manual entry cannot be created! Disable automatic entry for deferred accounting in accounts settings and try again,Manuelle Eingabe kann nicht erstellt werden! Deaktivieren Sie die automatische Eingabe für die verzögerte Buchhaltung in den Konteneinstellungen und versuchen Sie es erneut,
+Manual entry cannot be created! Disable automatic entry for deferred accounting in accounts settings and try again,Manuelle Eingabe kann nicht erstellt werden! Deaktivieren Sie die automatische Eingabe für die verzögerte Buchhaltung in den Buchhaltungseinstellungen und versuchen Sie es erneut,
End date cannot be before start date,Das Enddatum darf nicht vor dem Startdatum liegen,
Total Counts Targeted,Gesamtzahl der anvisierten Zählungen,
Total Counts Completed,Gesamtzahl der abgeschlossenen Zählungen,
@@ -8826,5 +8827,32 @@
Is Mandatory,Ist obligatorisch,
WhatsApp,WhatsApp,
Make a call,Einen Anruf tätigen,
+Enable Automatic Party Matching,Automatisches Zuordnen von Parteien aktivieren,
+Auto match and set the Party in Bank Transactions,"Partei automatisch anhand der Kontonummer bzw. IBAN zuordnen",
+Enable Fuzzy Matching,Fuzzy Matching aktivieren,
+Approximately match the description/party name against parties,"Partei automatisch anhand grober Übereinstimmung des Namens zuordnen"
+Accounts Closing,Kontenabschluss,
+Period Closing Settings,Periodenabschlusseinstellungen,
+Ignore Account Closing Balance,Saldo des Kontos zum Periodenabschluss ignorieren,
+Financial reports will be generated using GL Entry doctypes (should be enabled if Period Closing Voucher is not posted for all years sequentially or missing),"Finanzberichte werden anhand des Hauptbuchs erstellt (sollte aktiviert sein, wenn Periodenabschlüsse fehlen oder nicht sequentiell für alle Jahre gebucht werden)",
+Asset Settings,Vermögenswerteinstellungen,
+POS Setting,POS-Einstellungen,
+Create Ledger Entries for Change Amount,Buchungssätze für Wechselgeld erstellen,
+"If enabled, ledger entries will be posted for change amount in POS transactions","Wenn aktiviert, werden Buchungssätze für Wechselgeld in POS-Transaktionen erstellt",
+Credit Limit Settings,Kreditlimit-Einstellungen,
+Role Allowed to Over Bill,
+Users with this role are allowed to over bill above the allowance percentage,"Roll, die mehr als den erlaubten Prozentsatz zusätzlich abrechnen darf",
+Role allowed to bypass Credit Limit,"Rolle, die das Kreditlimit umgehen darf",
+Invoice Cancellation,Rechnungsstornierung,
+Delete Accounting and Stock Ledger Entries on deletion of Transaction,Beim Löschen einer Transaktion auch die entsprechenden Buchungs- und Lagerbuchungssätze löschen,
+Enable Common Party Accounting,Verknüpfung von Kunden und Liefeanten erlauben,
+Allow multi-currency invoices against single party account,Rechnungsbeträge in Fremdwährungen dürfen umgerechnet und in der Hauptwährung gebucht werden,
+Enabling this will allow creation of multi-currency invoices against single party account in company currency,Bei Aktivierung können Rechnungen in Fremdwährungen gegen ein Konto in der Hauptwährung gebucht werden,
+Payment Terms from orders will be fetched into the invoices as is,Zahlungsbedingungen aus Aufträgen werden eins zu eins in Rechnungen übernommen,
+Automatically Fetch Payment Terms from Order,Zahlungsbedingungen aus Auftrag in die Rechnung übernehmen,
+Enable Custom Cash Flow Format,Individuelles Cashflow-Format aktivieren,
+Tax Settings,Umsatzsteuer-Einstellungen,
+Book Tax Loss on Early Payment Discount,Umsatzsteueranteil bei Skonto berücksichtigen,
+Split Early Payment Discount Loss into Income and Tax Loss,"Skontobetrag in Aufwand und Umsatzsteuerkorrektur aufteilen",
Approve,Genehmigen,
Reject,Ablehnen,
diff --git a/erpnext/translations/el.csv b/erpnext/translations/el.csv
index 7a83cc5..e67eaff 100644
--- a/erpnext/translations/el.csv
+++ b/erpnext/translations/el.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Στην περίπτωση προγράμματος πολλαπλών βαθμίδων, οι Πελάτες θα αντιστοιχούν αυτόματα στη σχετική βαθμίδα σύμφωνα με το ποσό που δαπανώνται",
Inactive,Αδρανής,
Incentives,Κίνητρα,
-Include Default Book Entries,Συμπεριλάβετε τις προεπιλεγμένες καταχωρίσεις βιβλίων,
+Include Default FB Entries,Συμπεριλάβετε τις προεπιλεγμένες καταχωρίσεις βιβλίων,
Include Exploded Items,Συμπεριλάβετε εκραγμένα στοιχεία,
Include POS Transactions,Συμπεριλάβετε τις συναλλαγές POS,
Include UOM,Συμπεριλάβετε UOM,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),Περίοδος εγγύησης (σε ημέρες),
Auto re-order,Αυτόματη εκ νέου προκειμένου,
Reorder level based on Warehouse,Αναδιάταξη επίπεδο με βάση Αποθήκης,
-Will also apply for variants unless overrridden,Θα ισχύουν επίσης για τις παραλλαγές εκτός αν υπάρχει υπέρβαση,
+Will also apply for variants unless overridden,Θα ισχύουν επίσης για τις παραλλαγές εκτός αν υπάρχει υπέρβαση,
Units of Measure,Μονάδες μέτρησης,
Will also apply for variants,Θα ισχύουν επίσης για τις παραλλαγές,
Serial Nos and Batches,Σειριακοί αριθμοί και παρτίδες,
diff --git a/erpnext/translations/es.csv b/erpnext/translations/es.csv
index fadf7a7..0c90694 100644
--- a/erpnext/translations/es.csv
+++ b/erpnext/translations/es.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","En el caso del programa de varios niveles, los Clientes se asignarán automáticamente al nivel correspondiente según su gasto",
Inactive,Inactivo,
Incentives,Incentivos,
-Include Default Book Entries,Incluir entradas de libro predeterminadas,
+Include Default FB Entries,Incluir entradas de libro predeterminadas,
Include Exploded Items,Incluir Elementos Estallados,
Include POS Transactions,Incluir transacciones POS,
Include UOM,Incluir UOM,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),Período de garantía (en días),
Auto re-order,Ordenar Automáticamente,
Reorder level based on Warehouse,Nivel de reabastecimiento basado en almacén,
-Will also apply for variants unless overrridden,También se aplicará para las variantes menos que se sobre escriba,
+Will also apply for variants unless overridden,También se aplicará para las variantes menos que se sobre escriba,
Units of Measure,Unidades de medida,
Will also apply for variants,También se aplicará para las variantes,
Serial Nos and Batches,Números de serie y lotes,
diff --git a/erpnext/translations/et.csv b/erpnext/translations/et.csv
index 4e26a82..69a89d9 100644
--- a/erpnext/translations/et.csv
+++ b/erpnext/translations/et.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",Mitmekordsete programmide korral määratakse Kliendid automaatselt asjaomasele tasemele vastavalt nende kasutatud kuludele,
Inactive,Mitteaktiivne,
Incentives,Soodustused,
-Include Default Book Entries,Lisage vaikeraamatu kanded,
+Include Default FB Entries,Lisage vaikeraamatu kanded,
Include Exploded Items,Kaasa lõhutud esemed,
Include POS Transactions,Kaasa POS-tehingud,
Include UOM,Lisa UOM,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),Garantii Periood (päeva),
Auto re-order,Auto ümber korraldada,
Reorder level based on Warehouse,Reorder tasandil põhineb Warehouse,
-Will also apply for variants unless overrridden,"Kehtib ka variante, kui overrridden",
+Will also apply for variants unless overridden,"Kehtib ka variante, kui overridden",
Units of Measure,Mõõtühikud,
Will also apply for variants,Kehtib ka variandid,
Serial Nos and Batches,Serial Nos ning partiid,
diff --git a/erpnext/translations/fa.csv b/erpnext/translations/fa.csv
index 530965d..cddabfb 100644
--- a/erpnext/translations/fa.csv
+++ b/erpnext/translations/fa.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",در مورد برنامه چند لایه، مشتریان به صورت خودکار به سطر مربوطه اختصاص داده می شوند، همانطور که در هزینه های خود هستند,
Inactive,غیر فعال,
Incentives,انگیزه,
-Include Default Book Entries,شامل ورودی های پیش فرض کتاب,
+Include Default FB Entries,شامل ورودی های پیش فرض کتاب,
Include Exploded Items,شامل موارد انفجار,
Include POS Transactions,شامل معاملات POS,
Include UOM,شامل UOM,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),دوره گارانتی (در روز),
Auto re-order,خودکار دوباره سفارش,
Reorder level based on Warehouse,سطح تغییر مجدد ترتیب بر اساس انبار,
-Will also apply for variants unless overrridden,همچنین برای انواع اعمال می شود مگر اینکه overrridden,
+Will also apply for variants unless overridden,همچنین برای انواع اعمال می شود مگر اینکه overridden,
Units of Measure,واحدهای اندازه گیری,
Will also apply for variants,همچنین برای انواع اعمال می شود,
Serial Nos and Batches,سریال شماره و دسته,
diff --git a/erpnext/translations/fi.csv b/erpnext/translations/fi.csv
index 6e9380c..eae6053 100644
--- a/erpnext/translations/fi.csv
+++ b/erpnext/translations/fi.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",Monitasoisen ohjelman tapauksessa asiakkaat määräytyvät automaattisesti kyseiselle tasolle niiden kulutuksen mukaan,
Inactive,Epäaktiivinen,
Incentives,kannustimet/bonukset,
-Include Default Book Entries,Sisällytä oletustiedot,
+Include Default FB Entries,Sisällytä oletustiedot,
Include Exploded Items,Sisällytä räjähtämättömiä kohteita,
Include POS Transactions,Sisällytä POS-tapahtumia,
Include UOM,Sisällytä UOM,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),Takuuaika (päivinä),
Auto re-order,Auto re-order,
Reorder level based on Warehouse,Varastoon perustuva täydennystilaustaso,
-Will also apply for variants unless overrridden,"Sovelletaan myös tuotemalleissa, ellei kumota",
+Will also apply for variants unless overridden,"Sovelletaan myös tuotemalleissa, ellei kumota",
Units of Measure,Mittayksiköt,
Will also apply for variants,Sovelletaan myös tuotemalleissa,
Serial Nos and Batches,Sarjanumerot ja Erät,
diff --git a/erpnext/translations/fr.csv b/erpnext/translations/fr.csv
index d3875c1..5c34759 100644
--- a/erpnext/translations/fr.csv
+++ b/erpnext/translations/fr.csv
@@ -1058,7 +1058,7 @@
In Value,En valeur,
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Dans le cas d'un programme à plusieurs échelons, les clients seront automatiquement affectés au niveau approprié en fonction de leurs dépenses",
Incentives,Incitations,
-Include Default Book Entries,Inclure les entrées de livre par défaut,
+Include Default FB Entries,Inclure les entrées de livre par défaut,
Include Exploded Items,Inclure les articles éclatés,
Include POS Transactions,Inclure les transactions du point de vente,
Include UOM,Inclure UdM,
@@ -6650,7 +6650,7 @@
Warranty Period (in days),Période de Garantie (en jours),
Auto re-order,Re-commande auto,
Reorder level based on Warehouse,Niveau de réapprovisionnement basé sur l’Entrepôt,
-Will also apply for variants unless overrridden,S'appliquera également pour des variantes sauf si remplacé,
+Will also apply for variants unless overridden,S'appliquera également pour des variantes sauf si remplacé,
Units of Measure,Unités de Mesure,
Will also apply for variants,S'appliquera également pour les variantes,
Serial Nos and Batches,N° de Série et Lots,
diff --git a/erpnext/translations/gu.csv b/erpnext/translations/gu.csv
index e2de8ce..604ec41 100644
--- a/erpnext/translations/gu.csv
+++ b/erpnext/translations/gu.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","મલ્ટિ-ટાયર પ્રોગ્રામના કિસ્સામાં, ગ્રાહક તેમના ખર્ચ મુજબ સંબંધિત ટાયરમાં ઓટો હશે",
Inactive,નિષ્ક્રિય,
Incentives,ઇનસેન્ટીવ્સ,
-Include Default Book Entries,ડિફaultલ્ટ બુક એન્ટ્રીઓ શામેલ કરો,
+Include Default FB Entries,ડિફaultલ્ટ બુક એન્ટ્રીઓ શામેલ કરો,
Include Exploded Items,વિસ્ફોટ થયેલ આઇટમ્સ શામેલ કરો,
Include POS Transactions,POS વ્યવહારો શામેલ કરો,
Include UOM,યુએમએમ શામેલ કરો,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),(દિવસોમાં) વોરંટી સમયગાળા,
Auto re-order,ઓટો ફરી ઓર્ડર,
Reorder level based on Warehouse,વેરહાઉસ પર આધારિત પુનઃક્રમાંકિત કરો સ્તર,
-Will also apply for variants unless overrridden,Overrridden સિવાય પણ ચલો માટે લાગુ પડશે,
+Will also apply for variants unless overridden,Overrridden સિવાય પણ ચલો માટે લાગુ પડશે,
Units of Measure,માપવા એકમો,
Will also apply for variants,પણ ચલો માટે લાગુ પડશે,
Serial Nos and Batches,સીરીયલ સંખ્યા અને બૅચેસ,
diff --git a/erpnext/translations/he.csv b/erpnext/translations/he.csv
index 6cd900a..5407578 100644
--- a/erpnext/translations/he.csv
+++ b/erpnext/translations/he.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","במקרה של תוכנית רב-שכבתית, הלקוחות יוקצו אוטומטית לשכבה הנוגעת בדבר שהוצאו",
Inactive,לֹא פָּעִיל,
Incentives,תמריצים,
-Include Default Book Entries,כלול רשומות ברירת מחדל לספרים,
+Include Default FB Entries,כלול רשומות ברירת מחדל לספרים,
Include Exploded Items,כלול פריטים מפוצצים,
Include POS Transactions,כלול עסקאות קופה,
Include UOM,כלול UOM,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),תקופת אחריות (בימים),
Auto re-order,רכב מחדש כדי,
Reorder level based on Warehouse,רמת הזמנה חוזרת המבוסס על מחסן,
-Will also apply for variants unless overrridden,תחול גם לגרסות אלא אם overrridden,
+Will also apply for variants unless overridden,תחול גם לגרסות אלא אם overridden,
Units of Measure,יחידות מידה,
Will also apply for variants,תחול גם לגרסות,
Serial Nos and Batches,מספרים וסידורים סדרתיים,
diff --git a/erpnext/translations/hi.csv b/erpnext/translations/hi.csv
index 388b502..00532df 100644
--- a/erpnext/translations/hi.csv
+++ b/erpnext/translations/hi.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","मल्टी-स्तरीय कार्यक्रम के मामले में, ग्राहक अपने खर्च के अनुसार संबंधित स्तर को स्वचालित रूप से सौंपा जाएगा",
Inactive,निष्क्रिय,
Incentives,प्रोत्साहन,
-Include Default Book Entries,डिफ़ॉल्ट बुक प्रविष्टियाँ शामिल करें,
+Include Default FB Entries,डिफ़ॉल्ट बुक प्रविष्टियाँ शामिल करें,
Include Exploded Items,विस्फोट किए गए आइटम शामिल करें,
Include POS Transactions,पीओएस लेनदेन शामिल करें,
Include UOM,यूओएम शामिल करें,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),वारंटी अवधि (दिनों में),
Auto re-order,ऑटो पुनः आदेश,
Reorder level based on Warehouse,गोदाम के आधार पर पुन: व्यवस्थित स्तर,
-Will also apply for variants unless overrridden,Overrridden जब तक भी वेरिएंट के लिए लागू होगी,
+Will also apply for variants unless overridden,Overrridden जब तक भी वेरिएंट के लिए लागू होगी,
Units of Measure,मापन की इकाई,
Will also apply for variants,यह भी वेरिएंट के लिए लागू होगी,
Serial Nos and Batches,सीरियल नंबर और बैचों,
diff --git a/erpnext/translations/hr.csv b/erpnext/translations/hr.csv
index b44babc..3cc9ef3 100644
--- a/erpnext/translations/hr.csv
+++ b/erpnext/translations/hr.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","U slučaju višerazinskog programa, Kupci će biti automatski dodijeljeni odgovarajućem stupcu po njihovu potrošenom",
Inactive,neaktivan,
Incentives,poticaji,
-Include Default Book Entries,Uključite zadane unose u knjige,
+Include Default FB Entries,Uključite zadane unose u knjige,
Include Exploded Items,Uključi eksplodirane predmete,
Include POS Transactions,Uključi POS transakcije,
Include UOM,Uključi UOM,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),Jamstveni period (u danima),
Auto re-order,Automatski reorganiziraj,
Reorder level based on Warehouse,Razina redoslijeda na temelju Skladište,
-Will also apply for variants unless overrridden,Također će zatražiti varijante osim overrridden,
+Will also apply for variants unless overridden,Također će zatražiti varijante osim overridden,
Units of Measure,Mjerne jedinice,
Will also apply for variants,Također će podnijeti zahtjev za varijante,
Serial Nos and Batches,Serijski brojevi i serije,
diff --git a/erpnext/translations/hu.csv b/erpnext/translations/hu.csv
index 4ea5b9a..42175bb 100644
--- a/erpnext/translations/hu.csv
+++ b/erpnext/translations/hu.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Többszintű program esetében az ügyfeleket automatikusan az adott kategóriába sorolják, az általuk elköltöttek szerint",
Inactive,Inaktív,
Incentives,Ösztönzők,
-Include Default Book Entries,Tartalmazza az alapértelmezett könyvbejegyzéseket,
+Include Default FB Entries,Tartalmazza az alapértelmezett könyvbejegyzéseket,
Include Exploded Items,Tartalmazza a robbantott elemeket,
Include POS Transactions,Tartalmazza a POS kassza tranzakciókat,
Include UOM,Ide tartozik az ANYJ,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),Garancia hossza (napokban),
Auto re-order,Auto újra-rendelés,
Reorder level based on Warehouse,Raktárkészleten alapuló újrerendelési szint,
-Will also apply for variants unless overrridden,"Változatokra is alkalmazni fogja, hacsak nem kerül fellülírásra",
+Will also apply for variants unless overridden,"Változatokra is alkalmazni fogja, hacsak nem kerül fellülírásra",
Units of Measure,Mértékegységek,
Will also apply for variants,Változatokra is alkalmazni fogja,
Serial Nos and Batches,Sorszámok és kötegek,
diff --git a/erpnext/translations/id.csv b/erpnext/translations/id.csv
index d84c3fd..d69eef3 100644
--- a/erpnext/translations/id.csv
+++ b/erpnext/translations/id.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Dalam kasus program multi-tier, Pelanggan akan ditugaskan secara otomatis ke tingkat yang bersangkutan sesuai yang mereka habiskan",
Inactive,Tidak aktif,
Incentives,Insentif,
-Include Default Book Entries,Sertakan Entri Buku Default,
+Include Default FB Entries,Sertakan Entri Buku Default,
Include Exploded Items,Sertakan barang yang meledak,
Include POS Transactions,Sertakan Transaksi POS,
Include UOM,Termasuk UOM,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),Masa Garansi (dalam hari),
Auto re-order,Auto re-order,
Reorder level based on Warehouse,Tingkat Re-Order berdasarkan Gudang,
-Will also apply for variants unless overrridden,Juga akan berlaku untuk varian kecuali tertimpa,
+Will also apply for variants unless overridden,Juga akan berlaku untuk varian kecuali tertimpa,
Units of Measure,Satuan ukur,
Will also apply for variants,Juga akan berlaku untuk varian,
Serial Nos and Batches,Nomor Seri dan Partai,
diff --git a/erpnext/translations/is.csv b/erpnext/translations/is.csv
index 7687e4a..1acefbb 100644
--- a/erpnext/translations/is.csv
+++ b/erpnext/translations/is.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Þegar um er að ræða fjölþættaráætlun, verða viðskiptavinir sjálfkrafa tengdir viðkomandi flokka eftir því sem þeir eru í",
Inactive,Óvirkt,
Incentives,Incentives,
-Include Default Book Entries,Hafa sjálfgefnar bókarfærslur með,
+Include Default FB Entries,Hafa sjálfgefnar bókarfærslur með,
Include Exploded Items,Inniheldur sprauta hluti,
Include POS Transactions,Innifalið POS-viðskipti,
Include UOM,Innifalið UOM,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),Ábyrgðartímabilið (í dögum),
Auto re-order,Auto endurraða,
Reorder level based on Warehouse,Uppröðun stigi byggist á Lager,
-Will also apply for variants unless overrridden,Mun einnig gilda um afbrigði nema overrridden,
+Will also apply for variants unless overridden,Mun einnig gilda um afbrigði nema overridden,
Units of Measure,Mælieiningar,
Will also apply for variants,Mun einnig gilda fyrir afbrigði,
Serial Nos and Batches,Raðnúmer og lotur,
diff --git a/erpnext/translations/it.csv b/erpnext/translations/it.csv
index b88cada..e6e6425 100644
--- a/erpnext/translations/it.csv
+++ b/erpnext/translations/it.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Nel caso di un programma multilivello, i clienti verranno assegnati automaticamente al livello interessato come da loro speso",
Inactive,Inattivo,
Incentives,Incentivi,
-Include Default Book Entries,Includi voci di libro predefinite,
+Include Default FB Entries,Includi voci di libro predefinite,
Include Exploded Items,Includi elementi esplosi,
Include POS Transactions,Includi transazioni POS,
Include UOM,Includi UOM,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),Periodo di garanzia (in giorni),
Auto re-order,Auto riordino,
Reorder level based on Warehouse,Livello di riordino sulla base di Magazzino,
-Will also apply for variants unless overrridden,Si applica anche per le varianti meno overrridden,
+Will also apply for variants unless overridden,Si applica anche per le varianti meno overridden,
Units of Measure,Unità di misura,
Will also apply for variants,Si applica anche per le varianti,
Serial Nos and Batches,Numero e lotti seriali,
diff --git a/erpnext/translations/ja.csv b/erpnext/translations/ja.csv
index 11455bd..dd5820a 100644
--- a/erpnext/translations/ja.csv
+++ b/erpnext/translations/ja.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",マルチティアプログラムの場合、顧客は、消費されるごとに自動的に関係する層に割り当てられます,
Inactive,非アクティブ,
Incentives,インセンティブ,
-Include Default Book Entries,デフォルトのブックエントリを含める,
+Include Default FB Entries,デフォルトのブックエントリを含める,
Include Exploded Items,分解された項目を含める,
Include POS Transactions,POSトランザクションを含める,
Include UOM,UOMを含める,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),保証期間(日数),
Auto re-order,自動再注文,
Reorder level based on Warehouse,倉庫ごとの再注文レベル,
-Will also apply for variants unless overrridden,上書きされない限り、バリエーションについても適用されます,
+Will also apply for variants unless overridden,上書きされない限り、バリエーションについても適用されます,
Units of Measure,測定の単位,
Will also apply for variants,バリエーションについても適用されます,
Serial Nos and Batches,シリアル番号とバッチ,
diff --git a/erpnext/translations/km.csv b/erpnext/translations/km.csv
index 46dcaba..2740d7f 100644
--- a/erpnext/translations/km.csv
+++ b/erpnext/translations/km.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",ក្នុងករណីមានកម្មវិធីពហុលំដាប់អតិថិជននឹងត្រូវបានចាត់តាំងដោយខ្លួនឯងទៅថ្នាក់ដែលពាក់ព័ន្ធដោយចំណាយរបស់ពួកគេ,
Inactive,អសកម្ម,
Incentives,ការលើកទឹកចិត្ត,
-Include Default Book Entries,រួមបញ្ចូលធាតុសៀវភៅលំនាំដើម។,
+Include Default FB Entries,រួមបញ្ចូលធាតុសៀវភៅលំនាំដើម។,
Include Exploded Items,រួមបញ្ចូលធាតុផ្ទុះ,
Include POS Transactions,បញ្ចូលប្រតិបត្តិការ POS,
Include UOM,រួមបញ្ចូល UOM,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),ការធានារយៈពេល (នៅក្នុងថ្ងៃ),
Auto re-order,ការបញ្ជាទិញជាថ្មីម្តងទៀតដោយស្វ័យប្រវត្តិ,
Reorder level based on Warehouse,កម្រិតនៃការរៀបចំដែលមានមូលដ្ឋានលើឃ្លាំង,
-Will also apply for variants unless overrridden,ក៏នឹងអនុវត្តសម្រាប់វ៉ារ្យ៉ង់បានទេលុះត្រាតែ overrridden,
+Will also apply for variants unless overridden,ក៏នឹងអនុវត្តសម្រាប់វ៉ារ្យ៉ង់បានទេលុះត្រាតែ overridden,
Units of Measure,ឯកតារង្វាស់,
Will also apply for variants,ក៏នឹងអនុវត្តសម្រាប់វ៉ារ្យ៉ង់,
Serial Nos and Batches,សៀរៀល nos និងជំនាន់,
diff --git a/erpnext/translations/kn.csv b/erpnext/translations/kn.csv
index 18e44a1..8b27168 100644
--- a/erpnext/translations/kn.csv
+++ b/erpnext/translations/kn.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","ಮಲ್ಟಿ-ಟೈರ್ ಪ್ರೋಗ್ರಾಂನ ಸಂದರ್ಭದಲ್ಲಿ, ಗ್ರಾಹಕರು ತಮ್ಮ ಖರ್ಚುಗೆ ಅನುಗುಣವಾಗಿ ಆಯಾ ಶ್ರೇಣಿಗೆ ಸ್ವಯಂ ನಿಯೋಜಿಸಲಾಗುವುದು",
Inactive,ನಿಷ್ಕ್ರಿಯವಾಗಿದೆ,
Incentives,ಪ್ರೋತ್ಸಾಹ,
-Include Default Book Entries,ಡೀಫಾಲ್ಟ್ ಪುಸ್ತಕ ನಮೂದುಗಳನ್ನು ಸೇರಿಸಿ,
+Include Default FB Entries,ಡೀಫಾಲ್ಟ್ ಪುಸ್ತಕ ನಮೂದುಗಳನ್ನು ಸೇರಿಸಿ,
Include Exploded Items,ಸ್ಫೋಟಗೊಂಡ ವಸ್ತುಗಳನ್ನು ಸೇರಿಸಿ,
Include POS Transactions,ಪಿಒಎಸ್ ಟ್ರಾನ್ಸಾಕ್ಷನ್ಸ್ ಸೇರಿಸಿ,
Include UOM,UOM ಸೇರಿಸಿ,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),( ದಿನಗಳಲ್ಲಿ ) ಖಾತರಿ ಅವಧಿಯ,
Auto re-order,ಆಟೋ ಪುನಃ ಸಲುವಾಗಿ,
Reorder level based on Warehouse,ವೇರ್ಹೌಸ್ ಆಧರಿಸಿ ಮರುಕ್ರಮಗೊಳಿಸಿ ಮಟ್ಟದ,
-Will also apply for variants unless overrridden,Overrridden ಹೊರತು ಸಹ ರೂಪಾಂತರಗಳು ಅನ್ವಯವಾಗುವುದು,
+Will also apply for variants unless overridden,Overrridden ಹೊರತು ಸಹ ರೂಪಾಂತರಗಳು ಅನ್ವಯವಾಗುವುದು,
Units of Measure,ಮಾಪನದ ಘಟಕಗಳಿಗೆ,
Will also apply for variants,ಸಹ ರೂಪಾಂತರಗಳು ಅನ್ವಯವಾಗುವುದು,
Serial Nos and Batches,ಸೀರಿಯಲ್ ಸೂಲ ಮತ್ತು ಬ್ಯಾಚ್,
diff --git a/erpnext/translations/ko.csv b/erpnext/translations/ko.csv
index 788655c..edd3f27 100644
--- a/erpnext/translations/ko.csv
+++ b/erpnext/translations/ko.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",다중 계층 프로그램의 경우 고객은 지출 한대로 해당 계층에 자동으로 할당됩니다.,
Inactive,비활성,
Incentives,장려책,
-Include Default Book Entries,기본 도서 항목 포함,
+Include Default FB Entries,기본 도서 항목 포함,
Include Exploded Items,분해 된 항목 포함,
Include POS Transactions,POS 트랜잭션 포함,
Include UOM,UOM 포함,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),(일) 보증 기간,
Auto re-order,자동 재 주문,
Reorder level based on Warehouse,웨어 하우스를 기반으로 재정렬 수준,
-Will also apply for variants unless overrridden,overrridden가 아니면 변형 적용됩니다,
+Will also apply for variants unless overridden,overridden가 아니면 변형 적용됩니다,
Units of Measure,측정 단위,
Will also apply for variants,또한 변형 적용됩니다,
Serial Nos and Batches,일련 번호 및 배치,
diff --git a/erpnext/translations/ku.csv b/erpnext/translations/ku.csv
index a7fcf4e..e18ce45 100644
--- a/erpnext/translations/ku.csv
+++ b/erpnext/translations/ku.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Di rewşê de bernameya pir-tier, Ewrûpa dê ji hêla xercê xwe ve girêdayî xerîb be",
Inactive,Bêkar,
Incentives,aborîve,
-Include Default Book Entries,Navnîşanên Pirtûka Pêvek Bawer bikin,
+Include Default FB Entries,Navnîşanên Pirtûka Pêvek Bawer bikin,
Include Exploded Items,Included Dead Items,
Include POS Transactions,Têkiliyên POSê de,
Include UOM,UOM,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),Period Warranty (di rojên),
Auto re-order,Auto re-da,
Reorder level based on Warehouse,asta DIRTYHERTZ li ser Warehouse,
-Will also apply for variants unless overrridden,jî wê ji bo Guhertoyên serî heta overrridden,
+Will also apply for variants unless overridden,jî wê ji bo Guhertoyên serî heta overridden,
Units of Measure,Yekîneyên Measure,
Will also apply for variants,jî wê ji bo Guhertoyên serî,
Serial Nos and Batches,Serial Nos û lekerên,
diff --git a/erpnext/translations/lo.csv b/erpnext/translations/lo.csv
index 9b74f65..46acd22 100644
--- a/erpnext/translations/lo.csv
+++ b/erpnext/translations/lo.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","ໃນກໍລະນີຂອງໂຄງການຫຼາຍຂັ້ນ, ລູກຄ້າຈະໄດ້ຮັບການມອບຫມາຍໂດຍອັດຕະໂນມັດໃຫ້ກັບຂັ້ນຕອນທີ່ກ່ຽວຂ້ອງຕາມການໃຊ້ຈ່າຍຂອງເຂົາເຈົ້າ",
Inactive,Inactive,
Incentives,ສິ່ງຈູງໃຈ,
-Include Default Book Entries,ລວມທັງການອອກສຽງປື້ມແບບເລີ່ມຕົ້ນ,
+Include Default FB Entries,ລວມທັງການອອກສຽງປື້ມແບບເລີ່ມຕົ້ນ,
Include Exploded Items,ລວມເອົາສິ່ງທີ່ເກີດຂື້ນ,
Include POS Transactions,ລວມທຸລະກໍາ POS,
Include UOM,ລວມ UOM,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),ໄລຍະເວລາຮັບປະກັນ (ໃນວັນເວລາ),
Auto re-order,Auto Re: ຄໍາສັ່ງ,
Reorder level based on Warehouse,ລະດັບລໍາດັບຂຶ້ນຢູ່ກັບຄັງສິນຄ້າ,
-Will also apply for variants unless overrridden,ຍັງຈະນໍາໃຊ້ສໍາລັບການ variants ເວັ້ນເສຍແຕ່ວ່າ overrridden,
+Will also apply for variants unless overridden,ຍັງຈະນໍາໃຊ້ສໍາລັບການ variants ເວັ້ນເສຍແຕ່ວ່າ overridden,
Units of Measure,ຫົວຫນ່ວຍວັດແທກ,
Will also apply for variants,ຍັງຈະນໍາໃຊ້ສໍາລັບການ variants,
Serial Nos and Batches,Serial Nos ແລະສໍາຫລັບຂະບວນ,
diff --git a/erpnext/translations/lt.csv b/erpnext/translations/lt.csv
index 2c87256..292c9d8 100644
--- a/erpnext/translations/lt.csv
+++ b/erpnext/translations/lt.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Kalbant apie daugiapakopę programą, klientai bus automatiškai priskirti atitinkamam lygmeniui, atsižvelgiant į jų išleidimą",
Inactive,Neaktyvus,
Incentives,Paskatos,
-Include Default Book Entries,Įtraukite numatytuosius knygų įrašus,
+Include Default FB Entries,Įtraukite numatytuosius knygų įrašus,
Include Exploded Items,Įtraukti sprogus elementus,
Include POS Transactions,Įtraukti POS operacijas,
Include UOM,Įtraukti UOM,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),Garantinis laikotarpis (dienomis),
Auto re-order,Auto naujo užsakymas,
Reorder level based on Warehouse,Pertvarkyti lygį remiantis Warehouse,
-Will also apply for variants unless overrridden,Bus taikoma variantų nebent overrridden,
+Will also apply for variants unless overridden,Bus taikoma variantų nebent overridden,
Units of Measure,Matavimo vienetai,
Will also apply for variants,Bus taikoma variantų,
Serial Nos and Batches,Eilės Nr ir Partijos,
diff --git a/erpnext/translations/lv.csv b/erpnext/translations/lv.csv
index 2cfa130..52641b2 100644
--- a/erpnext/translations/lv.csv
+++ b/erpnext/translations/lv.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Daudzpakāpju programmas gadījumā Klienti tiks automātiski piešķirti attiecīgajam līmenim, salīdzinot ar viņu iztērēto",
Inactive,Neaktīvs,
Incentives,Stimuli,
-Include Default Book Entries,Iekļaujiet noklusējuma grāmatas ierakstus,
+Include Default FB Entries,Iekļaujiet noklusējuma grāmatas ierakstus,
Include Exploded Items,Iekļaut izpūstas preces,
Include POS Transactions,Iekļaut POS darījumus,
Include UOM,Iekļaut UOM,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),Garantijas periods (dienās),
Auto re-order,Auto re-pasūtīt,
Reorder level based on Warehouse,Pārkārtot līmenis balstās uz Noliktava,
-Will also apply for variants unless overrridden,"Attieksies arī uz variantiem, ja vien overrridden",
+Will also apply for variants unless overridden,"Attieksies arī uz variantiem, ja vien overridden",
Units of Measure,Mērvienību,
Will also apply for variants,Attieksies arī uz variantiem,
Serial Nos and Batches,Sērijas Nr un Partijām,
diff --git a/erpnext/translations/mk.csv b/erpnext/translations/mk.csv
index f01b1b0..0a29019 100644
--- a/erpnext/translations/mk.csv
+++ b/erpnext/translations/mk.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Во случај на повеќеслојна програма, корисниците ќе бидат автоматски доделени на засегнатите нивоа, како на нивните потрошени",
Inactive,Неактивен,
Incentives,Стимулации,
-Include Default Book Entries,Вклучете стандардни записи за книги,
+Include Default FB Entries,Вклучете стандардни записи за книги,
Include Exploded Items,Вклучи експлодирани елементи,
Include POS Transactions,Вклучете POS-трансакции,
Include UOM,Вклучете UOM,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),Гарантниот период (во денови),
Auto re-order,Автоматско повторно цел,
Reorder level based on Warehouse,Ниво врз основа на промените редоследот Магацински,
-Will also apply for variants unless overrridden,"Ќе се казни и варијанти, освен ако overrridden",
+Will also apply for variants unless overridden,"Ќе се казни и варијанти, освен ако overridden",
Units of Measure,На мерните единици,
Will also apply for variants,Ќе се применуваат и за варијанти,
Serial Nos and Batches,Сериски броеви и Пакетите,
diff --git a/erpnext/translations/ml.csv b/erpnext/translations/ml.csv
index 59d3160..04af8ab 100644
--- a/erpnext/translations/ml.csv
+++ b/erpnext/translations/ml.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","മൾട്ടി-ടയർ പരിപാടിയുടെ കാര്യത്തിൽ, കസ്റ്റമർമാർ ചെലവഴിച്ച തുക പ്രകാരം ബന്ധപ്പെട്ട ടീമിൽ ഓട്ടോ നിർണ്ണയിക്കും",
Inactive,നിഷ്ക്രിയം,
Incentives,ഇൻസെന്റീവ്സ്,
-Include Default Book Entries,സ്ഥിരസ്ഥിതി പുസ്തക എൻട്രികൾ ഉൾപ്പെടുത്തുക,
+Include Default FB Entries,സ്ഥിരസ്ഥിതി പുസ്തക എൻട്രികൾ ഉൾപ്പെടുത്തുക,
Include Exploded Items,എക്സ്പ്ലോഡഡ് ഇനങ്ങൾ ഉൾപ്പെടുത്തുക,
Include POS Transactions,POS ഇടപാടുകൾ ഉൾപ്പെടുത്തുക,
Include UOM,UOM ഉൾപ്പെടുത്തുക,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),(ദിവസങ്ങളിൽ) വാറന്റി കാലാവധി,
Auto re-order,ഓട്ടോ റീ-ഓർഡർ,
Reorder level based on Warehouse,വെയർഹൗസ് അടിസ്ഥാനമാക്കിയുള്ള പുനഃക്രമീകരിക്കുക തലത്തിൽ,
-Will also apply for variants unless overrridden,കൂടാതെ overrridden അവയൊഴിച്ച് മോഡലുകൾക്കാണ് ബാധകമാകും,
+Will also apply for variants unless overridden,കൂടാതെ overridden അവയൊഴിച്ച് മോഡലുകൾക്കാണ് ബാധകമാകും,
Units of Measure,അളവിന്റെ യൂണിറ്റുകൾ,
Will also apply for variants,കൂടാതെ മോഡലുകൾക്കാണ് ബാധകമാകും,
Serial Nos and Batches,സീരിയൽ എണ്ണം ബാച്ചുകളും,
diff --git a/erpnext/translations/mr.csv b/erpnext/translations/mr.csv
index ff339b6..785ab65 100644
--- a/erpnext/translations/mr.csv
+++ b/erpnext/translations/mr.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",बहु-स्तरीय कार्यक्रमाच्या बाबतीत ग्राहक त्यांच्या खर्चानुसार संबंधित टायरला स्वयंचलितरित्या नियुक्त केले जातील,
Inactive,निष्क्रिय,
Incentives,प्रोत्साहन,
-Include Default Book Entries,डीफॉल्ट पुस्तक नोंदी समाविष्ट करा,
+Include Default FB Entries,डीफॉल्ट पुस्तक नोंदी समाविष्ट करा,
Include Exploded Items,विस्फोट केलेल्या वस्तू समाविष्ट करा,
Include POS Transactions,पीओएस व्यवहार समाविष्ट करा,
Include UOM,यूओएम समाविष्ट करा,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),(दिवस मध्ये) वॉरंटी कालावधी,
Auto re-order,ऑटो पुन्हा आदेश,
Reorder level based on Warehouse,वखारवर आधारित पुन्हा क्रमवारी लावा पातळी,
-Will also apply for variants unless overrridden,Overrridden आहेत तोपर्यंत देखील रूपे लागू राहील,
+Will also apply for variants unless overridden,Overrridden आहेत तोपर्यंत देखील रूपे लागू राहील,
Units of Measure,माप युनिट,
Will also apply for variants,तसेच रूपे लागू राहील,
Serial Nos and Batches,सिरियल क्र आणि बॅच,
diff --git a/erpnext/translations/ms.csv b/erpnext/translations/ms.csv
index 2258a18..db20d3c 100644
--- a/erpnext/translations/ms.csv
+++ b/erpnext/translations/ms.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Dalam hal program multi-tier, Pelanggan akan ditugaskan secara automatik ke peringkat yang bersangkutan seperti yang dibelanjakannya",
Inactive,Tidak aktif,
Incentives,Insentif,
-Include Default Book Entries,Sertakan Penyertaan Buku Lalai,
+Include Default FB Entries,Sertakan Penyertaan Buku Lalai,
Include Exploded Items,Termasuk Item Meletup,
Include POS Transactions,Termasuk Transaksi POS,
Include UOM,Termasuk UOM,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),Tempoh jaminan (dalam hari),
Auto re-order,Auto semula perintah,
Reorder level based on Warehouse,Tahap pesanan semula berdasarkan Warehouse,
-Will also apply for variants unless overrridden,Juga akan memohon varian kecuali overrridden,
+Will also apply for variants unless overridden,Juga akan memohon varian kecuali overridden,
Units of Measure,Unit ukuran,
Will also apply for variants,Juga akan memohon varian,
Serial Nos and Batches,Serial Nos dan Kelompok,
diff --git a/erpnext/translations/my.csv b/erpnext/translations/my.csv
index dc5ab12..f4b8676 100644
--- a/erpnext/translations/my.csv
+++ b/erpnext/translations/my.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Multi-tier အစီအစဉ်၏အမှု၌, Customer များအလိုအလျောက်သူတို့ရဲ့သုံးစွဲနှုန်းအတိုင်းစိုးရိမ်ပူပန်ဆင့်မှတာဝန်ပေးအပ်ပါလိမ့်မည်",
Inactive,မလှုပ်ရှားတတ်သော,
Incentives,မက်လုံးတွေပေးပြီး,
-Include Default Book Entries,ပုံမှန်စာအုပ် Entries Include,
+Include Default FB Entries,ပုံမှန်စာအုပ် Entries Include,
Include Exploded Items,ပေါက်ကွဲပစ္စည်းများ Include,
Include POS Transactions,POS အရောင်းအဝယ် Include,
Include UOM,UOM Include,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),(ရက်) ကိုအာမခံကာလ,
Auto re-order,မော်တော်ကားပြန်လည်အမိန့်,
Reorder level based on Warehouse,ဂိုဒေါင်အပေါ်အခြေခံပြီး reorder level ကို,
-Will also apply for variants unless overrridden,စ overrridden မဟုတ်လျှင်မျိုးကွဲလျှောက်ထားလိမ့်မည်ဟု,
+Will also apply for variants unless overridden,စ overridden မဟုတ်လျှင်မျိုးကွဲလျှောက်ထားလိမ့်မည်ဟု,
Units of Measure,တိုင်း၏ယူနစ်,
Will also apply for variants,စမျိုးကွဲလျှောက်ထားလိမ့်မည်ဟု,
Serial Nos and Batches,serial Nos နှင့် batch,
diff --git a/erpnext/translations/nl.csv b/erpnext/translations/nl.csv
index ad11eae..1778c80 100644
--- a/erpnext/translations/nl.csv
+++ b/erpnext/translations/nl.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",In het geval van een meerlagig programma worden klanten automatisch toegewezen aan de betreffende laag op basis van hun bestede tijd,
Inactive,Inactief,
Incentives,Incentives,
-Include Default Book Entries,Standaard boekvermeldingen opnemen,
+Include Default FB Entries,Standaard boekvermeldingen opnemen,
Include Exploded Items,Exploded Items opnemen,
Include POS Transactions,POS-transacties opnemen,
Include UOM,Inclusief UOM,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),Garantieperiode (in dagen),
Auto re-order,Auto re-order,
Reorder level based on Warehouse,Bestelniveau gebaseerd op Warehouse,
-Will also apply for variants unless overrridden,Geldt ook voor varianten tenzij overrridden,
+Will also apply for variants unless overridden,Geldt ook voor varianten tenzij overridden,
Units of Measure,Meeteenheden,
Will also apply for variants,Geldt ook voor varianten,
Serial Nos and Batches,Serienummers en batches,
diff --git a/erpnext/translations/no.csv b/erpnext/translations/no.csv
index b136e97..542217a 100644
--- a/erpnext/translations/no.csv
+++ b/erpnext/translations/no.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Når det gjelder flerlagsprogram, vil kundene automatisk bli tilordnet den aktuelle delen som brukt",
Inactive,inaktiv,
Incentives,Motivasjon,
-Include Default Book Entries,Inkluder standardbokoppføringer,
+Include Default FB Entries,Inkluder standardbokoppføringer,
Include Exploded Items,Inkluder eksploderte elementer,
Include POS Transactions,Inkluder POS-transaksjoner,
Include UOM,Inkluder UOM,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),Garantiperioden (i dager),
Auto re-order,Auto re-order,
Reorder level based on Warehouse,Omgjøre nivå basert på Warehouse,
-Will also apply for variants unless overrridden,Vil også gjelde for varianter med mindre overrridden,
+Will also apply for variants unless overridden,Vil også gjelde for varianter med mindre overridden,
Units of Measure,Måleenheter,
Will also apply for variants,Vil også gjelde for varianter,
Serial Nos and Batches,Serienummer og partier,
diff --git a/erpnext/translations/pl.csv b/erpnext/translations/pl.csv
index 9be56f3..247d0ba 100644
--- a/erpnext/translations/pl.csv
+++ b/erpnext/translations/pl.csv
@@ -1151,7 +1151,7 @@
In Value,w polu Wartość,
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","W przypadku programu wielowarstwowego Klienci zostaną automatycznie przypisani do danego poziomu, zgodnie z wydatkami",
Inactive,Nieaktywny,
-Include Default Book Entries,Dołącz domyślne wpisy książki,
+Include Default FB Entries,Dołącz domyślne wpisy książki,
Include Exploded Items,Dołącz rozstrzelone przedmioty,
Include POS Transactions,Uwzględnij transakcje POS,
Include UOM,Dołącz UOM,
@@ -6987,7 +6987,7 @@
Warranty Period (in days),Okres gwarancji (w dniach),
Auto re-order,Automatyczne ponowne zamówienie,
Reorder level based on Warehouse,Zmiana kolejności w oparciu o poziom Magazynu,
-Will also apply for variants unless overrridden,"Również zostanie zastosowany do wariantów, chyba że zostanie nadpisany",
+Will also apply for variants unless overridden,"Również zostanie zastosowany do wariantów, chyba że zostanie nadpisany",
Units of Measure,Jednostki miary,
Will also apply for variants,Również zastosowanie do wariantów,
Serial Nos and Batches,Numery seryjne i partie,
diff --git a/erpnext/translations/ps.csv b/erpnext/translations/ps.csv
index 8033752..09d4df3 100644
--- a/erpnext/translations/ps.csv
+++ b/erpnext/translations/ps.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",د څو اړخیز پروګرام په صورت کې، پیرودونکي به د خپل مصرف په اساس اړوند ټیټ ته ګمارل کیږي,
Inactive,غیر فعال,
Incentives,هڅوونکي,
-Include Default Book Entries,د ډیفالټ کتاب ننوتل شامل کړئ,
+Include Default FB Entries,د ډیفالټ کتاب ننوتل شامل کړئ,
Include Exploded Items,چاودیدونکي توکي شامل کړئ,
Include POS Transactions,د POS تعاملات شامل کړئ,
Include UOM,UOM شامل کړئ,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),ګرنټی د دورې (په ورځو),
Auto re-order,د موټرونو د بيا نظم,
Reorder level based on Warehouse,ترمیمي په کچه د پر بنسټ د ګدام,
-Will also apply for variants unless overrridden,مګر overrridden به د بېرغونو هم تر غوږو,
+Will also apply for variants unless overridden,مګر overridden به د بېرغونو هم تر غوږو,
Units of Measure,د اندازه کولو واحدونه,
Will also apply for variants,به هم د بېرغونو درخواست,
Serial Nos and Batches,سریال وځيري او دستو,
diff --git a/erpnext/translations/pt-BR.csv b/erpnext/translations/pt-BR.csv
index 9823470..92845b0 100644
--- a/erpnext/translations/pt-BR.csv
+++ b/erpnext/translations/pt-BR.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","No caso do programa multicamadas, os Clientes serão atribuídos automaticamente ao nível em questão de acordo com o gasto",
Inactive,Inativo,
Incentives,Incentivos,
-Include Default Book Entries,Incluir Entradas de Livro Padrão,
+Include Default FB Entries,Incluir Entradas de Livro Padrão,
Include Exploded Items,Incluir Itens Explodidos,
Include POS Transactions,Incluir Transações PDV,
Include UOM,Incluir UDM,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),Período de Garantia (em dias),
Auto re-order,Reposição Automática,
Reorder level based on Warehouse,Nível de reposição baseado no Armazén,
-Will also apply for variants unless overrridden,Também se aplica a variantes a não ser que seja sobrescrito,
+Will also apply for variants unless overridden,Também se aplica a variantes a não ser que seja sobrescrito,
Units of Measure,Unidades de Medida,
Will also apply for variants,Também se aplica às variantes,
Serial Nos and Batches,Números de Série e Lotes,
diff --git a/erpnext/translations/pt.csv b/erpnext/translations/pt.csv
index c2afe32..58cf6c8 100644
--- a/erpnext/translations/pt.csv
+++ b/erpnext/translations/pt.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","No caso do programa multicamadas, os Clientes serão atribuídos automaticamente ao nível em questão de acordo com o gasto",
Inactive,Inativo,
Incentives,Incentivos,
-Include Default Book Entries,Incluir entradas de livro padrão,
+Include Default FB Entries,Incluir entradas de livro padrão,
Include Exploded Items,Incluir itens explodidos,
Include POS Transactions,Incluir transações POS,
Include UOM,Incluir UOM,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),Período de Garantia (em dias),
Auto re-order,Voltar a Pedir Autom.,
Reorder level based on Warehouse,Nível de reencomenda no Armazém,
-Will also apply for variants unless overrridden,Também se aplica para as variantes a menos que seja anulado,
+Will also apply for variants unless overridden,Também se aplica para as variantes a menos que seja anulado,
Units of Measure,Unidades de medida,
Will also apply for variants,Também se aplicará para as variantes,
Serial Nos and Batches,Números de série e lotes,
diff --git a/erpnext/translations/ro.csv b/erpnext/translations/ro.csv
index 0cb3f84..935b1e6 100644
--- a/erpnext/translations/ro.csv
+++ b/erpnext/translations/ro.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","În cazul unui program cu mai multe niveluri, Clienții vor fi automat alocați nivelului respectiv în funcție de cheltuielile efectuate",
Inactive,Inactiv,
Incentives,stimulente,
-Include Default Book Entries,Includeți intrări implicite în cărți,
+Include Default FB Entries,Includeți intrări implicite în cărți,
Include Exploded Items,Includeți articole explodate,
Include POS Transactions,Includeți tranzacțiile POS,
Include UOM,Includeți UOM,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),Perioada de garanție (în zile),
Auto re-order,Re-comandă automată,
Reorder level based on Warehouse,Nivel pentru re-comanda bazat pe Magazie,
-Will also apply for variants unless overrridden,Se va aplica și pentru variantele cu excepția cazului în overrridden,
+Will also apply for variants unless overridden,Se va aplica și pentru variantele cu excepția cazului în overridden,
Units of Measure,Unitati de masura,
Will also apply for variants,"Va aplică, de asemenea pentru variante",
Serial Nos and Batches,Numere și loturi seriale,
diff --git a/erpnext/translations/ru.csv b/erpnext/translations/ru.csv
index da4e1be..2f6f361 100644
--- a/erpnext/translations/ru.csv
+++ b/erpnext/translations/ru.csv
@@ -1151,7 +1151,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",В случае многоуровневой программы Клиенты будут автоматически назначены соответствующему уровню в соответствии с затраченными,
Inactive,Неактивный,
Incentives,Стимулирование,
-Include Default Book Entries,Включить записи в книгу по умолчанию,
+Include Default FB Entries,Включить записи в книгу по умолчанию,
Include Exploded Items,Включить раздробленные элементы,
Include POS Transactions,Включить POS-транзакции,
Include UOM,Включить UOM,
@@ -6985,7 +6985,7 @@
Warranty Period (in days),Гарантийный период (дней),
Auto re-order,Автоматический перезаказ,
Reorder level based on Warehouse,Уровень переупорядочивания на основе склада,
-Will also apply for variants unless overrridden,"Будет также применяться для модификаций, если не отменено",
+Will also apply for variants unless overridden,"Будет также применяться для модификаций, если не отменено",
Units of Measure,Единицы измерения,
Will also apply for variants,Также применять к модификациям,
Serial Nos and Batches,Серийные номера и партии,
diff --git a/erpnext/translations/rw.csv b/erpnext/translations/rw.csv
index ae0cb3a..59362a1 100644
--- a/erpnext/translations/rw.csv
+++ b/erpnext/translations/rw.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Kubijyanye na gahunda yo mu byiciro byinshi, Abakiriya bazahabwa imodoka bashinzwe urwego bireba nkuko bakoresheje",
Inactive,Kudakora,
Incentives,Inkunga,
-Include Default Book Entries,Shyiramo Ibisanzwe Byanditswe,
+Include Default FB Entries,Shyiramo Ibisanzwe Byanditswe,
Include Exploded Items,Shyiramo Ibintu Biturika,
Include POS Transactions,Shyiramo ibikorwa bya POS,
Include UOM,Shyiramo UOM,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),Igihe cya garanti (muminsi),
Auto re-order,Ongera utumire,
Reorder level based on Warehouse,Urwego rwo kwisubiramo rushingiye kububiko,
-Will also apply for variants unless overrridden,Uzasaba kandi kubitandukanye keretse birenze,
+Will also apply for variants unless overridden,Uzasaba kandi kubitandukanye keretse birenze,
Units of Measure,Ibipimo,
Will also apply for variants,Uzasaba kandi kubitandukanye,
Serial Nos and Batches,Urutonde Nomero,
diff --git a/erpnext/translations/si.csv b/erpnext/translations/si.csv
index fa6fbf0..dd2acc4 100644
--- a/erpnext/translations/si.csv
+++ b/erpnext/translations/si.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","බහු ස්ථරයේ වැඩසටහනක දී, පාරිභෝගිකයින් විසින් වැය කරනු ලබන පරිදි පාරිභෝගිකයින්ට අදාල ස්ථානයට ස්වයංක්රීයව පවරනු ලැබේ",
Inactive,අක්රියයි,
Incentives,සහන,
-Include Default Book Entries,පෙරනිමි පොත් ඇතුළත් කිරීම් ඇතුළත් කරන්න,
+Include Default FB Entries,පෙරනිමි පොත් ඇතුළත් කිරීම් ඇතුළත් කරන්න,
Include Exploded Items,පුපුරණ ද්රව්ය අඩංගු කරන්න,
Include POS Transactions,POS ගනුදෙනු,
Include UOM,UOM ඇතුළත් කරන්න,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),වගකීම් කාලය (දින තුළ),
Auto re-order,වාහන නැවත අනුපිළිවෙලට,
Reorder level based on Warehouse,ගබඩාව මත පදනම් මොහොත මට්ටමේ,
-Will also apply for variants unless overrridden,ද overrridden මිස ප්රභේද සඳහා අයදුම් කරනු ඇත,
+Will also apply for variants unless overridden,ද overridden මිස ප්රභේද සඳහා අයදුම් කරනු ඇත,
Units of Measure,නු ඒකක,
Will also apply for variants,ද ප්රභේද සඳහා අයදුම් කරනු ඇත,
Serial Nos and Batches,අනුක්රමික අංක සහ කාණ්ඩ,
diff --git a/erpnext/translations/sk.csv b/erpnext/translations/sk.csv
index 0e51158..025c8b7 100644
--- a/erpnext/translations/sk.csv
+++ b/erpnext/translations/sk.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",V prípade viacvrstvového programu budú zákazníci automaticky priradení príslušnému vrstvu podľa ich vynaložených prostriedkov,
Inactive,neaktívne,
Incentives,Pobídky,
-Include Default Book Entries,Zahrnúť predvolené položky knihy,
+Include Default FB Entries,Zahrnúť predvolené položky knihy,
Include Exploded Items,Zahrňte explodované položky,
Include POS Transactions,Zahrňte POS transakcie,
Include UOM,Zahrňte UOM,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),Záruční doba (ve dnech),
Auto re-order,Auto re-order,
Reorder level based on Warehouse,Úroveň Zmena poradia na základe Warehouse,
-Will also apply for variants unless overrridden,"Bude platiť aj pre varianty, pokiaľ nebude prepísané",
+Will also apply for variants unless overridden,"Bude platiť aj pre varianty, pokiaľ nebude prepísané",
Units of Measure,merné jednotky,
Will also apply for variants,Bude platiť aj pre varianty,
Serial Nos and Batches,Sériové čísla a dávky,
diff --git a/erpnext/translations/sl.csv b/erpnext/translations/sl.csv
index 7c4e11b..86b5e58 100644
--- a/erpnext/translations/sl.csv
+++ b/erpnext/translations/sl.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",V primeru večstopenjskega programa bodo stranke samodejno dodeljene zadevni stopnji glede na porabljene,
Inactive,Neaktivno,
Incentives,Spodbude,
-Include Default Book Entries,Vključi privzete vnose v knjige,
+Include Default FB Entries,Vključi privzete vnose v knjige,
Include Exploded Items,Vključi eksplodirane elemente,
Include POS Transactions,Vključite POS transakcije,
Include UOM,Vključi UOM,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),Garancijski rok (v dnevih),
Auto re-order,Auto re-order,
Reorder level based on Warehouse,Raven Preureditev temelji na Warehouse,
-Will also apply for variants unless overrridden,Bo veljalo tudi za variante razen overrridden,
+Will also apply for variants unless overridden,Bo veljalo tudi za variante razen overridden,
Units of Measure,Merske enote,
Will also apply for variants,Bo veljalo tudi za variante,
Serial Nos and Batches,Serijska št in Serije,
diff --git a/erpnext/translations/sq.csv b/erpnext/translations/sq.csv
index 0f10795..3cfa429 100644
--- a/erpnext/translations/sq.csv
+++ b/erpnext/translations/sq.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Në rastin e programit multi-shtresor, Konsumatorët do të caktohen automatikisht në nivelin përkatës sipas shpenzimeve të tyre",
Inactive,joaktiv,
Incentives,Nxitjet,
-Include Default Book Entries,Përfshini hyrje të librave të paracaktuar,
+Include Default FB Entries,Përfshini hyrje të librave të paracaktuar,
Include Exploded Items,Përfshirja e artikujve të eksploduar,
Include POS Transactions,Përfshi transaksione POS,
Include UOM,Përfshi UOM,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),Garanci Periudha (në ditë),
Auto re-order,Auto ri-qëllim,
Reorder level based on Warehouse,Niveli Reorder bazuar në Magazina,
-Will also apply for variants unless overrridden,Gjithashtu do të aplikojë për variantet nëse overrridden,
+Will also apply for variants unless overridden,Gjithashtu do të aplikojë për variantet nëse overridden,
Units of Measure,Njësitë e masës,
Will also apply for variants,Gjithashtu do të aplikojë për variantet,
Serial Nos and Batches,Serial Nr dhe Batches,
diff --git a/erpnext/translations/sr.csv b/erpnext/translations/sr.csv
index 38116ec..621772f 100644
--- a/erpnext/translations/sr.csv
+++ b/erpnext/translations/sr.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","У случају мулти-тиер програма, Корисници ће аутоматски бити додијељени за одређени ниво према њиховом потрошеном",
Inactive,Неактиван,
Incentives,Подстицаји,
-Include Default Book Entries,Укључивање заданих уноса у књиге,
+Include Default FB Entries,Укључивање заданих уноса у књиге,
Include Exploded Items,Укључите експлодиране ставке,
Include POS Transactions,Укључите ПОС трансакције,
Include UOM,Укључите УОМ,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),Гарантни период (у данима),
Auto re-order,Ауто поново реда,
Reorder level based on Warehouse,Промени редослед ниво на основу Варехоусе,
-Will also apply for variants unless overrridden,Ће конкурисати и за варијанте осим оверрридден,
+Will also apply for variants unless overridden,Ће конкурисати и за варијанте осим оверрридден,
Units of Measure,Мерних јединица,
Will also apply for variants,Ће конкурисати и за варијанте,
Serial Nos and Batches,Сериал Нос анд Пакети,
diff --git a/erpnext/translations/sv.csv b/erpnext/translations/sv.csv
index c4d46ea..4fef88b 100644
--- a/erpnext/translations/sv.csv
+++ b/erpnext/translations/sv.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",När det gäller program med flera nivåer kommer kunderna automatiskt att tilldelas den aktuella tiern enligt deras tillbringade,
Inactive,Inaktiv,
Incentives,Sporen,
-Include Default Book Entries,Inkludera standardbokposter,
+Include Default FB Entries,Inkludera standardbokposter,
Include Exploded Items,Inkludera explosiva artiklar,
Include POS Transactions,Inkludera POS-transaktioner,
Include UOM,Inkludera UOM,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),Garantitiden (i dagar),
Auto re-order,Auto återbeställning,
Reorder level based on Warehouse,Beställningsnivå baserat på Warehouse,
-Will also apply for variants unless overrridden,Kommer också att ansöka om varianter såvida inte överskriden,
+Will also apply for variants unless overridden,Kommer också att ansöka om varianter såvida inte överskriden,
Units of Measure,Måttenheter,
Will also apply for variants,Kommer också att ansöka om varianter,
Serial Nos and Batches,Serienummer och partier,
diff --git a/erpnext/translations/sw.csv b/erpnext/translations/sw.csv
index ad144f0..3b4d8ae 100644
--- a/erpnext/translations/sw.csv
+++ b/erpnext/translations/sw.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Katika kesi ya mpango wa mipango mbalimbali, Wateja watapatiwa auto kwa tier husika kama kwa matumizi yao",
Inactive,Haikufanya kazi,
Incentives,Vidokezo,
-Include Default Book Entries,Jumuisha Ingizo Mbadala za Kitabu,
+Include Default FB Entries,Jumuisha Ingizo Mbadala za Kitabu,
Include Exploded Items,Jumuisha Vipengee Vipengee,
Include POS Transactions,Jumuisha Shughuli za POS,
Include UOM,Jumuisha UOM,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),Kipindi cha udhamini (katika siku),
Auto re-order,Rejesha upya,
Reorder level based on Warehouse,Weka upya ngazi kulingana na Ghala,
-Will also apply for variants unless overrridden,Pia itatumika kwa vipengee isipokuwa imeingizwa,
+Will also apply for variants unless overridden,Pia itatumika kwa vipengee isipokuwa imeingizwa,
Units of Measure,Units of Measure,
Will also apply for variants,Pia itatumika kwa vipengee,
Serial Nos and Batches,Serial Nos na Batches,
diff --git a/erpnext/translations/ta.csv b/erpnext/translations/ta.csv
index 5ccabc0..f40e512 100644
--- a/erpnext/translations/ta.csv
+++ b/erpnext/translations/ta.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","பல அடுக்கு திட்டத்தின் விஷயத்தில், வாடிக்கையாளர்கள் தங்கள் செலவினங்களின்படி சம்பந்தப்பட்ட அடுக்குக்கு கார் ஒதுக்கப்படுவார்கள்",
Inactive,செயல்படா,
Incentives,செயல் தூண்டுதல்,
-Include Default Book Entries,இயல்புநிலை புத்தக உள்ளீடுகளைச் சேர்க்கவும்,
+Include Default FB Entries,இயல்புநிலை புத்தக உள்ளீடுகளைச் சேர்க்கவும்,
Include Exploded Items,வெடித்துள்ள பொருட்கள் அடங்கும்,
Include POS Transactions,POS பரிமாற்றங்களைச் சேர்க்கவும்,
Include UOM,UOM ஐ சேர்க்கவும்,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),உத்தரவாதத்தை காலம் (நாட்கள்),
Auto re-order,வாகன மறு ஒழுங்கு,
Reorder level based on Warehouse,கிடங்கில் அடிப்படையில் மறுவரிசைப்படுத்துக நிலை,
-Will also apply for variants unless overrridden,Overrridden வரை கூட வகைகளில் விண்ணப்பிக்க,
+Will also apply for variants unless overridden,Overrridden வரை கூட வகைகளில் விண்ணப்பிக்க,
Units of Measure,அளவின் அலகுகள்,
Will also apply for variants,கூட வகைகளில் விண்ணப்பிக்க,
Serial Nos and Batches,சீரியல் எண்கள் மற்றும் தொகுப்புகளும்,
diff --git a/erpnext/translations/te.csv b/erpnext/translations/te.csv
index 8472163..22fd7d7 100644
--- a/erpnext/translations/te.csv
+++ b/erpnext/translations/te.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","మల్టీ-టైర్ ప్రోగ్రామ్ విషయంలో, కస్టమర్లు వారి గడువు ప్రకారం, సంబంధిత స్థాయికి కేటాయించబడతారు",
Inactive,క్రియారహిత,
Incentives,ఇన్సెంటివ్స్,
-Include Default Book Entries,డిఫాల్ట్ బుక్ ఎంట్రీలను చేర్చండి,
+Include Default FB Entries,డిఫాల్ట్ బుక్ ఎంట్రీలను చేర్చండి,
Include Exploded Items,ఎక్స్ప్లోడ్ ఐటెమ్లను చేర్చండి,
Include POS Transactions,POS లావాదేవీలను చేర్చండి,
Include UOM,UOM ని చేర్చండి,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),(రోజుల్లో) వారంటీ వ్యవధి,
Auto re-order,ఆటో క్రమాన్ని,
Reorder level based on Warehouse,వేర్హౌస్ ఆధారంగా క్రమాన్ని స్థాయి,
-Will also apply for variants unless overrridden,Overrridden తప్ప కూడా రూపాంతరాలు వర్తిస్తాయని,
+Will also apply for variants unless overridden,Overrridden తప్ప కూడా రూపాంతరాలు వర్తిస్తాయని,
Units of Measure,యూనిట్స్ ఆఫ్ మెజర్,
Will also apply for variants,కూడా రూపాంతరాలు వర్తిస్తాయని,
Serial Nos and Batches,సీరియల్ Nos మరియు ఇస్తున్న,
diff --git a/erpnext/translations/th.csv b/erpnext/translations/th.csv
index dcd632b..5dfb93c 100644
--- a/erpnext/translations/th.csv
+++ b/erpnext/translations/th.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",ในกรณีของโปรแกรมแบบหลายชั้นลูกค้าจะได้รับการกำหนดให้โดยอัตโนมัติตามระดับที่เกี่ยวข้องตามการใช้จ่าย,
Inactive,เฉื่อยชา,
Incentives,แรงจูงใจ,
-Include Default Book Entries,รวมรายการหนังสือเริ่มต้น,
+Include Default FB Entries,รวมรายการหนังสือเริ่มต้น,
Include Exploded Items,รวมรายการที่ระเบิดแล้ว,
Include POS Transactions,รวมธุรกรรม POS,
Include UOM,รวม UOM,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),ระยะเวลารับประกัน (วัน),
Auto re-order,Auto สั่งซื้อใหม่,
Reorder level based on Warehouse,ระดับสั่งซื้อใหม่บนพื้นฐานของคลังสินค้า,
-Will also apply for variants unless overrridden,นอกจากนี้ยังจะใช้สำหรับสายพันธุ์เว้นแต่ overrridden,
+Will also apply for variants unless overridden,นอกจากนี้ยังจะใช้สำหรับสายพันธุ์เว้นแต่ overridden,
Units of Measure,หน่วยวัด,
Will also apply for variants,นอกจากนี้ยังจะใช้สำหรับสายพันธุ์,
Serial Nos and Batches,หมายเลขและชุดเลขที่ผลิตภัณฑ์,
diff --git a/erpnext/translations/tr.csv b/erpnext/translations/tr.csv
index 3708246..82d2824 100644
--- a/erpnext/translations/tr.csv
+++ b/erpnext/translations/tr.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Çok katmanlı program söz konusu olduğunda, Müşteriler harcanan esasa göre ilgili kademeye otomatik olarak atanacaktır.",
Inactive,etkisiz,
Incentives,Teşvikler,
-Include Default Book Entries,Varsayılan Defter Girişlerini Dahil et,
+Include Default FB Entries,Varsayılan Defter Girişlerini Dahil et,
Include Exploded Items,Patlatılmış Öğeleri Dahil et,
Include POS Transactions,POS İşlemlerini Dahil et,
Include UOM,Birimi Dahil et,
@@ -7051,7 +7051,7 @@
Warranty Period (in days),Garanti Süresi (gün),
Auto re-order,Otomatik Yeniden Sipariş,
Reorder level based on Warehouse,Depo bazlı Yeniden sipariş seviyesi,
-Will also apply for variants unless overrridden,Geçersiz kılınmadığı sürece varyantlar için de geçerli olacaktır.,
+Will also apply for variants unless overridden,Geçersiz kılınmadığı sürece varyantlar için de geçerli olacaktır.,
Units of Measure,Ölçü Birimleri,
Will also apply for variants,Varyantlar için de geçerli olacak,
Serial Nos and Batches,Seri No ve Batches (Parti),
diff --git a/erpnext/translations/uk.csv b/erpnext/translations/uk.csv
index 1752330..f77c6da 100644
--- a/erpnext/translations/uk.csv
+++ b/erpnext/translations/uk.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","У випадку багаторівневої програми, Клієнти будуть автоматично призначені для відповідного рівня відповідно до витрачених ними витрат",
Inactive,Неактивний,
Incentives,Стимули,
-Include Default Book Entries,Включити записи за замовчуванням,
+Include Default FB Entries,Включити записи за замовчуванням,
Include Exploded Items,Включити вибухнуті елементи,
Include POS Transactions,Включити POS-транзакції,
Include UOM,Включити UOM,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),Гарантійний термін (в днях),
Auto re-order,Авто-дозамовлення,
Reorder level based on Warehouse,Рівень перезамовлення по складу,
-Will also apply for variants unless overrridden,"Буде також застосовуватися для варіантів, якщо не перевказано",
+Will also apply for variants unless overridden,"Буде також застосовуватися для варіантів, якщо не перевказано",
Units of Measure,одиниці виміру,
Will also apply for variants,Буде також застосовуватися для варіантів,
Serial Nos and Batches,Серійні номери і Порції,
diff --git a/erpnext/translations/ur.csv b/erpnext/translations/ur.csv
index 504cc8d..4dc872b 100644
--- a/erpnext/translations/ur.csv
+++ b/erpnext/translations/ur.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",کثیر درجے کے پروگرام کے معاملے میں، صارفین اپنے اخراجات کے مطابق متعلقہ درجے کو خود کار طریقے سے تفویض کریں گے,
Inactive,غیر فعال,
Incentives,ترغیبات,
-Include Default Book Entries,ڈیفالٹ کتاب اندراجات شامل کریں۔,
+Include Default FB Entries,ڈیفالٹ کتاب اندراجات شامل کریں۔,
Include Exploded Items,دھماکہ خیز اشیاء شامل کریں,
Include POS Transactions,پی او ایس کے لین دین میں شامل کریں,
Include UOM,UOM شامل کریں,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),(دن میں) وارنٹی مدت,
Auto re-order,آٹو دوبارہ آرڈر,
Reorder level based on Warehouse,گودام کی بنیاد پر ترتیب کی سطح کو منتخب,
-Will also apply for variants unless overrridden,overrridden جب تک بھی مختلف حالتوں کے لئے لاگو ہوں گے,
+Will also apply for variants unless overridden,overridden جب تک بھی مختلف حالتوں کے لئے لاگو ہوں گے,
Units of Measure,پیمائش کی اکائیوں,
Will also apply for variants,بھی مختلف حالتوں کے لئے لاگو ہوں گے,
Serial Nos and Batches,سیریل نمبر اور بیچوں,
diff --git a/erpnext/translations/uz.csv b/erpnext/translations/uz.csv
index 6b25e7b..c09aa89 100644
--- a/erpnext/translations/uz.csv
+++ b/erpnext/translations/uz.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",Ko'p qatlamli dasturda mijozlar o'zlari sarflagan xarajatlariga muvofiq tegishli darajaga avtomatik tarzda topshiriladi,
Inactive,Faol emas,
Incentives,Rag'batlantirish,
-Include Default Book Entries,Odatiy kitob yozuvlarini qo'shing,
+Include Default FB Entries,Odatiy kitob yozuvlarini qo'shing,
Include Exploded Items,Portlatilgan narsalarni qo'shish,
Include POS Transactions,Qalin operatsiyalarni qo'shish,
Include UOM,UOM ni qo'shing,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),Kafolat muddati (kunlar),
Auto re-order,Avtomatik buyurtma,
Reorder level based on Warehouse,Qoidalarga asoslangan qayta tartiblash,
-Will also apply for variants unless overrridden,"Variantlar uchun ham qo'llanilmaydi, agar bekor qilinsa",
+Will also apply for variants unless overridden,"Variantlar uchun ham qo'llanilmaydi, agar bekor qilinsa",
Units of Measure,O'lchov birliklari,
Will also apply for variants,"Shuningdek, variantlar uchun ham qo'llaniladi",
Serial Nos and Batches,Seriya nos va paketlar,
diff --git a/erpnext/translations/vi.csv b/erpnext/translations/vi.csv
index e576ef7..eb251a5 100644
--- a/erpnext/translations/vi.csv
+++ b/erpnext/translations/vi.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent","Trong trường hợp chương trình nhiều tầng, Khách hàng sẽ được tự động chỉ định cho cấp có liên quan theo mức chi tiêu của họ",
Inactive,Không hoạt động,
Incentives,Ưu đãi,
-Include Default Book Entries,Bao gồm các mục sách mặc định,
+Include Default FB Entries,Bao gồm các mục sách mặc định,
Include Exploded Items,Bao gồm các mục đã Phát hiện,
Include POS Transactions,Bao gồm giao dịch POS,
Include UOM,Bao gồm UOM,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),Thời gian bảo hành (trong...ngày),
Auto re-order,Auto lại trật tự,
Reorder level based on Warehouse,mức đèn đỏ mua vật tư (phải bổ xung hoặc đặt mua thêm),
-Will also apply for variants unless overrridden,Cũng sẽ được áp dụng cho các biến thể trừ phần bị ghi đèn,
+Will also apply for variants unless overridden,Cũng sẽ được áp dụng cho các biến thể trừ phần bị ghi đèn,
Units of Measure,Đơn vị đo lường,
Will also apply for variants,Cũng sẽ được áp dụng cho các biến thể,
Serial Nos and Batches,Số hàng loạt và hàng loạt,
diff --git a/erpnext/translations/zh-TW.csv b/erpnext/translations/zh-TW.csv
index 699d802..0f76e97 100644
--- a/erpnext/translations/zh-TW.csv
+++ b/erpnext/translations/zh-TW.csv
@@ -2964,7 +2964,7 @@
Code {0} already exist,代碼{0}已經存在
Sales orders are not available for production,銷售訂單不可用於生產
Fixed Asset Depreciation Settings,固定資產折舊設置
-Will also apply for variants unless overrridden,同時將申請變種,除非overrridden,
+Will also apply for variants unless overridden,同時將申請變種,除非overridden,
Advances,進展
Manufacture against Material Request,對製造材料要求
Assessment Group: ,評估組:
diff --git a/erpnext/translations/zh.csv b/erpnext/translations/zh.csv
index 70ec81a..08f8d33 100644
--- a/erpnext/translations/zh.csv
+++ b/erpnext/translations/zh.csv
@@ -1153,7 +1153,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",在多层程序的情况下,客户将根据其花费自动分配到相关层,
Inactive,非活动的,
Incentives,激励政策,
-Include Default Book Entries,包括默认工作簿条目,
+Include Default FB Entries,包括默认工作簿条目,
Include Exploded Items,包含爆炸物料,
Include POS Transactions,包括POS交易,
Include UOM,包括基本计量单位,
@@ -7052,7 +7052,7 @@
Warranty Period (in days),保修期限(天数),
Auto re-order,自动重订货,
Reorder level based on Warehouse,根据仓库订货点水平,
-Will also apply for variants unless overrridden,除非手动指定,否则会同时应用于变体,
+Will also apply for variants unless overridden,除非手动指定,否则会同时应用于变体,
Units of Measure,计量单位,
Will also apply for variants,会同时应用于变体,
Serial Nos and Batches,序列号和批号,
@@ -8743,3 +8743,4 @@
Make a call,打个电话,
Approve,同意,
Reject,拒绝,
+Stock,库存,
diff --git a/erpnext/translations/zh_tw.csv b/erpnext/translations/zh_tw.csv
index 75157f0..dd683c5 100644
--- a/erpnext/translations/zh_tw.csv
+++ b/erpnext/translations/zh_tw.csv
@@ -1166,7 +1166,7 @@
"In the case of multi-tier program, Customers will be auto assigned to the concerned tier as per their spent",在多層程序的情況下,客戶將根據其花費自動分配到相關層,
Inactive,待用,
Incentives,獎勵,
-Include Default Book Entries,包括默認工作簿條目,
+Include Default FB Entries,包括默認工作簿條目,
Include Exploded Items,包含爆炸物品,
Include UOM,包括UOM,
Included in Gross Profit,包含在毛利潤中,
@@ -7485,7 +7485,7 @@
Warranty Period (in days),保修期限(天數),
Auto re-order,自動重新排序,
Reorder level based on Warehouse,根據倉庫訂貨點水平,
-Will also apply for variants unless overrridden,同時將申請變種,除非overrridden,
+Will also apply for variants unless overridden,同時將申請變種,除非overridden,
Units of Measure,測量的單位,
Will also apply for variants,同時將申請變種,
Serial Nos and Batches,序列號和批號,
diff --git a/erpnext/utilities/bulk_transaction.py b/erpnext/utilities/bulk_transaction.py
index 5e57b31..fcee265 100644
--- a/erpnext/utilities/bulk_transaction.py
+++ b/erpnext/utilities/bulk_transaction.py
@@ -192,9 +192,7 @@
record = 0
for d in log_doc.get("logger_data"):
if d.transaction_name == doc_name and d.transaction_status == "Failed":
- d.retried = 1
+ frappe.db.set_value("Bulk Transaction Log Detail", d.name, "retried", 1)
record = record + 1
- log_doc.save()
-
return record