Merge pull request #37828 from GursheenK/payments-irrespective-of-party-types
fix: payments irrespective of party types
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/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_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/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/report/accounts_payable/accounts_payable.js b/erpnext/accounts/report/accounts_payable/accounts_payable.js
index 9c73cbb..eff705d 100644
--- a/erpnext/accounts/report/accounts_payable/accounts_payable.js
+++ b/erpnext/accounts/report/accounts_payable/accounts_payable.js
@@ -143,7 +143,13 @@
"fieldname": "show_future_payments",
"label": __("Show Future Payments"),
"fieldtype": "Check",
+ },
+ {
+ "fieldname": "ignore_accounts",
+ "label": __("Group by Voucher"),
+ "fieldtype": "Check",
}
+
],
"formatter": function(value, row, column, data, default_formatter) {
@@ -175,4 +181,4 @@
});
});
return options;
-}
\ No newline at end of file
+}
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js
index 1073be0..786aad6 100644
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.js
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.js
@@ -172,7 +172,13 @@
"fieldname": "show_remarks",
"label": __("Show Remarks"),
"fieldtype": "Check",
+ },
+ {
+ "fieldname": "ignore_accounts",
+ "label": __("Group by Voucher"),
+ "fieldtype": "Check",
}
+
],
"formatter": function(value, row, column, data, default_formatter) {
@@ -205,4 +211,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
index 4cc0f0c..ef28f60 100755
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
@@ -114,7 +114,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,
@@ -181,7 +186,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
@@ -190,13 +198,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
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/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 aef0c38..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)
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/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/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 6efe631..38c1e82 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -2269,6 +2269,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/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/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 1850d1e..6b12a29 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)
@@ -831,7 +841,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 +1647,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])
@@ -1780,3 +1790,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 d414988..e9c6ee3 100644
--- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
@@ -1042,13 +1042,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):
@@ -1359,6 +1360,93 @@
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):
"""
@@ -1379,6 +1467,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/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/patches.txt b/erpnext/patches.txt
index 78d2c2c..d394db6 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -345,5 +345,9 @@
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
# 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/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/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/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/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/repost_item_valuation/test_repost_item_valuation.py b/erpnext/stock/doctype/repost_item_valuation/test_repost_item_valuation.py
index 623e8fa..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
@@ -196,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
@@ -373,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_reconciliation/stock_reconciliation.py b/erpnext/stock/doctype/stock_reconciliation/stock_reconciliation.py
index 98b4ffd..323ad4f 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:
@@ -689,56 +718,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 +808,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..e9381d4 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)
@@ -754,27 +768,22 @@
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 +1470,7 @@
currency=None,
company=None,
raise_error_if_no_rate=True,
+ batch_no=None,
serial_and_batch_bundle=None,
):
@@ -1469,6 +1479,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 +1592,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 +1620,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 +1726,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 +1809,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
diff --git a/erpnext/translations/af.csv b/erpnext/translations/af.csv
index f457314..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,
diff --git a/erpnext/translations/am.csv b/erpnext/translations/am.csv
index 0453d5d..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 አካት,
diff --git a/erpnext/translations/ar.csv b/erpnext/translations/ar.csv
index 67b409e..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,
diff --git a/erpnext/translations/bg.csv b/erpnext/translations/bg.csv
index 787f81e..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,
diff --git a/erpnext/translations/bn.csv b/erpnext/translations/bn.csv
index 69fd08c..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 অন্তর্ভুক্ত করুন,
diff --git a/erpnext/translations/bs.csv b/erpnext/translations/bs.csv
index ef680a3..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,
diff --git a/erpnext/translations/ca.csv b/erpnext/translations/ca.csv
index fa545a4..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,
diff --git a/erpnext/translations/cs.csv b/erpnext/translations/cs.csv
index 7fb1679..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,
diff --git a/erpnext/translations/da.csv b/erpnext/translations/da.csv
index 4eb3960..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,
diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv
index c8fc2cf..61e301e 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,
@@ -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 21fb435..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,
diff --git a/erpnext/translations/es.csv b/erpnext/translations/es.csv
index 2abe418..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,
diff --git a/erpnext/translations/et.csv b/erpnext/translations/et.csv
index a4a8736..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,
diff --git a/erpnext/translations/fa.csv b/erpnext/translations/fa.csv
index bd40c8b..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,
diff --git a/erpnext/translations/fi.csv b/erpnext/translations/fi.csv
index 33cf157..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,
diff --git a/erpnext/translations/fr.csv b/erpnext/translations/fr.csv
index d15af74..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,
diff --git a/erpnext/translations/gu.csv b/erpnext/translations/gu.csv
index 06a3cc6..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,યુએમએમ શામેલ કરો,
diff --git a/erpnext/translations/he.csv b/erpnext/translations/he.csv
index d5fcab6..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,
diff --git a/erpnext/translations/hi.csv b/erpnext/translations/hi.csv
index a5caa66..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,यूओएम शामिल करें,
diff --git a/erpnext/translations/hr.csv b/erpnext/translations/hr.csv
index 2834602..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,
diff --git a/erpnext/translations/hu.csv b/erpnext/translations/hu.csv
index a262c8a..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,
diff --git a/erpnext/translations/id.csv b/erpnext/translations/id.csv
index c4e50fd..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,
diff --git a/erpnext/translations/is.csv b/erpnext/translations/is.csv
index 50c06ec..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,
diff --git a/erpnext/translations/it.csv b/erpnext/translations/it.csv
index 3760895..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,
diff --git a/erpnext/translations/ja.csv b/erpnext/translations/ja.csv
index 888ec80..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を含める,
diff --git a/erpnext/translations/km.csv b/erpnext/translations/km.csv
index d2003c0..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,
diff --git a/erpnext/translations/kn.csv b/erpnext/translations/kn.csv
index 7206671..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 ಸೇರಿಸಿ,
diff --git a/erpnext/translations/ko.csv b/erpnext/translations/ko.csv
index 9911925..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 포함,
diff --git a/erpnext/translations/ku.csv b/erpnext/translations/ku.csv
index 8fec059..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,
diff --git a/erpnext/translations/lo.csv b/erpnext/translations/lo.csv
index 0831788..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,
diff --git a/erpnext/translations/lt.csv b/erpnext/translations/lt.csv
index 8215275..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,
diff --git a/erpnext/translations/lv.csv b/erpnext/translations/lv.csv
index 8c4526c..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,
diff --git a/erpnext/translations/mk.csv b/erpnext/translations/mk.csv
index a622524..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,
diff --git a/erpnext/translations/ml.csv b/erpnext/translations/ml.csv
index 777d5c6..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 ഉൾപ്പെടുത്തുക,
diff --git a/erpnext/translations/mr.csv b/erpnext/translations/mr.csv
index 624f1ab..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,यूओएम समाविष्ट करा,
diff --git a/erpnext/translations/ms.csv b/erpnext/translations/ms.csv
index 75e150a..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,
diff --git a/erpnext/translations/my.csv b/erpnext/translations/my.csv
index 36cd874..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,
diff --git a/erpnext/translations/nl.csv b/erpnext/translations/nl.csv
index 5859833..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,
diff --git a/erpnext/translations/no.csv b/erpnext/translations/no.csv
index a3236ac..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,
diff --git a/erpnext/translations/pl.csv b/erpnext/translations/pl.csv
index df41e39..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,
diff --git a/erpnext/translations/ps.csv b/erpnext/translations/ps.csv
index 5a0b2a5..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 شامل کړئ,
diff --git a/erpnext/translations/pt-BR.csv b/erpnext/translations/pt-BR.csv
index bc5b616..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,
diff --git a/erpnext/translations/pt.csv b/erpnext/translations/pt.csv
index e6846c6..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,
diff --git a/erpnext/translations/ro.csv b/erpnext/translations/ro.csv
index ac7e598..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,
diff --git a/erpnext/translations/ru.csv b/erpnext/translations/ru.csv
index 52c2998..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,
diff --git a/erpnext/translations/rw.csv b/erpnext/translations/rw.csv
index f035d57..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,
diff --git a/erpnext/translations/si.csv b/erpnext/translations/si.csv
index 4047263..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 ඇතුළත් කරන්න,
diff --git a/erpnext/translations/sk.csv b/erpnext/translations/sk.csv
index 98e1663..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,
diff --git a/erpnext/translations/sl.csv b/erpnext/translations/sl.csv
index 5380714..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,
diff --git a/erpnext/translations/sq.csv b/erpnext/translations/sq.csv
index 2a893d2..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,
diff --git a/erpnext/translations/sr.csv b/erpnext/translations/sr.csv
index c1e5eb0..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,Укључите УОМ,
diff --git a/erpnext/translations/sv.csv b/erpnext/translations/sv.csv
index 8b4ab06..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,
diff --git a/erpnext/translations/sw.csv b/erpnext/translations/sw.csv
index fa2287c..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,
diff --git a/erpnext/translations/ta.csv b/erpnext/translations/ta.csv
index 6eaae34..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 ஐ சேர்க்கவும்,
diff --git a/erpnext/translations/te.csv b/erpnext/translations/te.csv
index d3f739a..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 ని చేర్చండి,
diff --git a/erpnext/translations/th.csv b/erpnext/translations/th.csv
index a065595..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,
diff --git a/erpnext/translations/tr.csv b/erpnext/translations/tr.csv
index 9e916f0..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,
diff --git a/erpnext/translations/uk.csv b/erpnext/translations/uk.csv
index 8d1fb04..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,
diff --git a/erpnext/translations/ur.csv b/erpnext/translations/ur.csv
index 649c1c7..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 شامل کریں,
diff --git a/erpnext/translations/uz.csv b/erpnext/translations/uz.csv
index 5ca51cc..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,
diff --git a/erpnext/translations/vi.csv b/erpnext/translations/vi.csv
index 7a005fa..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,
diff --git a/erpnext/translations/zh.csv b/erpnext/translations/zh.csv
index 1d8a261..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,包括基本计量单位,
@@ -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 8ecbaaa..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,包含在毛利潤中,
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