Merge pull request #31943 from nabinhait/asset-repair
fix: gl entries for asset repair
diff --git a/erpnext/accounts/doctype/gl_entry/gl_entry.py b/erpnext/accounts/doctype/gl_entry/gl_entry.py
index 1987c83..7227b95 100644
--- a/erpnext/accounts/doctype/gl_entry/gl_entry.py
+++ b/erpnext/accounts/doctype/gl_entry/gl_entry.py
@@ -366,7 +366,7 @@
if against_voucher_type in ["Sales Invoice", "Purchase Invoice", "Fees"]:
ref_doc = frappe.get_doc(against_voucher_type, against_voucher)
- # Didn't use db_set for optimisation purpose
+ # Didn't use db_set for optimization purpose
ref_doc.outstanding_amount = bal
frappe.db.set_value(against_voucher_type, against_voucher, "outstanding_amount", bal)
diff --git a/erpnext/accounts/doctype/payment_entry/payment_entry.py b/erpnext/accounts/doctype/payment_entry/payment_entry.py
index 48edda9..4618d08 100644
--- a/erpnext/accounts/doctype/payment_entry/payment_entry.py
+++ b/erpnext/accounts/doctype/payment_entry/payment_entry.py
@@ -181,7 +181,11 @@
frappe.throw(_("Party is mandatory"))
_party_name = "title" if self.party_type == "Shareholder" else self.party_type.lower() + "_name"
- self.party_name = frappe.db.get_value(self.party_type, self.party, _party_name)
+
+ if frappe.db.has_column(self.party_type, _party_name):
+ self.party_name = frappe.db.get_value(self.party_type, self.party, _party_name)
+ else:
+ self.party_name = frappe.db.get_value(self.party_type, self.party, "name")
if self.party:
if not self.party_balance:
@@ -295,6 +299,9 @@
def validate_reference_documents(self):
valid_reference_doctypes = self.get_valid_reference_doctypes()
+ if not valid_reference_doctypes:
+ return
+
for d in self.get("references"):
if not d.allocated_amount:
continue
@@ -362,7 +369,7 @@
if not d.allocated_amount:
continue
- if d.reference_doctype in ("Sales Invoice", "Purchase Invoice", "Fees"):
+ if d.reference_doctype in ("Sales Invoice", "Purchase Invoice"):
outstanding_amount, is_return = frappe.get_cached_value(
d.reference_doctype, d.reference_name, ["outstanding_amount", "is_return"]
)
@@ -1201,7 +1208,7 @@
party_account_currency = get_account_currency(args.get("party_account"))
company_currency = frappe.get_cached_value("Company", args.get("company"), "default_currency")
- # Get positive outstanding sales /purchase invoices/ Fees
+ # Get positive outstanding sales /purchase invoices
condition = ""
if args.get("voucher_type") and args.get("voucher_no"):
condition = " and voucher_type={0} and voucher_no={1}".format(
@@ -1597,10 +1604,11 @@
elif reference_doctype != "Journal Entry":
if not total_amount:
if party_account_currency == company_currency:
- total_amount = ref_doc.base_grand_total
+ # for handling cases that don't have multi-currency (base field)
+ total_amount = ref_doc.get("grand_total") or ref_doc.get("base_grand_total")
exchange_rate = 1
else:
- total_amount = ref_doc.grand_total
+ total_amount = ref_doc.get("grand_total")
if not exchange_rate:
# Get the exchange rate from the original ref doc
# or get it based on the posting date of the ref doc.
@@ -1611,7 +1619,7 @@
if reference_doctype in ("Sales Invoice", "Purchase Invoice"):
outstanding_amount = ref_doc.get("outstanding_amount")
else:
- outstanding_amount = flt(total_amount) - flt(ref_doc.advance_paid)
+ outstanding_amount = flt(total_amount) - flt(ref_doc.get("advance_paid"))
else:
# Get the exchange rate based on the posting date of the ref doc.
@@ -1629,16 +1637,23 @@
@frappe.whitelist()
-def get_payment_entry(dt, dn, party_amount=None, bank_account=None, bank_amount=None):
+def get_payment_entry(
+ dt, dn, party_amount=None, bank_account=None, bank_amount=None, party_type=None, payment_type=None
+):
reference_doc = None
doc = frappe.get_doc(dt, dn)
if dt in ("Sales Order", "Purchase Order") and flt(doc.per_billed, 2) > 0:
frappe.throw(_("Can only make payment against unbilled {0}").format(dt))
- party_type = set_party_type(dt)
+ if not party_type:
+ party_type = set_party_type(dt)
+
party_account = set_party_account(dt, dn, doc, party_type)
party_account_currency = set_party_account_currency(dt, party_account, doc)
- payment_type = set_payment_type(dt, doc)
+
+ if not payment_type:
+ payment_type = set_payment_type(dt, doc)
+
grand_total, outstanding_amount = set_grand_total_and_outstanding_amount(
party_amount, dt, party_account_currency, doc
)
@@ -1788,8 +1803,6 @@
party_account = get_party_account_based_on_invoice_discounting(dn) or doc.debit_to
elif dt == "Purchase Invoice":
party_account = doc.credit_to
- elif dt == "Fees":
- party_account = doc.receivable_account
else:
party_account = get_party_account(party_type, doc.get(party_type.lower()), doc.company)
return party_account
@@ -1805,8 +1818,7 @@
def set_payment_type(dt, doc):
if (
- dt == "Sales Order"
- or (dt in ("Sales Invoice", "Fees", "Dunning") and doc.outstanding_amount > 0)
+ dt == "Sales Order" or (dt in ("Sales Invoice", "Dunning") and doc.outstanding_amount > 0)
) or (dt == "Purchase Invoice" and doc.outstanding_amount < 0):
payment_type = "Receive"
else:
@@ -1824,18 +1836,15 @@
else:
grand_total = doc.rounded_total or doc.grand_total
outstanding_amount = doc.outstanding_amount
- elif dt == "Fees":
- grand_total = doc.grand_total
- outstanding_amount = doc.outstanding_amount
elif dt == "Dunning":
grand_total = doc.grand_total
outstanding_amount = doc.grand_total
else:
if party_account_currency == doc.company_currency:
- grand_total = flt(doc.get("base_rounded_total") or doc.base_grand_total)
+ grand_total = flt(doc.get("base_rounded_total") or doc.get("base_grand_total"))
else:
- grand_total = flt(doc.get("rounded_total") or doc.grand_total)
- outstanding_amount = grand_total - flt(doc.advance_paid)
+ grand_total = flt(doc.get("rounded_total") or doc.get("grand_total"))
+ outstanding_amount = doc.get("outstanding_amount") or (grand_total - flt(doc.advance_paid))
return grand_total, outstanding_amount
diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js
index 98f3420..1d596c1 100644
--- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js
+++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.js
@@ -36,6 +36,15 @@
});
set_html_data(frm);
+
+ if (frm.doc.docstatus == 1) {
+ if (!frm.doc.posting_date) {
+ frm.set_value("posting_date", frappe.datetime.nowdate());
+ }
+ if (!frm.doc.posting_time) {
+ frm.set_value("posting_time", frappe.datetime.now_time());
+ }
+ }
},
refresh: function(frm) {
diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.json b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.json
index d6e35c6..9d15e6c 100644
--- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.json
+++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.json
@@ -11,6 +11,7 @@
"period_end_date",
"column_break_3",
"posting_date",
+ "posting_time",
"pos_opening_entry",
"status",
"section_break_5",
@@ -51,7 +52,6 @@
"fieldtype": "Datetime",
"in_list_view": 1,
"label": "Period End Date",
- "read_only": 1,
"reqd": 1
},
{
@@ -219,6 +219,13 @@
"fieldtype": "Small Text",
"label": "Error",
"read_only": 1
+ },
+ {
+ "fieldname": "posting_time",
+ "fieldtype": "Time",
+ "label": "Posting Time",
+ "no_copy": 1,
+ "reqd": 1
}
],
"is_submittable": 1,
@@ -228,10 +235,11 @@
"link_fieldname": "pos_closing_entry"
}
],
- "modified": "2021-10-20 16:19:25.340565",
+ "modified": "2022-08-01 11:37:14.991228",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Closing Entry",
+ "naming_rule": "Expression (old style)",
"owner": "Administrator",
"permissions": [
{
@@ -278,5 +286,6 @@
],
"sort_field": "modified",
"sort_order": "DESC",
+ "states": [],
"track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py
index 49aab0d..655c4ec 100644
--- a/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py
+++ b/erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py
@@ -15,6 +15,9 @@
class POSClosingEntry(StatusUpdater):
def validate(self):
+ self.posting_date = self.posting_date or frappe.utils.nowdate()
+ self.posting_time = self.posting_time or frappe.utils.nowtime()
+
if frappe.db.get_value("POS Opening Entry", self.pos_opening_entry, "status") != "Open":
frappe.throw(_("Selected POS Opening Entry should be open."), title=_("Invalid Opening Entry"))
diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.json b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.json
index d762087..a059455 100644
--- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.json
+++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.json
@@ -6,6 +6,7 @@
"engine": "InnoDB",
"field_order": [
"posting_date",
+ "posting_time",
"merge_invoices_based_on",
"column_break_3",
"pos_closing_entry",
@@ -105,12 +106,19 @@
"label": "Customer Group",
"mandatory_depends_on": "eval:doc.merge_invoices_based_on == 'Customer Group'",
"options": "Customer Group"
+ },
+ {
+ "fieldname": "posting_time",
+ "fieldtype": "Time",
+ "label": "Posting Time",
+ "no_copy": 1,
+ "reqd": 1
}
],
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2021-09-14 11:17:19.001142",
+ "modified": "2022-08-01 11:36:42.456429",
"modified_by": "Administrator",
"module": "Accounts",
"name": "POS Invoice Merge Log",
@@ -173,5 +181,6 @@
],
"sort_field": "modified",
"sort_order": "DESC",
+ "states": [],
"track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
index 5003a1d..81a234a 100644
--- a/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
+++ b/erpnext/accounts/doctype/pos_invoice_merge_log/pos_invoice_merge_log.py
@@ -9,7 +9,7 @@
from frappe.core.page.background_jobs.background_jobs import get_info
from frappe.model.document import Document
from frappe.model.mapper import map_child_doc, map_doc
-from frappe.utils import cint, flt, getdate, nowdate
+from frappe.utils import cint, flt, get_time, getdate, nowdate, nowtime
from frappe.utils.background_jobs import enqueue
from frappe.utils.scheduler import is_scheduler_inactive
@@ -99,6 +99,7 @@
sales_invoice.is_consolidated = 1
sales_invoice.set_posting_time = 1
sales_invoice.posting_date = getdate(self.posting_date)
+ sales_invoice.posting_time = get_time(self.posting_time)
sales_invoice.save()
sales_invoice.submit()
@@ -115,6 +116,7 @@
credit_note.is_consolidated = 1
credit_note.set_posting_time = 1
credit_note.posting_date = getdate(self.posting_date)
+ credit_note.posting_time = get_time(self.posting_time)
# TODO: return could be against multiple sales invoice which could also have been consolidated?
# credit_note.return_against = self.consolidated_invoice
credit_note.save()
@@ -402,6 +404,9 @@
merge_log.posting_date = (
getdate(closing_entry.get("posting_date")) if closing_entry else nowdate()
)
+ merge_log.posting_time = (
+ get_time(closing_entry.get("posting_time")) if closing_entry else nowtime()
+ )
merge_log.customer = customer
merge_log.pos_closing_entry = closing_entry.get("name") if closing_entry else None
diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
index de3927e..fea81e9 100644
--- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py
@@ -575,7 +575,6 @@
self.make_supplier_gl_entry(gl_entries)
self.make_item_gl_entries(gl_entries)
- self.make_discount_gl_entries(gl_entries)
if self.check_asset_cwip_enabled():
self.get_asset_gl_entry(gl_entries)
@@ -807,7 +806,7 @@
)
if not item.is_fixed_asset:
- dummy, amount = self.get_amount_and_base_amount(item, enable_discount_accounting)
+ dummy, amount = self.get_amount_and_base_amount(item, None)
else:
amount = flt(item.base_net_amount + item.item_tax_amount, item.precision("base_net_amount"))
@@ -1165,7 +1164,7 @@
)
for tax in self.get("taxes"):
- amount, base_amount = self.get_tax_amounts(tax, enable_discount_accounting)
+ amount, base_amount = self.get_tax_amounts(tax, None)
if tax.category in ("Total", "Valuation and Total") and flt(base_amount):
account_currency = get_account_currency(tax.account_head)
@@ -1791,4 +1790,6 @@
target_doc,
)
+ doc.set_onload("ignore_price_list", True)
+
return doc
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index e55d3a7..0a4f25b 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -338,59 +338,6 @@
self.assertEqual(discrepancy_caused_by_exchange_rate_diff, amount)
- @change_settings("Buying Settings", {"enable_discount_accounting": 1})
- def test_purchase_invoice_with_discount_accounting_enabled(self):
-
- discount_account = create_account(
- account_name="Discount Account",
- parent_account="Indirect Expenses - _TC",
- company="_Test Company",
- )
- pi = make_purchase_invoice(discount_account=discount_account, rate=45)
-
- expected_gle = [
- ["_Test Account Cost for Goods Sold - _TC", 250.0, 0.0, nowdate()],
- ["Creditors - _TC", 0.0, 225.0, nowdate()],
- ["Discount Account - _TC", 0.0, 25.0, nowdate()],
- ]
-
- check_gl_entries(self, pi.name, expected_gle, nowdate())
-
- @change_settings("Buying Settings", {"enable_discount_accounting": 1})
- def test_additional_discount_for_purchase_invoice_with_discount_accounting_enabled(self):
-
- additional_discount_account = create_account(
- account_name="Discount Account",
- parent_account="Indirect Expenses - _TC",
- company="_Test Company",
- )
-
- pi = make_purchase_invoice(do_not_save=1, parent_cost_center="Main - _TC")
- pi.apply_discount_on = "Grand Total"
- pi.additional_discount_account = additional_discount_account
- pi.additional_discount_percentage = 10
- pi.disable_rounded_total = 1
- pi.append(
- "taxes",
- {
- "charge_type": "On Net Total",
- "account_head": "_Test Account VAT - _TC",
- "cost_center": "Main - _TC",
- "description": "Test",
- "rate": 10,
- },
- )
- pi.submit()
-
- expected_gle = [
- ["_Test Account Cost for Goods Sold - _TC", 250.0, 0.0, nowdate()],
- ["_Test Account VAT - _TC", 25.0, 0.0, nowdate()],
- ["Creditors - _TC", 0.0, 247.5, nowdate()],
- ["Discount Account - _TC", 0.0, 27.5, nowdate()],
- ]
-
- check_gl_entries(self, pi.name, expected_gle, nowdate())
-
def test_purchase_invoice_change_naming_series(self):
pi = frappe.copy_doc(test_records[1])
pi.insert()
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
index adcbb83..73ec051 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.js
@@ -479,9 +479,13 @@
is_cash_or_non_trade_discount() {
this.frm.set_df_property("additional_discount_account", "hidden", 1 - this.frm.doc.is_cash_or_non_trade_discount);
+ this.frm.set_df_property("additional_discount_account", "reqd", this.frm.doc.is_cash_or_non_trade_discount);
+
if (!this.frm.doc.is_cash_or_non_trade_discount) {
this.frm.set_value("additional_discount_account", "");
}
+
+ this.calculate_taxes_and_totals();
}
};
diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
index 19a234d..4008863 100644
--- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py
@@ -1033,22 +1033,6 @@
)
)
- if self.apply_discount_on == "Grand Total" and self.get("is_cash_or_discount_account"):
- gl_entries.append(
- self.get_gl_dict(
- {
- "account": self.additional_discount_account,
- "against": self.debit_to,
- "debit": self.base_discount_amount,
- "debit_in_account_currency": self.discount_amount,
- "cost_center": self.cost_center,
- "project": self.project,
- },
- self.currency,
- item=self,
- )
- )
-
def make_tax_gl_entries(self, gl_entries):
enable_discount_accounting = cint(
frappe.db.get_single_value("Selling Settings", "enable_discount_accounting")
diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
index a519d8b..6004e2b 100644
--- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
+++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py
@@ -318,7 +318,6 @@
"is_cancelled": 0,
"party_type": party_type,
"party": ["in", parties],
- "against_voucher": ["is", "not set"],
}
if company:
diff --git a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
index e937edb..63242e8 100755
--- a/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/accounts_receivable.py
@@ -178,6 +178,11 @@
key = (ple.against_voucher_type, ple.against_voucher_no, 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.voucher_type, ple.voucher_no, ple.party))
+
return row
def update_voucher_balance(self, ple):
@@ -187,7 +192,11 @@
if not row:
return
- amount = ple.amount
+ # amount in "Party Currency", if its supplied. If not, amount in company currency
+ if self.filters.get(scrub(self.party_type)):
+ amount = ple.amount_in_account_currency
+ else:
+ amount = ple.amount
amount_in_account_currency = ple.amount_in_account_currency
# update voucher
@@ -685,7 +694,7 @@
ple.party,
ple.posting_date,
ple.due_date,
- ple.account_currency.as_("currency"),
+ ple.account_currency,
ple.amount,
ple.amount_in_account_currency,
)
diff --git a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py
index edddbbc..bac8bee 100644
--- a/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py
+++ b/erpnext/accounts/report/accounts_receivable/test_accounts_receivable.py
@@ -1,19 +1,27 @@
import unittest
import frappe
+from frappe.tests.utils import FrappeTestCase
from frappe.utils import add_days, getdate, today
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice
from erpnext.accounts.report.accounts_receivable.accounts_receivable import execute
+from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
-class TestAccountsReceivable(unittest.TestCase):
- def test_accounts_receivable(self):
+class TestAccountsReceivable(FrappeTestCase):
+ def setUp(self):
frappe.db.sql("delete from `tabSales Invoice` where company='_Test Company 2'")
+ frappe.db.sql("delete from `tabSales Order` where company='_Test Company 2'")
+ frappe.db.sql("delete from `tabPayment Entry` where company='_Test Company 2'")
frappe.db.sql("delete from `tabGL Entry` where company='_Test Company 2'")
frappe.db.sql("delete from `tabPayment Ledger Entry` where company='_Test Company 2'")
+ def tearDown(self):
+ frappe.db.rollback()
+
+ def test_accounts_receivable(self):
filters = {
"company": "_Test Company 2",
"based_on_payment_terms": 1,
@@ -66,6 +74,50 @@
],
)
+ def test_payment_againt_po_in_receivable_report(self):
+ """
+ Payments made against Purchase Order will show up as outstanding amount
+ """
+
+ so = make_sales_order(
+ company="_Test Company 2",
+ customer="_Test Customer 2",
+ warehouse="Finished Goods - _TC2",
+ currency="EUR",
+ debit_to="Debtors - _TC2",
+ income_account="Sales - _TC2",
+ expense_account="Cost of Goods Sold - _TC2",
+ cost_center="Main - _TC2",
+ )
+
+ pe = get_payment_entry(so.doctype, so.name)
+ pe = pe.save().submit()
+
+ filters = {
+ "company": "_Test Company 2",
+ "based_on_payment_terms": 0,
+ "report_date": today(),
+ "range1": 30,
+ "range2": 60,
+ "range3": 90,
+ "range4": 120,
+ }
+
+ report = execute(filters)
+
+ expected_data_after_payment = [0, 1000, 0, -1000]
+
+ row = report[1][0]
+ self.assertEqual(
+ expected_data_after_payment,
+ [
+ row.invoiced,
+ row.paid,
+ row.credit_note,
+ row.outstanding,
+ ],
+ )
+
def make_sales_invoice():
frappe.set_user("Administrator")
diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py
index 526ea9d..54af225 100644
--- a/erpnext/accounts/report/gross_profit/gross_profit.py
+++ b/erpnext/accounts/report/gross_profit/gross_profit.py
@@ -616,7 +616,7 @@
previous_stock_value = len(my_sle) > i + 1 and flt(my_sle[i + 1].stock_value) or 0.0
if previous_stock_value:
- return (previous_stock_value - flt(sle.stock_value)) * flt(row.qty) / abs(flt(sle.qty))
+ return abs(previous_stock_value - flt(sle.stock_value)) * flt(row.qty) / abs(flt(sle.qty))
else:
return flt(row.qty) * self.get_average_buying_rate(row, item_code)
else:
diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
index 4e29ee5..31a4837 100644
--- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
+++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.js
@@ -15,9 +15,12 @@
frm.fields_dict["suppliers"].grid.get_field("contact").get_query = function(doc, cdt, cdn) {
let d = locals[cdt][cdn];
return {
- query: "erpnext.buying.doctype.request_for_quotation.request_for_quotation.get_supplier_contacts",
- filters: {'supplier': d.supplier}
- }
+ query: "frappe.contacts.doctype.contact.contact.contact_query",
+ filters: {
+ link_doctype: "Supplier",
+ link_name: d.supplier || ""
+ }
+ };
}
},
diff --git a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
index 3ef57bb..ee28eb6 100644
--- a/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
+++ b/erpnext/buying/doctype/request_for_quotation/request_for_quotation.py
@@ -287,18 +287,6 @@
@frappe.whitelist()
-@frappe.validate_and_sanitize_search_inputs
-def get_supplier_contacts(doctype, txt, searchfield, start, page_len, filters):
- return frappe.db.sql(
- """select `tabContact`.name from `tabContact`, `tabDynamic Link`
- where `tabDynamic Link`.link_doctype = 'Supplier' and (`tabDynamic Link`.link_name=%(name)s
- and `tabDynamic Link`.link_name like %(txt)s) and `tabContact`.name = `tabDynamic Link`.parent
- limit %(page_len)s offset %(start)s""",
- {"start": start, "page_len": page_len, "txt": "%%%s%%" % txt, "name": filters.get("supplier")},
- )
-
-
-@frappe.whitelist()
def make_supplier_quotation_from_rfq(source_name, target_doc=None, for_supplier=None):
def postprocess(source, target_doc):
if for_supplier:
diff --git a/erpnext/buying/report/subcontracted_item_to_be_received/test_subcontracted_item_to_be_received.py b/erpnext/buying/report/subcontracted_item_to_be_received/test_subcontracted_item_to_be_received.py
index c772c1a..d13d970 100644
--- a/erpnext/buying/report/subcontracted_item_to_be_received/test_subcontracted_item_to_be_received.py
+++ b/erpnext/buying/report/subcontracted_item_to_be_received/test_subcontracted_item_to_be_received.py
@@ -4,6 +4,8 @@
# Decompiled by https://python-decompiler.com
+import copy
+
import frappe
from frappe.tests.utils import FrappeTestCase
@@ -11,10 +13,12 @@
execute,
)
from erpnext.controllers.tests.test_subcontracting_controller import (
+ get_rm_items,
get_subcontracting_order,
make_service_item,
+ make_stock_in_entry,
+ make_stock_transfer_entry,
)
-from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
from erpnext.subcontracting.doctype.subcontracting_order.subcontracting_order import (
make_subcontracting_receipt,
)
@@ -36,15 +40,18 @@
sco = get_subcontracting_order(
service_items=service_items, supplier_warehouse="_Test Warehouse 1 - _TC"
)
- make_stock_entry(
- item_code="_Test Item", target="_Test Warehouse 1 - _TC", qty=100, basic_rate=100
+ rm_items = get_rm_items(sco.supplied_items)
+ itemwise_details = make_stock_in_entry(rm_items=rm_items)
+
+ for item in rm_items:
+ item["sco_rm_detail"] = sco.items[0].name
+
+ make_stock_transfer_entry(
+ sco_no=sco.name,
+ rm_items=rm_items,
+ itemwise_details=copy.deepcopy(itemwise_details),
)
- make_stock_entry(
- item_code="_Test Item Home Desktop 100",
- target="_Test Warehouse 1 - _TC",
- qty=100,
- basic_rate=100,
- )
+
make_subcontracting_receipt_against_sco(sco.name)
sco.reload()
col, data = execute(
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 70d2bc6..e689d56 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -1109,17 +1109,17 @@
frappe.db.get_single_value("Selling Settings", "enable_discount_accounting")
)
+ if self.doctype == "Purchase Invoice":
+ dr_or_cr = "credit"
+ rev_dr_cr = "debit"
+ supplier_or_customer = self.supplier
+
+ else:
+ dr_or_cr = "debit"
+ rev_dr_cr = "credit"
+ supplier_or_customer = self.customer
+
if enable_discount_accounting:
- if self.doctype == "Purchase Invoice":
- dr_or_cr = "credit"
- rev_dr_cr = "debit"
- supplier_or_customer = self.supplier
-
- else:
- dr_or_cr = "debit"
- rev_dr_cr = "credit"
- supplier_or_customer = self.customer
-
for item in self.get("items"):
if item.get("discount_amount") and item.get("discount_account"):
discount_amount = item.discount_amount * item.qty
@@ -1173,18 +1173,22 @@
)
)
- if self.get("discount_amount") and self.get("additional_discount_account"):
- gl_entries.append(
- self.get_gl_dict(
- {
- "account": self.additional_discount_account,
- "against": supplier_or_customer,
- dr_or_cr: self.discount_amount,
- "cost_center": self.cost_center,
- },
- item=self,
- )
+ if (
+ (enable_discount_accounting or self.get("is_cash_or_non_trade_discount"))
+ and self.get("additional_discount_account")
+ and self.get("discount_amount")
+ ):
+ gl_entries.append(
+ self.get_gl_dict(
+ {
+ "account": self.additional_discount_account,
+ "against": supplier_or_customer,
+ dr_or_cr: self.discount_amount,
+ "cost_center": self.cost_center,
+ },
+ item=self,
)
+ )
def validate_multiple_billing(self, ref_dt, item_ref_dn, based_on, parentfield):
from erpnext.controllers.status_updater import get_allowance_for
diff --git a/erpnext/controllers/stock_controller.py b/erpnext/controllers/stock_controller.py
index e27718a..36bed36 100644
--- a/erpnext/controllers/stock_controller.py
+++ b/erpnext/controllers/stock_controller.py
@@ -36,6 +36,10 @@
pass
+class BatchExpiredError(frappe.ValidationError):
+ pass
+
+
class StockController(AccountsController):
def validate(self):
super(StockController, self).validate()
@@ -77,6 +81,10 @@
def validate_serialized_batch(self):
from erpnext.stock.doctype.serial_no.serial_no import get_serial_nos
+ is_material_issue = False
+ if self.doctype == "Stock Entry" and self.purpose == "Material Issue":
+ is_material_issue = True
+
for d in self.get("items"):
if hasattr(d, "serial_no") and hasattr(d, "batch_no") and d.serial_no and d.batch_no:
serial_nos = frappe.get_all(
@@ -93,6 +101,9 @@
)
)
+ if is_material_issue:
+ continue
+
if flt(d.qty) > 0.0 and d.get("batch_no") and self.get("posting_date") and self.docstatus < 2:
expiry_date = frappe.get_cached_value("Batch", d.get("batch_no"), "expiry_date")
@@ -100,7 +111,8 @@
frappe.throw(
_("Row #{0}: The batch {1} has already expired.").format(
d.idx, get_link_to_form("Batch", d.get("batch_no"))
- )
+ ),
+ BatchExpiredError,
)
def clean_serial_nos(self):
@@ -310,7 +322,13 @@
)
if (
self.doctype
- not in ("Purchase Receipt", "Purchase Invoice", "Stock Reconciliation", "Stock Entry")
+ not in (
+ "Purchase Receipt",
+ "Purchase Invoice",
+ "Stock Reconciliation",
+ "Stock Entry",
+ "Subcontracting Receipt",
+ )
and not is_expense_account
):
frappe.throw(
@@ -374,9 +392,24 @@
def update_inventory_dimensions(self, row, sl_dict) -> None:
dimensions = get_evaluated_inventory_dimension(row, sl_dict, parent_doc=self)
for dimension in dimensions:
- if dimension and row.get(dimension.source_fieldname):
+ if not dimension:
+ continue
+
+ if row.get(dimension.source_fieldname):
sl_dict[dimension.target_fieldname] = row.get(dimension.source_fieldname)
+ if not sl_dict.get(dimension.target_fieldname) and dimension.fetch_from_parent:
+ sl_dict[dimension.target_fieldname] = self.get(dimension.fetch_from_parent)
+
+ # Get value based on doctype name
+ if not sl_dict.get(dimension.target_fieldname):
+ fieldname = frappe.get_cached_value(
+ "DocField", {"parent": self.doctype, "options": dimension.fetch_from_parent}, "fieldname"
+ )
+
+ if fieldname and self.get(fieldname):
+ sl_dict[dimension.target_fieldname] = self.get(fieldname)
+
def make_sl_entries(self, sl_entries, allow_negative_stock=False, via_landed_cost_voucher=False):
from erpnext.stock.stock_ledger import make_sl_entries
diff --git a/erpnext/controllers/subcontracting_controller.py b/erpnext/controllers/subcontracting_controller.py
index 2a2f8f5..1372c89 100644
--- a/erpnext/controllers/subcontracting_controller.py
+++ b/erpnext/controllers/subcontracting_controller.py
@@ -490,7 +490,7 @@
row.item_code,
row.get(self.subcontract_data.order_field),
) and transfer_item.qty > 0:
- qty = self.__get_qty_based_on_material_transfer(row, transfer_item) or 0
+ qty = flt(self.__get_qty_based_on_material_transfer(row, transfer_item))
transfer_item.qty -= qty
self.__add_supplied_item(row, transfer_item.get("item_details"), qty)
@@ -720,6 +720,25 @@
sco_doc = frappe.get_doc("Subcontracting Order", sco)
sco_doc.update_status()
+ def set_missing_values_in_additional_costs(self):
+ self.total_additional_costs = sum(flt(item.amount) for item in self.get("additional_costs"))
+
+ if self.total_additional_costs:
+ if self.distribute_additional_costs_based_on == "Amount":
+ total_amt = sum(flt(item.amount) for item in self.get("items"))
+ for item in self.items:
+ item.additional_cost_per_qty = (
+ (item.amount * self.total_additional_costs) / total_amt
+ ) / item.qty
+ else:
+ total_qty = sum(flt(item.qty) for item in self.get("items"))
+ additional_cost_per_qty = self.total_additional_costs / total_qty
+ for item in self.items:
+ item.additional_cost_per_qty = additional_cost_per_qty
+ else:
+ for item in self.items:
+ item.additional_cost_per_qty = 0
+
@frappe.whitelist()
def get_current_stock(self):
if self.doctype in ["Purchase Receipt", "Subcontracting Receipt"]:
@@ -730,7 +749,7 @@
{"item_code": item.rm_item_code, "warehouse": self.supplier_warehouse},
"actual_qty",
)
- item.current_stock = flt(actual_qty) or 0
+ item.current_stock = flt(actual_qty)
@property
def sub_contracted_items(self):
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index 0d8cffe..bc38d08 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -37,6 +37,11 @@
self.set_discount_amount()
self.apply_discount_amount()
+ # Update grand total as per cash and non trade discount
+ if self.doc.apply_discount_on == "Grand Total" and self.doc.get("is_cash_or_non_trade_discount"):
+ self.doc.grand_total -= self.doc.discount_amount
+ self.doc.base_grand_total -= self.doc.base_discount_amount
+
self.calculate_shipping_charges()
if self.doc.doctype in ["Sales Invoice", "Purchase Invoice"]:
@@ -500,9 +505,6 @@
else:
self.doc.grand_total = flt(self.doc.net_total)
- if self.doc.apply_discount_on == "Grand Total" and self.doc.get("is_cash_or_non_trade_discount"):
- self.doc.grand_total -= self.doc.discount_amount
-
if self.doc.get("taxes"):
self.doc.total_taxes_and_charges = flt(
self.doc.grand_total - self.doc.net_total - flt(self.doc.rounding_adjustment),
@@ -597,16 +599,16 @@
if not self.doc.apply_discount_on:
frappe.throw(_("Please select Apply Discount On"))
+ self.doc.base_discount_amount = flt(
+ self.doc.discount_amount * self.doc.conversion_rate, self.doc.precision("base_discount_amount")
+ )
+
if self.doc.apply_discount_on == "Grand Total" and self.doc.get(
"is_cash_or_non_trade_discount"
):
self.discount_amount_applied = True
return
- self.doc.base_discount_amount = flt(
- self.doc.discount_amount * self.doc.conversion_rate, self.doc.precision("base_discount_amount")
- )
-
total_for_discount_amount = self.get_total_for_discount_amount()
taxes = self.doc.get("taxes")
net_total = 0
diff --git a/erpnext/controllers/tests/test_subcontracting_controller.py b/erpnext/controllers/tests/test_subcontracting_controller.py
index 4fab805..bc503f5 100644
--- a/erpnext/controllers/tests/test_subcontracting_controller.py
+++ b/erpnext/controllers/tests/test_subcontracting_controller.py
@@ -36,6 +36,36 @@
sco.remove_empty_rows()
self.assertEqual((len_before - 1), len(sco.service_items))
+ def test_set_missing_values_in_additional_costs(self):
+ sco = get_subcontracting_order(do_not_submit=1)
+
+ rate_without_additional_cost = sco.items[0].rate
+ amount_without_additional_cost = sco.items[0].amount
+
+ additional_amount = 120
+ sco.append(
+ "additional_costs",
+ {
+ "expense_account": "Cost of Goods Sold - _TC",
+ "description": "Test",
+ "amount": additional_amount,
+ },
+ )
+ sco.save()
+
+ additional_cost_per_qty = additional_amount / sco.items[0].qty
+
+ self.assertEqual(sco.items[0].additional_cost_per_qty, additional_cost_per_qty)
+ self.assertEqual(rate_without_additional_cost + additional_cost_per_qty, sco.items[0].rate)
+ self.assertEqual(amount_without_additional_cost + additional_amount, sco.items[0].amount)
+
+ sco.additional_costs = []
+ sco.save()
+
+ self.assertEqual(sco.items[0].additional_cost_per_qty, 0)
+ self.assertEqual(rate_without_additional_cost, sco.items[0].rate)
+ self.assertEqual(amount_without_additional_cost, sco.items[0].amount)
+
def test_create_raw_materials_supplied(self):
sco = get_subcontracting_order()
sco.supplied_items = None
diff --git a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py
index 7d676e4..cd4aaee 100644
--- a/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py
+++ b/erpnext/erpnext_integrations/doctype/tally_migration/tally_migration.py
@@ -12,7 +12,9 @@
import frappe
from bs4 import BeautifulSoup as bs
from frappe import _
-from frappe.custom.doctype.custom_field.custom_field import create_custom_field
+from frappe.custom.doctype.custom_field.custom_field import (
+ create_custom_fields as _create_custom_fields,
+)
from frappe.model.document import Document
from frappe.utils.data import format_datetime
@@ -577,22 +579,25 @@
new_year.save()
oldest_year = new_year
- def create_custom_fields(doctypes):
- tally_guid_df = {
- "fieldtype": "Data",
- "fieldname": "tally_guid",
- "read_only": 1,
- "label": "Tally GUID",
- }
- tally_voucher_no_df = {
- "fieldtype": "Data",
- "fieldname": "tally_voucher_no",
- "read_only": 1,
- "label": "Tally Voucher Number",
- }
- for df in [tally_guid_df, tally_voucher_no_df]:
- for doctype in doctypes:
- create_custom_field(doctype, df)
+ def create_custom_fields():
+ _create_custom_fields(
+ {
+ ("Journal Entry", "Purchase Invoice", "Sales Invoice"): [
+ {
+ "fieldtype": "Data",
+ "fieldname": "tally_guid",
+ "read_only": 1,
+ "label": "Tally GUID",
+ },
+ {
+ "fieldtype": "Data",
+ "fieldname": "tally_voucher_no",
+ "read_only": 1,
+ "label": "Tally Voucher Number",
+ },
+ ]
+ }
+ )
def create_price_list():
frappe.get_doc(
@@ -628,7 +633,7 @@
create_fiscal_years(vouchers)
create_price_list()
- create_custom_fields(["Journal Entry", "Purchase Invoice", "Sales Invoice"])
+ create_custom_fields()
total = len(vouchers)
is_last = False
diff --git a/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.py b/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.py
index 2e18776..4aa98aa 100644
--- a/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.py
+++ b/erpnext/erpnext_integrations/doctype/woocommerce_settings/woocommerce_settings.py
@@ -6,7 +6,7 @@
import frappe
from frappe import _
-from frappe.custom.doctype.custom_field.custom_field import create_custom_field
+from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
from frappe.model.document import Document
from frappe.utils.nestedset import get_root_of
@@ -19,27 +19,24 @@
def create_delete_custom_fields(self):
if self.enable_sync:
- custom_fields = {}
- # create
- for doctype in ["Customer", "Sales Order", "Item", "Address"]:
- df = dict(
- fieldname="woocommerce_id",
- label="Woocommerce ID",
- fieldtype="Data",
- read_only=1,
- print_hide=1,
- )
- create_custom_field(doctype, df)
-
- for doctype in ["Customer", "Address"]:
- df = dict(
- fieldname="woocommerce_email",
- label="Woocommerce Email",
- fieldtype="Data",
- read_only=1,
- print_hide=1,
- )
- create_custom_field(doctype, df)
+ create_custom_fields(
+ {
+ ("Customer", "Sales Order", "Item", "Address"): dict(
+ fieldname="woocommerce_id",
+ label="Woocommerce ID",
+ fieldtype="Data",
+ read_only=1,
+ print_hide=1,
+ ),
+ ("Customer", "Address"): dict(
+ fieldname="woocommerce_email",
+ label="Woocommerce Email",
+ fieldtype="Data",
+ read_only=1,
+ print_hide=1,
+ ),
+ }
+ )
if not frappe.get_value("Item Group", {"name": _("WooCommerce Products")}):
item_group = frappe.new_doc("Item Group")
diff --git a/erpnext/erpnext_integrations/exotel_integration.py b/erpnext/erpnext_integrations/exotel_integration.py
index fd9f74e..fd0f783 100644
--- a/erpnext/erpnext_integrations/exotel_integration.py
+++ b/erpnext/erpnext_integrations/exotel_integration.py
@@ -26,6 +26,7 @@
except Exception as e:
frappe.db.rollback()
exotel_settings.log_error("Error in Exotel incoming call")
+ frappe.db.commit()
@frappe.whitelist(allow_guest=True)
diff --git a/erpnext/hooks.py b/erpnext/hooks.py
index c4f0c59..a08feb4 100644
--- a/erpnext/hooks.py
+++ b/erpnext/hooks.py
@@ -520,6 +520,10 @@
"Purchase Order",
"Purchase Receipt",
"Sales Order",
+ "Subcontracting Order",
+ "Subcontracting Order Item",
+ "Subcontracting Receipt",
+ "Subcontracting Receipt Item",
]
# get matching queries for Bank Reconciliation
diff --git a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py
index 09d4429..3dc6b0f 100644
--- a/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py
+++ b/erpnext/maintenance/doctype/maintenance_schedule/maintenance_schedule.py
@@ -415,7 +415,7 @@
},
"Maintenance Schedule Item": {
"doctype": "Maintenance Visit Purpose",
- "condition": lambda doc: doc.item_name == item_name,
+ "condition": lambda doc: doc.item_name == item_name if item_name else True,
"field_map": {"sales_person": "service_person"},
"postprocess": update_serial,
},
diff --git a/erpnext/manufacturing/doctype/bom/bom.py b/erpnext/manufacturing/doctype/bom/bom.py
index b29f671..70637d3 100644
--- a/erpnext/manufacturing/doctype/bom/bom.py
+++ b/erpnext/manufacturing/doctype/bom/bom.py
@@ -189,8 +189,8 @@
self.validate_transfer_against()
self.set_routing_operations()
self.validate_operations()
- self.update_exploded_items(save=False)
self.calculate_cost()
+ self.update_exploded_items(save=False)
self.update_stock_qty()
self.update_cost(update_parent=False, from_child_bom=True, update_hour_rate=False, save=False)
self.validate_scrap_items()
diff --git a/erpnext/manufacturing/doctype/bom/test_bom.py b/erpnext/manufacturing/doctype/bom/test_bom.py
index a190cc7..27f3cc9 100644
--- a/erpnext/manufacturing/doctype/bom/test_bom.py
+++ b/erpnext/manufacturing/doctype/bom/test_bom.py
@@ -611,6 +611,34 @@
bom.reload()
self.assertEqual(frappe.get_value("Item", fg_item.item_code, "default_bom"), bom.name)
+ def test_exploded_items_rate(self):
+ rm_item = make_item(
+ properties={"is_stock_item": 1, "valuation_rate": 99, "last_purchase_rate": 89}
+ ).name
+ fg_item = make_item(properties={"is_stock_item": 1}).name
+
+ from erpnext.manufacturing.doctype.production_plan.test_production_plan import make_bom
+
+ bom = make_bom(item=fg_item, raw_materials=[rm_item], do_not_save=True)
+
+ bom.rm_cost_as_per = "Last Purchase Rate"
+ bom.save()
+ self.assertEqual(bom.items[0].base_rate, 89)
+ self.assertEqual(bom.exploded_items[0].rate, bom.items[0].base_rate)
+
+ bom.rm_cost_as_per = "Price List"
+ bom.save()
+ self.assertEqual(bom.items[0].base_rate, 0.0)
+ self.assertEqual(bom.exploded_items[0].rate, bom.items[0].base_rate)
+
+ bom.rm_cost_as_per = "Valuation Rate"
+ bom.save()
+ self.assertEqual(bom.items[0].base_rate, 99)
+ self.assertEqual(bom.exploded_items[0].rate, bom.items[0].base_rate)
+
+ bom.submit()
+ self.assertEqual(bom.exploded_items[0].rate, bom.items[0].base_rate)
+
def get_default_bom(item_code="_Test FG Item 2"):
return frappe.db.get_value("BOM", {"item": item_code, "is_active": 1, "is_default": 1})
diff --git a/erpnext/manufacturing/doctype/bom_item/bom_item.json b/erpnext/manufacturing/doctype/bom_item/bom_item.json
index 0a8ae7b..c526611 100644
--- a/erpnext/manufacturing/doctype/bom_item/bom_item.json
+++ b/erpnext/manufacturing/doctype/bom_item/bom_item.json
@@ -184,6 +184,7 @@
"in_list_view": 1,
"label": "Rate",
"options": "currency",
+ "read_only": 1,
"reqd": 1
},
{
@@ -288,7 +289,7 @@
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2022-05-19 02:32:43.785470",
+ "modified": "2022-07-28 10:20:51.559010",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "BOM Item",
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index 70ccb78..66d458b 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -482,7 +482,6 @@
"bom_no",
"stock_uom",
"bom_level",
- "production_plan_item",
"schedule_date",
]:
if row.get(field):
@@ -639,6 +638,9 @@
sub_assembly_items_store = [] # temporary store to process all subassembly items
for row in self.po_items:
+ if not row.item_code:
+ frappe.throw(_("Row #{0}: Please select Item Code in Assembly Items").format(row.idx))
+
bom_data = []
get_sub_assembly_items(row.bom_no, bom_data, row.planned_qty)
self.set_sub_assembly_items_based_on_level(row, bom_data, manufacturing_type)
@@ -654,6 +656,8 @@
row.idx = idx + 1
self.append("sub_assembly_items", row)
+ self.set_default_supplier_for_subcontracting_order()
+
def set_sub_assembly_items_based_on_level(self, row, bom_data, manufacturing_type=None):
"Modify bom_data, set additional details."
for data in bom_data:
@@ -665,6 +669,32 @@
"Subcontract" if data.is_sub_contracted_item else "In House"
)
+ def set_default_supplier_for_subcontracting_order(self):
+ items = [
+ d.production_item for d in self.sub_assembly_items if d.type_of_manufacturing == "Subcontract"
+ ]
+
+ if not items:
+ return
+
+ default_supplier = frappe._dict(
+ frappe.get_all(
+ "Item Default",
+ fields=["parent", "default_supplier"],
+ filters={"parent": ("in", items), "default_supplier": ("is", "set")},
+ as_list=1,
+ )
+ )
+
+ if not default_supplier:
+ return
+
+ for row in self.sub_assembly_items:
+ if row.type_of_manufacturing != "Subcontract":
+ continue
+
+ row.supplier = default_supplier.get(row.production_item)
+
def combine_subassembly_items(self, sub_assembly_items_store):
"Aggregate if same: Item, Warehouse, Inhouse/Outhouse Manu.g, BOM No."
key_wise_data = {}
diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
index 040e791..1d2d1bd 100644
--- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
@@ -11,8 +11,9 @@
get_warehouse_list,
)
from erpnext.manufacturing.doctype.work_order.work_order import OverProductionError
+from erpnext.manufacturing.doctype.work_order.work_order import make_stock_entry as make_se_from_wo
from erpnext.selling.doctype.sales_order.test_sales_order import make_sales_order
-from erpnext.stock.doctype.item.test_item import create_item
+from erpnext.stock.doctype.item.test_item import create_item, make_item
from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry
from erpnext.stock.doctype.stock_reconciliation.test_stock_reconciliation import (
create_stock_reconciliation,
@@ -280,6 +281,31 @@
pln.reload()
pln.cancel()
+ def test_production_plan_subassembly_default_supplier(self):
+ from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom
+
+ bom_tree_1 = {"Test Laptop": {"Test Motherboard": {"Test Motherboard Wires": {}}}}
+ bom = create_nested_bom(bom_tree_1, prefix="")
+
+ item_doc = frappe.get_doc("Item", "Test Motherboard")
+ company = "_Test Company"
+
+ item_doc.is_sub_contracted_item = 1
+ for row in item_doc.item_defaults:
+ if row.company == company and not row.default_supplier:
+ row.default_supplier = "_Test Supplier"
+
+ if not item_doc.item_defaults:
+ item_doc.append("item_defaults", {"company": company, "default_supplier": "_Test Supplier"})
+
+ item_doc.save()
+
+ plan = create_production_plan(item_code="Test Laptop", use_multi_level_bom=1, do_not_submit=True)
+ plan.get_sub_assembly_items()
+ plan.set_default_supplier_for_subcontracting_order()
+
+ self.assertEqual(plan.sub_assembly_items[0].supplier, "_Test Supplier")
+
def test_production_plan_combine_subassembly(self):
"""
Test combining Sub assembly items belonging to the same BOM in Prod Plan.
@@ -583,9 +609,6 @@
Test Prod Plan impact via: SO -> Prod Plan -> WO -> SE -> SE (cancel)
"""
from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record
- from erpnext.manufacturing.doctype.work_order.work_order import (
- make_stock_entry as make_se_from_wo,
- )
make_stock_entry(
item_code="Raw Material Item 1", target="Work In Progress - _TC", qty=2, basic_rate=100
@@ -629,9 +652,6 @@
def test_production_plan_pending_qty_independent_items(self):
"Test Prod Plan impact if items are added independently (no from SO or MR)."
from erpnext.manufacturing.doctype.work_order.test_work_order import make_wo_order_test_record
- from erpnext.manufacturing.doctype.work_order.work_order import (
- make_stock_entry as make_se_from_wo,
- )
make_stock_entry(
item_code="Raw Material Item 1", target="Work In Progress - _TC", qty=2, basic_rate=100
@@ -728,6 +748,57 @@
for po_item, subassy_item in zip(pp.po_items, pp.sub_assembly_items):
self.assertEqual(po_item.name, subassy_item.production_plan_item)
+ def test_produced_qty_for_multi_level_bom_item(self):
+ # Create Items and BOMs
+ rm_item = make_item(properties={"is_stock_item": 1}).name
+ sub_assembly_item = make_item(properties={"is_stock_item": 1}).name
+ fg_item = make_item(properties={"is_stock_item": 1}).name
+
+ make_stock_entry(
+ item_code=rm_item,
+ qty=60,
+ to_warehouse="Work In Progress - _TC",
+ rate=99,
+ purpose="Material Receipt",
+ )
+
+ make_bom(item=sub_assembly_item, raw_materials=[rm_item], rm_qty=3)
+ make_bom(item=fg_item, raw_materials=[sub_assembly_item], rm_qty=4)
+
+ # Step - 1: Create Production Plan
+ pln = create_production_plan(item_code=fg_item, planned_qty=5, skip_getting_mr_items=1)
+ pln.get_sub_assembly_items()
+
+ # Step - 2: Create Work Orders
+ pln.make_work_order()
+ work_orders = frappe.get_all("Work Order", filters={"production_plan": pln.name}, pluck="name")
+ sa_wo = fg_wo = None
+ for work_order in work_orders:
+ wo_doc = frappe.get_doc("Work Order", work_order)
+ if wo_doc.production_plan_item:
+ wo_doc.update(
+ {"wip_warehouse": "Work In Progress - _TC", "fg_warehouse": "Finished Goods - _TC"}
+ )
+ fg_wo = wo_doc.name
+ else:
+ wo_doc.update(
+ {"wip_warehouse": "Work In Progress - _TC", "fg_warehouse": "Work In Progress - _TC"}
+ )
+ sa_wo = wo_doc.name
+ wo_doc.submit()
+
+ # Step - 3: Complete Work Orders
+ se = frappe.get_doc(make_se_from_wo(sa_wo, "Manufacture"))
+ se.submit()
+
+ se = frappe.get_doc(make_se_from_wo(fg_wo, "Manufacture"))
+ se.submit()
+
+ # Step - 4: Check Production Plan Item Produced Qty
+ pln.load_from_db()
+ self.assertEqual(pln.status, "Completed")
+ self.assertEqual(pln.po_items[0].produced_qty, 5)
+
def create_production_plan(**args):
"""
diff --git a/erpnext/manufacturing/doctype/work_order/test_work_order.py b/erpnext/manufacturing/doctype/work_order/test_work_order.py
index b556d99..a53c42c 100644
--- a/erpnext/manufacturing/doctype/work_order/test_work_order.py
+++ b/erpnext/manufacturing/doctype/work_order/test_work_order.py
@@ -26,6 +26,8 @@
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
from erpnext.stock.utils import get_bin
+test_dependencies = ["BOM"]
+
class TestWorkOrder(FrappeTestCase):
def setUp(self):
diff --git a/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py b/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py
index 465460f..d0dcc55 100644
--- a/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py
+++ b/erpnext/manufacturing/doctype/work_order/work_order_dashboard.py
@@ -7,6 +7,6 @@
"non_standard_fieldnames": {"Batch": "reference_name"},
"transactions": [
{"label": _("Transactions"), "items": ["Stock Entry", "Job Card", "Pick List"]},
- {"label": _("Reference"), "items": ["Serial No", "Batch"]},
+ {"label": _("Reference"), "items": ["Serial No", "Batch", "Material Request"]},
],
}
diff --git a/erpnext/patches.txt b/erpnext/patches.txt
index e5beacd..d92353a 100644
--- a/erpnext/patches.txt
+++ b/erpnext/patches.txt
@@ -310,4 +310,5 @@
erpnext.patches.v14_0.remove_india_localisation # 14-07-2022
erpnext.patches.v13_0.fix_number_and_frequency_for_monthly_depreciation
erpnext.patches.v14_0.remove_hr_and_payroll_modules # 20-07-2022
-erpnext.patches.v14_0.fix_crm_no_of_employees
\ No newline at end of file
+erpnext.patches.v14_0.fix_crm_no_of_employees
+erpnext.patches.v14_0.create_accounting_dimensions_in_subcontracting_doctypes
\ No newline at end of file
diff --git a/erpnext/patches/v14_0/create_accounting_dimensions_in_subcontracting_doctypes.py b/erpnext/patches/v14_0/create_accounting_dimensions_in_subcontracting_doctypes.py
new file mode 100644
index 0000000..b349c07
--- /dev/null
+++ b/erpnext/patches/v14_0/create_accounting_dimensions_in_subcontracting_doctypes.py
@@ -0,0 +1,47 @@
+import frappe
+from frappe.custom.doctype.custom_field.custom_field import create_custom_field
+
+
+def execute():
+ accounting_dimensions = frappe.db.get_all(
+ "Accounting Dimension", fields=["fieldname", "label", "document_type", "disabled"]
+ )
+
+ if not accounting_dimensions:
+ return
+
+ count = 1
+ for d in accounting_dimensions:
+
+ if count % 2 == 0:
+ insert_after_field = "dimension_col_break"
+ else:
+ insert_after_field = "accounting_dimensions_section"
+
+ for doctype in [
+ "Subcontracting Order",
+ "Subcontracting Order Item",
+ "Subcontracting Receipt",
+ "Subcontracting Receipt Item",
+ ]:
+
+ field = frappe.db.get_value("Custom Field", {"dt": doctype, "fieldname": d.fieldname})
+
+ if field:
+ continue
+
+ df = {
+ "fieldname": d.fieldname,
+ "label": d.label,
+ "fieldtype": "Link",
+ "options": d.document_type,
+ "insert_after": insert_after_field,
+ }
+
+ try:
+ create_custom_field(doctype, df, ignore_validate=True)
+ frappe.clear_cache(doctype=doctype)
+ except Exception:
+ pass
+
+ count += 1
diff --git a/erpnext/projects/doctype/project/project.py b/erpnext/projects/doctype/project/project.py
index e78e4b6..a2be936 100644
--- a/erpnext/projects/doctype/project/project.py
+++ b/erpnext/projects/doctype/project/project.py
@@ -379,7 +379,7 @@
{fcond} {mcond}
order by
(case when locate(%(_txt)s, name) > 0 then locate(%(_txt)s, name) else 99999 end),
- (case when locate(%(_txt)s, full_name) > 0 then locate(%(_txt)s, full_name) else 99999 end)
+ (case when locate(%(_txt)s, full_name) > 0 then locate(%(_txt)s, full_name) else 99999 end),
idx desc,
name, full_name
limit %(page_len)s offset %(start)s""".format(
diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js
index 16b0b4a..4c3e9dc 100644
--- a/erpnext/public/js/controllers/taxes_and_totals.js
+++ b/erpnext/public/js/controllers/taxes_and_totals.js
@@ -39,6 +39,12 @@
this._calculate_taxes_and_totals();
this.calculate_discount_amount();
+ // # Update grand total as per cash and non trade discount
+ if (this.frm.doc.apply_discount_on == "Grand Total" && this.frm.doc.is_cash_or_non_trade_discount) {
+ this.frm.doc.grand_total -= this.frm.doc.discount_amount;
+ this.frm.doc.base_grand_total -= this.frm.doc.base_discount_amount;
+ }
+
await this.calculate_shipping_charges();
// Advance calculation applicable to Sales /Purchase Invoice
@@ -633,6 +639,10 @@
this.frm.doc.base_discount_amount = flt(this.frm.doc.discount_amount * this.frm.doc.conversion_rate,
precision("base_discount_amount"));
+ if (this.frm.doc.apply_discount_on == "Grand Total" && this.frm.doc.is_cash_or_non_trade_discount) {
+ return;
+ }
+
var total_for_discount_amount = this.get_total_for_discount_amount();
var net_total = 0;
// calculate item amount after Discount Amount
diff --git a/erpnext/selling/page/point_of_sale/pos_item_cart.js b/erpnext/selling/page/point_of_sale/pos_item_cart.js
index eacf480..e7dd211 100644
--- a/erpnext/selling/page/point_of_sale/pos_item_cart.js
+++ b/erpnext/selling/page/point_of_sale/pos_item_cart.js
@@ -524,7 +524,8 @@
const currency = this.events.get_frm().doc.currency;
const taxes_html = taxes.map(t => {
if (t.tax_amount_after_discount_amount == 0.0) return;
- const description = /[0-9]+/.test(t.description) ? t.description : `${t.description} @ ${t.rate}%`;
+ // if tax rate is 0, don't print it.
+ const description = /[0-9]+/.test(t.description) ? t.description : ((t.rate != 0) ? `${t.description} @ ${t.rate}%`: t.description);
return `<div class="tax-row">
<div class="tax-label">${description}</div>
<div class="tax-value">${format_currency(t.tax_amount_after_discount_amount, currency)}</div>
diff --git a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js
index eeb8523..40165c3 100644
--- a/erpnext/selling/page/point_of_sale/pos_past_order_summary.js
+++ b/erpnext/selling/page/point_of_sale/pos_past_order_summary.js
@@ -130,7 +130,8 @@
if (!doc.taxes.length) return '';
let taxes_html = doc.taxes.map(t => {
- const description = /[0-9]+/.test(t.description) ? t.description : `${t.description} @ ${t.rate}%`;
+ // if tax rate is 0, don't print it.
+ const description = /[0-9]+/.test(t.description) ? t.description : ((t.rate != 0) ? `${t.description} @ ${t.rate}%`: t.description);
return `
<div class="tax-row">
<div class="tax-label">${description}</div>
diff --git a/erpnext/selling/report/sales_analytics/sales_analytics.py b/erpnext/selling/report/sales_analytics/sales_analytics.py
index 9d7d806..1863528 100644
--- a/erpnext/selling/report/sales_analytics/sales_analytics.py
+++ b/erpnext/selling/report/sales_analytics/sales_analytics.py
@@ -168,7 +168,7 @@
def get_sales_transactions_based_on_items(self):
if self.filters["value_quantity"] == "Value":
- value_field = "base_amount"
+ value_field = "base_net_amount"
else:
value_field = "stock_qty"
@@ -216,7 +216,7 @@
def get_sales_transactions_based_on_item_group(self):
if self.filters["value_quantity"] == "Value":
- value_field = "base_amount"
+ value_field = "base_net_amount"
else:
value_field = "qty"
diff --git a/erpnext/setup/install.py b/erpnext/setup/install.py
index 7d7e6b5..2076dde 100644
--- a/erpnext/setup/install.py
+++ b/erpnext/setup/install.py
@@ -4,7 +4,7 @@
import frappe
from frappe import _
-from frappe.custom.doctype.custom_field.custom_field import create_custom_field
+from frappe.custom.doctype.custom_field.custom_field import create_custom_fields
from frappe.desk.page.setup_wizard.setup_wizard import add_all_roles_to
from frappe.utils import cint
@@ -83,35 +83,32 @@
def create_print_setting_custom_fields():
- create_custom_field(
- "Print Settings",
+ create_custom_fields(
{
- "label": _("Compact Item Print"),
- "fieldname": "compact_item_print",
- "fieldtype": "Check",
- "default": 1,
- "insert_after": "with_letterhead",
- },
- )
- create_custom_field(
- "Print Settings",
- {
- "label": _("Print UOM after Quantity"),
- "fieldname": "print_uom_after_quantity",
- "fieldtype": "Check",
- "default": 0,
- "insert_after": "compact_item_print",
- },
- )
- create_custom_field(
- "Print Settings",
- {
- "label": _("Print taxes with zero amount"),
- "fieldname": "print_taxes_with_zero_amount",
- "fieldtype": "Check",
- "default": 0,
- "insert_after": "allow_print_for_cancelled",
- },
+ "Print Settings": [
+ {
+ "label": _("Compact Item Print"),
+ "fieldname": "compact_item_print",
+ "fieldtype": "Check",
+ "default": "1",
+ "insert_after": "with_letterhead",
+ },
+ {
+ "label": _("Print UOM after Quantity"),
+ "fieldname": "print_uom_after_quantity",
+ "fieldtype": "Check",
+ "default": "0",
+ "insert_after": "compact_item_print",
+ },
+ {
+ "label": _("Print taxes with zero amount"),
+ "fieldname": "print_taxes_with_zero_amount",
+ "fieldtype": "Check",
+ "default": "0",
+ "insert_after": "allow_print_for_cancelled",
+ },
+ ]
+ }
)
diff --git a/erpnext/stock/doctype/batch/test_batch.py b/erpnext/stock/doctype/batch/test_batch.py
index 3e470d4..271e2e0 100644
--- a/erpnext/stock/doctype/batch/test_batch.py
+++ b/erpnext/stock/doctype/batch/test_batch.py
@@ -473,7 +473,13 @@
"doctype": "Batch",
"batch_id": args.batch_id,
"item": args.item_code,
+ "expiry_date": args.expiry_date,
}
- ).insert()
+ )
+
+ if args.expiry_date:
+ batch.expiry_date = args.expiry_date
+
+ batch.insert()
return batch
diff --git a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.js b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.js
index 91a21f4..07cb73b 100644
--- a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.js
+++ b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.js
@@ -35,14 +35,39 @@
refresh(frm) {
if (frm.doc.__onload && frm.doc.__onload.has_stock_ledger
&& frm.doc.__onload.has_stock_ledger.length) {
- let msg = __('Stock transactions exists against this dimension, user can not update document.');
- frm.dashboard.add_comment(msg, 'blue', true);
+ let allow_to_edit_fields = ['disabled', 'fetch_from_parent',
+ 'type_of_transaction', 'condition'];
frm.fields.forEach((field) => {
- if (field.df.fieldname !== 'disabled') {
+ if (!in_list(allow_to_edit_fields, field.df.fieldname)) {
frm.set_df_property(field.df.fieldname, "read_only", "1");
}
});
}
+
+ if (!frm.is_new()) {
+ frm.add_custom_button(__('Delete Dimension'), () => {
+ frm.trigger('delete_dimension');
+ });
+ }
+ },
+
+ delete_dimension(frm) {
+ let msg = (`
+ Custom fields related to this dimension will be deleted on deletion of dimension.
+ <br> Do you want to delete {0} dimension?
+ `);
+
+ frappe.confirm(__(msg, [frm.doc.name.bold()]), () => {
+ frappe.call({
+ method: 'erpnext.stock.doctype.inventory_dimension.inventory_dimension.delete_dimension',
+ args: {
+ dimension: frm.doc.name
+ },
+ callback: function() {
+ frappe.set_route('List', 'Inventory Dimension');
+ }
+ });
+ });
}
});
diff --git a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.json b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.json
index 8b334d1..03e7fda 100644
--- a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.json
+++ b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.json
@@ -1,6 +1,5 @@
{
"actions": [],
- "allow_rename": 1,
"autoname": "field:dimension_name",
"creation": "2022-06-17 13:04:16.554051",
"doctype": "DocType",
@@ -22,6 +21,7 @@
"document_type",
"istable",
"type_of_transaction",
+ "fetch_from_parent",
"column_break_16",
"condition",
"applicable_condition_example_section",
@@ -101,12 +101,14 @@
"fieldname": "target_fieldname",
"fieldtype": "Data",
"label": "Target Fieldname (Stock Ledger Entry)",
+ "no_copy": 1,
"read_only": 1
},
{
"fieldname": "source_fieldname",
"fieldtype": "Data",
"label": "Source Fieldname",
+ "no_copy": 1,
"read_only": 1
},
{
@@ -123,7 +125,7 @@
"fieldname": "type_of_transaction",
"fieldtype": "Select",
"label": "Type of Transaction",
- "options": "\nInward\nOutward"
+ "options": "\nInward\nOutward\nBoth"
},
{
"fieldname": "html_19",
@@ -140,11 +142,18 @@
{
"fieldname": "column_break_4",
"fieldtype": "Column Break"
+ },
+ {
+ "depends_on": "istable",
+ "description": "Set fieldname or DocType name like Supplier, Customer etc.",
+ "fieldname": "fetch_from_parent",
+ "fieldtype": "Data",
+ "label": "Fetch Value From Parent Form"
}
],
"index_web_pages_for_search": 1,
"links": [],
- "modified": "2022-07-19 21:06:11.824976",
+ "modified": "2022-08-17 11:43:24.722441",
"modified_by": "Administrator",
"module": "Stock",
"name": "Inventory Dimension",
diff --git a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py
index 5a9541f..4ff8f33 100644
--- a/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py
+++ b/erpnext/stock/doctype/inventory_dimension/inventory_dimension.py
@@ -43,13 +43,37 @@
return
old_doc = self._doc_before_save
+ allow_to_edit_fields = [
+ "disabled",
+ "fetch_from_parent",
+ "type_of_transaction",
+ "condition",
+ ]
+
for field in frappe.get_meta("Inventory Dimension").fields:
- if field.fieldname != "disabled" and old_doc.get(field.fieldname) != self.get(field.fieldname):
+ if field.fieldname not in allow_to_edit_fields and old_doc.get(field.fieldname) != self.get(
+ field.fieldname
+ ):
msg = f"""The user can not change value of the field {bold(field.label)} because
stock transactions exists against the dimension {bold(self.name)}."""
frappe.throw(_(msg), DoNotChangeError)
+ def on_trash(self):
+ self.delete_custom_fields()
+
+ def delete_custom_fields(self):
+ filters = {"fieldname": self.source_fieldname}
+
+ if self.document_type:
+ filters["dt"] = self.document_type
+
+ for field in frappe.get_all("Custom Field", filters=filters):
+ frappe.delete_doc("Custom Field", field.name)
+
+ msg = f"Deleted custom fields related to the dimension {self.name}"
+ frappe.msgprint(_(msg))
+
def reset_value(self):
if self.apply_to_all_doctypes:
self.istable = 0
@@ -76,30 +100,35 @@
self.add_custom_fields()
def add_custom_fields(self):
- dimension_field = dict(
- fieldname=self.source_fieldname,
- fieldtype="Link",
- insert_after="warehouse",
- options=self.reference_document,
- label=self.dimension_name,
- )
+ dimension_fields = [
+ dict(
+ fieldname="inventory_dimension",
+ fieldtype="Section Break",
+ insert_after="warehouse",
+ label="Inventory Dimension",
+ collapsible=1,
+ ),
+ dict(
+ fieldname=self.source_fieldname,
+ fieldtype="Link",
+ insert_after="inventory_dimension",
+ options=self.reference_document,
+ label=self.dimension_name,
+ ),
+ ]
custom_fields = {}
if self.apply_to_all_doctypes:
for doctype in get_inventory_documents():
- if not frappe.db.get_value(
- "Custom Field", {"dt": doctype[0], "fieldname": self.source_fieldname}
- ):
- custom_fields.setdefault(doctype[0], dimension_field)
- elif not frappe.db.get_value(
- "Custom Field", {"dt": self.document_type, "fieldname": self.source_fieldname}
- ):
- custom_fields.setdefault(self.document_type, dimension_field)
+ custom_fields.setdefault(doctype[0], dimension_fields)
+ else:
+ custom_fields.setdefault(self.document_type, dimension_fields)
if not frappe.db.get_value(
"Custom Field", {"dt": "Stock Ledger Entry", "fieldname": self.target_fieldname}
):
+ dimension_field = dimension_fields[1]
dimension_field["fieldname"] = self.target_fieldname
custom_fields["Stock Ledger Entry"] = dimension_field
@@ -143,7 +172,7 @@
elif (
row.type_of_transaction == "Outward"
if doc.docstatus == 1
- else row.type_of_transaction != "Inward"
+ else row.type_of_transaction != "Outward"
) and sl_dict.actual_qty > 0:
continue
@@ -166,7 +195,14 @@
if not frappe.local.document_wise_inventory_dimensions.get(doctype):
dimensions = frappe.get_all(
"Inventory Dimension",
- fields=["name", "source_fieldname", "condition", "target_fieldname", "type_of_transaction"],
+ fields=[
+ "name",
+ "source_fieldname",
+ "condition",
+ "target_fieldname",
+ "type_of_transaction",
+ "fetch_from_parent",
+ ],
filters={"disabled": 0},
or_filters={"document_type": doctype, "apply_to_all_doctypes": 1},
)
@@ -194,3 +230,9 @@
frappe.local.inventory_dimensions = dimensions
return frappe.local.inventory_dimensions
+
+
+@frappe.whitelist()
+def delete_dimension(dimension):
+ doc = frappe.get_doc("Inventory Dimension", dimension)
+ doc.delete()
diff --git a/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py b/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py
index 998a0e9..cc90b74 100644
--- a/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py
+++ b/erpnext/stock/doctype/inventory_dimension/test_inventory_dimension.py
@@ -8,6 +8,7 @@
CanNotBeChildDoc,
CanNotBeDefaultDimension,
DoNotChangeError,
+ delete_dimension,
)
from erpnext.stock.doctype.stock_entry.stock_entry_utils import make_stock_entry
from erpnext.stock.doctype.warehouse.test_warehouse import create_warehouse
@@ -42,6 +43,32 @@
self.assertRaises(CanNotBeDefaultDimension, inv_dim1.insert)
+ def test_delete_inventory_dimension(self):
+ inv_dim1 = create_inventory_dimension(
+ reference_document="Shelf",
+ type_of_transaction="Outward",
+ dimension_name="From Shelf",
+ apply_to_all_doctypes=0,
+ document_type="Stock Entry Detail",
+ condition="parent.purpose == 'Material Issue'",
+ )
+
+ inv_dim1.save()
+
+ custom_field = frappe.db.get_value(
+ "Custom Field", {"fieldname": "from_shelf", "dt": "Stock Entry Detail"}, "name"
+ )
+
+ self.assertTrue(custom_field)
+
+ delete_dimension(inv_dim1.name)
+
+ custom_field = frappe.db.get_value(
+ "Custom Field", {"fieldname": "from_shelf", "dt": "Stock Entry Detail"}, "name"
+ )
+
+ self.assertFalse(custom_field)
+
def test_inventory_dimension(self):
warehouse = "Shelf Warehouse - _TC"
item_code = "_Test Item"
diff --git a/erpnext/stock/doctype/item/item_dashboard.py b/erpnext/stock/doctype/item/item_dashboard.py
index 897acb7..34bb4d1 100644
--- a/erpnext/stock/doctype/item/item_dashboard.py
+++ b/erpnext/stock/doctype/item/item_dashboard.py
@@ -5,7 +5,7 @@
return {
"heatmap": True,
"heatmap_message": _("This is based on stock movement. See {0} for details").format(
- '<a href="#query-report/Stock Ledger">' + _("Stock Ledger") + "</a>"
+ '<a href="/app/query-report/Stock Ledger">' + _("Stock Ledger") + "</a>"
),
"fieldname": "item_code",
"non_standard_fieldnames": {
diff --git a/erpnext/stock/doctype/material_request/material_request.json b/erpnext/stock/doctype/material_request/material_request.json
index cb46a6c..3593130 100644
--- a/erpnext/stock/doctype/material_request/material_request.json
+++ b/erpnext/stock/doctype/material_request/material_request.json
@@ -37,7 +37,8 @@
"tc_name",
"terms",
"reference",
- "job_card"
+ "job_card",
+ "work_order"
],
"fields": [
{
@@ -309,16 +310,24 @@
"label": "Transfer Status",
"options": "\nNot Started\nIn Transit\nCompleted",
"read_only": 1
+ },
+ {
+ "fieldname": "work_order",
+ "fieldtype": "Link",
+ "label": "Work Order",
+ "options": "Work Order",
+ "read_only": 1
}
],
"icon": "fa fa-ticket",
"idx": 70,
"is_submittable": 1,
"links": [],
- "modified": "2021-08-17 20:16:12.737743",
+ "modified": "2022-08-25 11:49:28.155048",
"modified_by": "Administrator",
"module": "Stock",
"name": "Material Request",
+ "naming_rule": "By \"Naming Series\" field",
"owner": "Administrator",
"permissions": [
{
@@ -386,5 +395,6 @@
"show_name_in_global_search": 1,
"sort_field": "modified",
"sort_order": "DESC",
+ "states": [],
"title_field": "title"
}
\ No newline at end of file
diff --git a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
index c97dbee..39833b5 100644
--- a/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
+++ b/erpnext/stock/doctype/purchase_receipt_item/purchase_receipt_item.json
@@ -792,10 +792,8 @@
{
"fieldname": "expense_account",
"fieldtype": "Link",
- "hidden": 1,
"label": "Expense Account",
- "options": "Account",
- "read_only": 1
+ "options": "Account"
},
{
"fieldname": "accounting_dimensions_section",
@@ -1001,7 +999,7 @@
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2022-06-17 05:32:16.483178",
+ "modified": "2022-07-28 19:27:54.880781",
"modified_by": "Administrator",
"module": "Stock",
"name": "Purchase Receipt Item",
diff --git a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js
index 4cd40bf..eae7305 100644
--- a/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js
+++ b/erpnext/stock/doctype/repost_item_valuation/repost_item_valuation.js
@@ -15,7 +15,7 @@
return {
filters: {
name: ['in', ['Purchase Receipt', 'Purchase Invoice', 'Delivery Note',
- 'Sales Invoice', 'Stock Entry', 'Stock Reconciliation']]
+ 'Sales Invoice', 'Stock Entry', 'Stock Reconciliation', 'Subcontracting Receipt']]
}
};
});
@@ -24,7 +24,8 @@
frm.set_query("voucher_no", () => {
return {
filters: {
- company: frm.doc.company
+ company: frm.doc.company,
+ docstatus: 1
}
};
});
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.js b/erpnext/stock/doctype/stock_entry/stock_entry.js
index 1c514a9..1bbe570 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.js
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.js
@@ -174,6 +174,8 @@
if(!items.length) {
items = frm.doc.items;
}
+
+ mr.work_order = frm.doc.work_order;
items.forEach(function(item) {
var mr_item = frappe.model.add_child(mr, 'items');
mr_item.item_code = item.item_code;
@@ -583,18 +585,23 @@
},
add_to_transit: function(frm) {
- if(frm.doc.add_to_transit && frm.doc.purpose=='Material Transfer') {
- frm.set_value('to_warehouse', '');
+ if(frm.doc.purpose=='Material Transfer') {
+ var filters = {
+ 'is_group': 0,
+ 'company': frm.doc.company
+ }
+
+ if(frm.doc.add_to_transit){
+ filters['warehouse_type'] = 'Transit';
+ frm.set_value('to_warehouse', '');
+ frm.trigger('set_transit_warehouse');
+ }
+
frm.fields_dict.to_warehouse.get_query = function() {
return {
- filters:{
- 'warehouse_type' : 'Transit',
- 'is_group': 0,
- 'company': frm.doc.company
- }
+ filters:filters
};
};
- frm.trigger('set_transit_warehouse');
}
},
diff --git a/erpnext/stock/doctype/stock_entry/test_stock_entry.py b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
index a2f9978..b574b71 100644
--- a/erpnext/stock/doctype/stock_entry/test_stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/test_stock_entry.py
@@ -5,7 +5,7 @@
import frappe
from frappe.permissions import add_user_permission, remove_user_permission
from frappe.tests.utils import FrappeTestCase, change_settings
-from frappe.utils import add_days, flt, nowdate, nowtime
+from frappe.utils import add_days, flt, nowdate, nowtime, today
from erpnext.accounts.doctype.account.test_account import get_inventory_account
from erpnext.stock.doctype.item.test_item import (
@@ -1589,6 +1589,31 @@
self.assertEqual(obj.items[index].basic_rate, 200)
self.assertEqual(obj.items[index].basic_amount, 2000)
+ def test_batch_expiry(self):
+ from erpnext.controllers.stock_controller import BatchExpiredError
+ from erpnext.stock.doctype.batch.test_batch import make_new_batch
+
+ item_code = "Test Batch Expiry Test Item - 001"
+ item_doc = create_item(item_code=item_code, is_stock_item=1, valuation_rate=10)
+
+ item_doc.has_batch_no = 1
+ item_doc.save()
+
+ batch = make_new_batch(
+ batch_id=frappe.generate_hash("", 5), item_code=item_doc.name, expiry_date=add_days(today(), -1)
+ )
+
+ se = make_stock_entry(
+ item_code=item_code,
+ purpose="Material Receipt",
+ qty=4,
+ to_warehouse="_Test Warehouse - _TC",
+ batch_no=batch.name,
+ do_not_save=True,
+ )
+
+ self.assertRaises(BatchExpiredError, se.save)
+
def make_serialized_item(**args):
args = frappe._dict(args)
diff --git a/erpnext/stock/landed_taxes_and_charges_common.js b/erpnext/stock/landed_taxes_and_charges_common.js
index ff8a69f..1d76a3d 100644
--- a/erpnext/stock/landed_taxes_and_charges_common.js
+++ b/erpnext/stock/landed_taxes_and_charges_common.js
@@ -1,4 +1,4 @@
-let document_list = ['Landed Cost Voucher', 'Stock Entry'];
+let document_list = ['Landed Cost Voucher', 'Stock Entry', 'Subcontracting Order', 'Subcontracting Receipt'];
document_list.forEach((doctype) => {
frappe.ui.form.on(doctype, {
diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js
index dbd337a..c20f8ab 100644
--- a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js
+++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.js
@@ -3,6 +3,8 @@
frappe.provide('erpnext.buying');
+{% include 'erpnext/stock/landed_taxes_and_charges_common.js' %};
+
frappe.ui.form.on('Subcontracting Order', {
setup: (frm) => {
frm.get_field("items").grid.cannot_add_rows = true;
@@ -136,6 +138,16 @@
}
});
+frappe.ui.form.on('Landed Cost Taxes and Charges', {
+ amount: function (frm, cdt, cdn) {
+ frm.events.set_base_amount(frm, cdt, cdn);
+ },
+
+ expense_account: function (frm, cdt, cdn) {
+ frm.events.set_account_currency(frm, cdt, cdn);
+ }
+});
+
erpnext.buying.SubcontractingOrderController = class SubcontractingOrderController {
setup() {
this.frm.custom_make_buttons = {
diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.json b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.json
index c6e76c7..f98f559 100644
--- a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.json
+++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.json
@@ -19,6 +19,10 @@
"transaction_date",
"schedule_date",
"amended_from",
+ "accounting_dimensions_section",
+ "cost_center",
+ "dimension_col_break",
+ "project",
"address_and_contact_section",
"supplier_address",
"address_display",
@@ -422,12 +426,34 @@
"fieldtype": "Select",
"label": "Distribute Additional Costs Based On ",
"options": "Qty\nAmount"
+ },
+ {
+ "collapsible": 1,
+ "fieldname": "accounting_dimensions_section",
+ "fieldtype": "Section Break",
+ "label": "Accounting Dimensions"
+ },
+ {
+ "fieldname": "cost_center",
+ "fieldtype": "Link",
+ "label": "Cost Center",
+ "options": "Cost Center"
+ },
+ {
+ "fieldname": "dimension_col_break",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "project",
+ "fieldtype": "Link",
+ "label": "Project",
+ "options": "Project"
}
],
"icon": "fa fa-file-text",
"is_submittable": 1,
"links": [],
- "modified": "2022-04-11 21:02:44.097841",
+ "modified": "2022-08-15 14:08:49.204218",
"modified_by": "Administrator",
"module": "Subcontracting",
"name": "Subcontracting Order",
diff --git a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py
index 71cdc94..156f027 100644
--- a/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py
+++ b/erpnext/subcontracting/doctype/subcontracting_order/subcontracting_order.py
@@ -82,25 +82,6 @@
self.set_missing_values_in_supplied_items()
self.set_missing_values_in_items()
- def set_missing_values_in_additional_costs(self):
- if self.get("additional_costs"):
- self.total_additional_costs = sum(flt(item.amount) for item in self.get("additional_costs"))
-
- if self.total_additional_costs:
- if self.distribute_additional_costs_based_on == "Amount":
- total_amt = sum(flt(item.amount) for item in self.get("items"))
- for item in self.items:
- item.additional_cost_per_qty = (
- (item.amount * self.total_additional_costs) / total_amt
- ) / item.qty
- else:
- total_qty = sum(flt(item.qty) for item in self.get("items"))
- additional_cost_per_qty = self.total_additional_costs / total_qty
- for item in self.items:
- item.additional_cost_per_qty = additional_cost_per_qty
- else:
- self.total_additional_costs = 0
-
def set_missing_values_in_service_items(self):
for idx, item in enumerate(self.get("service_items")):
self.items[idx].service_cost_per_qty = item.amount / self.items[idx].qty
@@ -114,9 +95,7 @@
def set_missing_values_in_items(self):
total_qty = total = 0
for item in self.items:
- item.rate = (
- item.rm_cost_per_qty + item.service_cost_per_qty + (item.additional_cost_per_qty or 0)
- )
+ item.rate = item.rm_cost_per_qty + item.service_cost_per_qty + flt(item.additional_cost_per_qty)
item.amount = item.qty * item.rate
total_qty += flt(item.qty)
total += flt(item.amount)
@@ -187,7 +166,7 @@
total_required_qty = total_supplied_qty = 0
for item in self.supplied_items:
total_required_qty += item.required_qty
- total_supplied_qty += item.supplied_qty or 0
+ total_supplied_qty += flt(item.supplied_qty)
if total_supplied_qty:
status = "Partial Material Transferred"
if total_supplied_qty >= total_required_qty:
diff --git a/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json b/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json
index 291f47a..3675a4e 100644
--- a/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json
+++ b/erpnext/subcontracting/doctype/subcontracting_order_item/subcontracting_order_item.json
@@ -40,6 +40,10 @@
"manufacture_section",
"manufacturer",
"manufacturer_part_no",
+ "accounting_dimensions_section",
+ "cost_center",
+ "dimension_col_break",
+ "project",
"section_break_34",
"page_break"
],
@@ -304,13 +308,35 @@
"no_copy": 1,
"print_hide": 1,
"read_only": 1
+ },
+ {
+ "collapsible": 1,
+ "fieldname": "accounting_dimensions_section",
+ "fieldtype": "Section Break",
+ "label": "Accounting Dimensions"
+ },
+ {
+ "fieldname": "cost_center",
+ "fieldtype": "Link",
+ "label": "Cost Center",
+ "options": "Cost Center"
+ },
+ {
+ "fieldname": "dimension_col_break",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "project",
+ "fieldtype": "Link",
+ "label": "Project",
+ "options": "Project"
}
],
"idx": 1,
"index_web_pages_for_search": 1,
"istable": 1,
"links": [],
- "modified": "2022-04-11 21:28:06.585338",
+ "modified": "2022-08-15 14:25:45.177703",
"modified_by": "Administrator",
"module": "Subcontracting",
"name": "Subcontracting Order Item",
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js
index b2506cd..aff76eb 100644
--- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.js
@@ -3,6 +3,8 @@
frappe.provide('erpnext.buying');
+{% include 'erpnext/stock/landed_taxes_and_charges_common.js' %};
+
frappe.ui.form.on('Subcontracting Receipt', {
setup: (frm) => {
frm.get_field('supplied_items').grid.cannot_add_rows = true;
@@ -48,6 +50,13 @@
is_group: 0
}
}));
+
+ frm.set_query("expense_account", "items", function () {
+ return {
+ query: "erpnext.controllers.queries.get_expense_account",
+ filters: { 'company': frm.doc.company }
+ };
+ });
},
refresh: (frm) => {
@@ -121,6 +130,16 @@
},
});
+frappe.ui.form.on('Landed Cost Taxes and Charges', {
+ amount: function (frm, cdt, cdn) {
+ frm.events.set_base_amount(frm, cdt, cdn);
+ },
+
+ expense_account: function (frm, cdt, cdn) {
+ frm.events.set_account_currency(frm, cdt, cdn);
+ }
+});
+
frappe.ui.form.on('Subcontracting Receipt Item', {
item_code(frm) {
set_missing_values(frm);
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json
index e963814..84e9554 100644
--- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json
@@ -15,8 +15,13 @@
"company",
"posting_date",
"posting_time",
+ "set_posting_time",
"is_return",
"return_against",
+ "accounting_dimensions_section",
+ "cost_center",
+ "dimension_col_break",
+ "project",
"section_addresses",
"supplier_address",
"contact_person",
@@ -43,12 +48,14 @@
"raw_material_details",
"get_current_stock",
"supplied_items",
+ "additional_costs_section",
+ "distribute_additional_costs_based_on",
+ "additional_costs",
+ "total_additional_costs",
"section_break_46",
"in_words",
"bill_no",
"bill_date",
- "accounting_details_section",
- "provisional_expense_account",
"more_info",
"status",
"column_break_39",
@@ -132,6 +139,7 @@
"label": "Date",
"no_copy": 1,
"print_width": "100px",
+ "read_only_depends_on": "eval: !doc.set_posting_time",
"reqd": 1,
"search_index": 1,
"width": "100px"
@@ -144,6 +152,7 @@
"no_copy": 1,
"print_hide": 1,
"print_width": "100px",
+ "read_only_depends_on": "eval: !doc.set_posting_time",
"reqd": 1,
"width": "100px"
},
@@ -521,19 +530,6 @@
"read_only": 1
},
{
- "collapsible": 1,
- "fieldname": "accounting_details_section",
- "fieldtype": "Section Break",
- "label": "Accounting Details"
- },
- {
- "fieldname": "provisional_expense_account",
- "fieldtype": "Link",
- "hidden": 1,
- "label": "Provisional Expense Account",
- "options": "Account"
- },
- {
"default": "0",
"fieldname": "is_return",
"fieldtype": "Check",
@@ -569,11 +565,70 @@
{
"fieldname": "section_break_47",
"fieldtype": "Section Break"
+ },
+ {
+ "collapsible": 1,
+ "fieldname": "accounting_dimensions_section",
+ "fieldtype": "Section Break",
+ "label": "Accounting Dimensions "
+ },
+ {
+ "fieldname": "cost_center",
+ "fieldtype": "Link",
+ "label": "Cost Center",
+ "options": "Cost Center"
+ },
+ {
+ "fieldname": "dimension_col_break",
+ "fieldtype": "Column Break"
+ },
+ {
+ "fieldname": "project",
+ "fieldtype": "Link",
+ "label": "Project",
+ "options": "Project"
+ },
+ {
+ "collapsible": 1,
+ "collapsible_depends_on": "total_additional_costs",
+ "depends_on": "eval:(doc.docstatus == 0 || doc.total_additional_costs)",
+ "fieldname": "additional_costs_section",
+ "fieldtype": "Section Break",
+ "label": "Additional Costs"
+ },
+ {
+ "default": "Qty",
+ "fieldname": "distribute_additional_costs_based_on",
+ "fieldtype": "Select",
+ "label": "Distribute Additional Costs Based On ",
+ "options": "Qty\nAmount"
+ },
+ {
+ "fieldname": "additional_costs",
+ "fieldtype": "Table",
+ "label": "Additional Costs",
+ "options": "Landed Cost Taxes and Charges"
+ },
+ {
+ "fieldname": "total_additional_costs",
+ "fieldtype": "Currency",
+ "label": "Total Additional Costs",
+ "print_hide_if_no_value": 1,
+ "read_only": 1
+ },
+ {
+ "default": "0",
+ "depends_on": "eval:doc.docstatus==0",
+ "fieldname": "set_posting_time",
+ "fieldtype": "Check",
+ "label": "Edit Posting Date and Time",
+ "print_hide": 1
}
],
+ "in_create": 1,
"is_submittable": 1,
"links": [],
- "modified": "2022-04-18 13:15:12.011682",
+ "modified": "2022-08-22 17:30:40.827517",
"modified_by": "Administrator",
"module": "Subcontracting",
"name": "Subcontracting Receipt",
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py
index 0c4ec6f..021d9aa 100644
--- a/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.py
@@ -3,7 +3,7 @@
import frappe
from frappe import _
-from frappe.utils import cint, getdate, nowdate
+from frappe.utils import cint, flt, getdate, nowdate
from erpnext.controllers.subcontracting_controller import SubcontractingController
@@ -103,6 +103,7 @@
@frappe.whitelist()
def set_missing_values(self):
+ self.set_missing_values_in_additional_costs()
self.set_missing_values_in_supplied_items()
self.set_missing_values_in_items()
@@ -125,12 +126,12 @@
item.rm_cost_per_qty = item.rm_supp_cost / item.qty
rm_supp_cost.pop(item.name)
- if self.is_new() and item.rm_supp_cost > 0:
+ if item.recalculate_rate:
item.rate = (
- item.rm_cost_per_qty + (item.service_cost_per_qty or 0) + item.additional_cost_per_qty
+ flt(item.rm_cost_per_qty) + flt(item.service_cost_per_qty) + flt(item.additional_cost_per_qty)
)
- item.received_qty = item.qty + (item.rejected_qty or 0)
+ item.received_qty = item.qty + flt(item.rejected_qty)
item.amount = item.qty * item.rate
total_qty += item.qty
total_amount += item.amount
diff --git a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json
index e2785ce..fd86895 100644
--- a/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json
+++ b/erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json
@@ -29,6 +29,7 @@
"rate_and_amount",
"rate",
"amount",
+ "recalculate_rate",
"column_break_19",
"rm_cost_per_qty",
"service_cost_per_qty",
@@ -49,15 +50,16 @@
"col_break5",
"batch_no",
"rejected_serial_no",
- "expense_account",
"manufacture_details",
"manufacturer",
"column_break_16",
"manufacturer_part_no",
+ "accounting_details_section",
+ "expense_account",
"accounting_dimensions_section",
- "project",
- "dimension_col_break",
"cost_center",
+ "dimension_col_break",
+ "project",
"section_break_80",
"page_break"
],
@@ -192,6 +194,8 @@
"label": "Rate",
"options": "currency",
"print_width": "100px",
+ "read_only": 1,
+ "read_only_depends_on": "eval: doc.recalculate_rate",
"width": "100px"
},
{
@@ -363,10 +367,8 @@
{
"fieldname": "expense_account",
"fieldtype": "Link",
- "hidden": 1,
"label": "Expense Account",
- "options": "Account",
- "read_only": 1
+ "options": "Account"
},
{
"collapsible": 1,
@@ -456,12 +458,23 @@
"no_copy": 1,
"print_hide": 1,
"read_only": 1
+ },
+ {
+ "fieldname": "accounting_details_section",
+ "fieldtype": "Section Break",
+ "label": "Accounting Details"
+ },
+ {
+ "default": "1",
+ "fieldname": "recalculate_rate",
+ "fieldtype": "Check",
+ "label": "Recalculate Rate"
}
],
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2022-04-21 12:07:55.899701",
+ "modified": "2022-08-20 17:16:48.269164",
"modified_by": "Administrator",
"module": "Subcontracting",
"name": "Subcontracting Receipt Item",
diff --git a/erpnext/translations/de.csv b/erpnext/translations/de.csv
index d6bceb3..ca16403 100644
--- a/erpnext/translations/de.csv
+++ b/erpnext/translations/de.csv
@@ -196,7 +196,7 @@
All the mandatory Task for employee creation hasn't been done yet.,Alle obligatorischen Aufgaben zur Mitarbeitererstellung wurden noch nicht erledigt.,
Allocate Payment Amount,Zahlungsbetrag zuweisen,
Allocated Amount,Zugewiesene Menge,
-Allocated Leaves,Zugewiesene Blätter,
+Allocated Leaves,Zugewiesene Urlaubstage,
Allocating leaves...,Blätter zuordnen...,
Already record exists for the item {0},Es existiert bereits ein Datensatz für den Artikel {0},
"Already set default in pos profile {0} for user {1}, kindly disabled default","Im Standardprofil {0} für den Benutzer {1} ist der Standard bereits festgelegt, standardmäßig deaktiviert",
@@ -8623,8 +8623,8 @@
Select warehouse for material requests,Wählen Sie Lager für Materialanfragen,
Transfer Materials For Warehouse {0},Material für Lager übertragen {0},
Production Plan Material Request Warehouse,Produktionsplan Materialanforderungslager,
-Sets 'Source Warehouse' in each row of the items table.,Legt 'Source Warehouse' in jeder Zeile der Artikeltabelle fest.,
-Sets 'Target Warehouse' in each row of the items table.,"Füllt das Feld ""Ziel Lager"" in allen Positionen der folgenden Tabelle.",
+Sets 'Source Warehouse' in each row of the items table.,Legt in jeder Zeile der Artikeltabelle das „Ausgangslager“ fest.,
+Sets 'Target Warehouse' in each row of the items table.,Legt in jeder Zeile der Artikeltabelle das „Eingangslager“ fest.,
Show Cancelled Entries,Abgebrochene Einträge anzeigen,
Backdated Stock Entry,Backdated Stock Entry,
Row #{}: Currency of {} - {} doesn't matches company currency.,Zeile # {}: Die Währung von {} - {} stimmt nicht mit der Firmenwährung überein.,
@@ -9871,3 +9871,31 @@
From Lead,Aus Lead,
From Opportunity,Aus Chance,
Publish in Website,Auf Webseite veröffentlichen,
+Total Allocated Leave(s),Gesamte zugewiesene Urlaubstage,
+Expired Leave(s),Verfallene Urlaubstage,
+Used Leave(s),Verbrauchte Urlaubstage,
+Leave(s) Pending Approval,Urlaubstage zur Genehmigung ausstehend,
+Available Leave(s),Verfügbare Urlaubstage,
+Party Specific Item,Parteispezifischer Artikel,
+Active Customers,Aktive Kunden,
+Annual Sales,Jährlicher Umsatz,
+Total Outgoing Bills,Ausgangsrechnungen insgesamt,
+Total Incoming Bills,Eingangsrechnungen insgesamt,
+Total Incoming Payment,Zahlungseingang insgesamt,
+Total Outgoing Payment,Zahlungsausgang insgesamt,
+Incoming Bills (Purchase Invoice),Eingehende Rechnungen (Eingangsrechnung),
+Outgoing Bills (Sales Invoice),Ausgehende Rechnungen (Ausgangsrechnung),
+Accounts Receivable Ageing,Fälligkeit Forderungen,
+Accounts Payable Ageing,Fälligkeit Verbindlichkeiten,
+Budget Variance,Budgetabweichung,
+Based On Value,Basierend auf Wert,
+Restrict Items Based On,Artikel einschränken auf Basis von,
+Earnings & Deductions,Erträge & Abzüge,
+Is Process Loss,Ist Prozessverlust,
+Is Finished Item,Ist fertiger Artikel,
+Is Scrap Item,Ist Schrott,
+Issue a debit note with 0 qty against an existing Sales Invoice,Lastschrift mit Menge 0 gegen eine bestehende Ausgangsrechnung ausstellen,
+Show Remarks,Bemerkungen anzeigen,
+Website Item,Webseiten-Artikel,
+Update Property,Eigenschaft aktualisieren,
+Recurring Sales Invoice,Wiederkehrende Ausgangsrechnung,
diff --git a/erpnext/translations/fr.csv b/erpnext/translations/fr.csv
index dbc3194..b207461 100644
--- a/erpnext/translations/fr.csv
+++ b/erpnext/translations/fr.csv
@@ -329,11 +329,11 @@
Avg Daily Outgoing,Moy Quotidienne Sortante,
Avg. Buying Price List Rate,Moyenne de la liste de prix d'achat,
Avg. Selling Price List Rate,Prix moyen de la liste de prix de vente,
-Avg. Selling Rate,Moy. Taux de vente,
+Avg. Selling Rate,Moy. prix de vente,
BOM,Nomenclature,
BOM Browser,Explorateur Nomenclature,
BOM No,N° Nomenclature,
-BOM Rate,Valeur nomenclature,
+BOM Rate,Cout nomenclature,
BOM Stock Report,Rapport de Stock des nomenclatures,
BOM and Manufacturing Quantity are required,Nomenclature et quantité de production sont nécessaires,
BOM does not contain any stock item,Nomenclature ne contient aucun article en stock,
@@ -561,9 +561,9 @@
Combined invoice portion must equal 100%,La portion combinée de la facture doit être égale à 100%,
Commercial,Commercial,
Commission,Commission,
-Commission Rate %,Taux de commission%,
+Commission Rate %,Pourcentage de commission,
Commission on Sales,Commission sur les ventes,
-Commission rate cannot be greater than 100,Taux de commission ne peut pas être supérieure à 100,
+Commission rate cannot be greater than 100,Pourcentage de commission ne peut pas être supérieure à 100,
Community Forum,Forum de la communauté,
Company (not Customer or Supplier) master.,Données de base de la Société (ni les Clients ni les Fournisseurs),
Company Abbreviation,Abréviation de la Société,
@@ -658,7 +658,7 @@
Create Invoices,Créer des factures,
Create Job Card,Créer une carte de travail,
Create Journal Entry,Créer une entrée de journal,
-Create Lead,Créer une piste,
+Create Lead,Créer un Prospect,
Create Leads,Créer des Prospects,
Create Maintenance Visit,Créer une visite de maintenance,
Create Material Request,Créer une demande de matériel,
@@ -1072,7 +1072,7 @@
"For an item {0}, quantity must be negative number","Pour l'article {0}, la quantité doit être un nombre négatif",
"For an item {0}, quantity must be positive number","Pour un article {0}, la quantité doit être un nombre positif",
"For job card {0}, you can only make the 'Material Transfer for Manufacture' type stock entry","Pour la carte de travail {0}, vous pouvez uniquement saisir une entrée de stock de type "Transfert d'article pour fabrication".",
-"For row {0} in {1}. To include {2} in Item rate, rows {3} must also be included","Pour la ligne {0} dans {1}. Pour inclure {2} dans le prix de l'Article, les lignes {3} doivent également être incluses",
+"For row {0} in {1}. To include {2} in Item rate, rows {3} must also be included","Pour la ligne {0} dans {1}. Pour inclure {2} dans le prix de l'article, les lignes {3} doivent également être incluses",
For row {0}: Enter Planned Qty,Pour la ligne {0}: entrez la quantité planifiée,
"For {0}, only credit accounts can be linked against another debit entry","Pour {0}, seuls les comptes de crédit peuvent être liés avec une autre écriture de débit",
"For {0}, only debit accounts can be linked against another credit entry","Pour {0}, seuls les comptes de débit peuvent être liés avec une autre écriture de crédit",
@@ -1235,7 +1235,7 @@
Identifying Decision Makers,Identifier les décideurs,
"If Auto Opt In is checked, then the customers will be automatically linked with the concerned Loyalty Program (on save)","Si l'option adhésion automatique est cochée, les clients seront automatiquement liés au programme de fidélité concerné (après l'enregistrement)",
"If multiple Pricing Rules continue to prevail, users are asked to set Priority manually to resolve conflict.","Si plusieurs Règles de Prix continuent de prévaloir, les utilisateurs sont invités à définir manuellement la priorité pour résoudre les conflits.",
-"If selected Pricing Rule is made for 'Rate', it will overwrite Price List. Pricing Rule rate is the final rate, so no further discount should be applied. Hence, in transactions like Sales Order, Purchase Order etc, it will be fetched in 'Rate' field, rather than 'Price List Rate' field.","Si la règle de tarification sélectionnée est définie pour le «Prix Unitaire», elle écrase la liste de prix. Le prix unitaire de la règle de tarification est le prix unitaire final, donc aucune autre réduction supplémentaire ne doit être appliquée. Par conséquent, dans les transactions telles que la commande client, la commande d'achat, etc., elle sera récupérée dans le champ ""Prix Unitaire"", plutôt que dans le champ ""Tarif de la liste de prix"".",
+"If selected Pricing Rule is made for 'Rate', it will overwrite Price List. Pricing Rule rate is the final rate, so no further discount should be applied. Hence, in transactions like Sales Order, Purchase Order etc, it will be fetched in 'Rate' field, rather than 'Price List Rate' field.","Si la règle de tarification sélectionnée est définie pour le 'Prix Unitaire', elle écrase la liste de prix. Le prix unitaire de la règle de tarification est le prix unitaire final, donc aucune autre réduction supplémentaire ne doit être appliquée. Par conséquent, dans les transactions telles que la commande client, la commande d'achat, etc., elle sera récupérée dans le champ 'Prix Unitaire', plutôt que dans le champ 'Tarif de la liste de prix'.",
"If two or more Pricing Rules are found based on the above conditions, Priority is applied. Priority is a number between 0 to 20 while default value is zero (blank). Higher number means it will take precedence if there are multiple Pricing Rules with same conditions.","Si deux Règles de Prix ou plus sont trouvées sur la base des conditions ci-dessus, une Priorité est appliquée. La Priorité est un nombre compris entre 0 et 20 avec une valeur par défaut de zéro (vide). Les nombres les plus élévés sont prioritaires s'il y a plusieurs Règles de Prix avec mêmes conditions.",
"If unlimited expiry for the Loyalty Points, keep the Expiry Duration empty or 0.","Si vous souhaitez ne pas mettre de date d'expiration pour les points de fidélité, laissez la durée d'expiration vide ou mettez 0.",
"If you have any questions, please get back to us.","Si vous avez des questions, veuillez revenir vers nous.",
@@ -1269,7 +1269,7 @@
Income Account,Compte de Produits,
Income Tax,Impôt sur le revenu,
Incoming,Entrant,
-Incoming Rate,Taux d'Entrée,
+Incoming Rate,Prix d'Entrée,
Incorrect number of General Ledger Entries found. You might have selected a wrong Account in the transaction.,Nombre incorrect d'Écritures Grand Livre trouvées. Vous avez peut-être choisi le mauvais Compte dans la transaction.,
Increment cannot be 0,Incrément ne peut pas être 0,
Increment for Attribute {0} cannot be 0,Incrément pour l'Attribut {0} ne peut pas être 0,
@@ -1365,7 +1365,7 @@
Item Variants updated,Variantes d'article mises à jour,
Item has variants.,L'article a des variantes.,
Item must be added using 'Get Items from Purchase Receipts' button,L'article doit être ajouté à l'aide du bouton 'Obtenir des éléments de Reçus d'Achat',
-Item valuation rate is recalculated considering landed cost voucher amount,Le taux d'évaluation de l'article est recalculé compte tenu du montant du bon de prix au débarquement,
+Item valuation rate is recalculated considering landed cost voucher amount,Le taux de valorisation de l'article est recalculé compte tenu du montant du bon de prix au débarquement,
Item variant {0} exists with same attributes,La variante de l'article {0} existe avec les mêmes caractéristiques,
Item {0} does not exist,Article {0} n'existe pas,
Item {0} does not exist in the system or has expired,L'article {0} n'existe pas dans le système ou a expiré,
@@ -2147,7 +2147,7 @@
Price,Prix,
Price List,Liste de prix,
Price List Currency not selected,Devise de la Liste de Prix non sélectionnée,
-Price List Rate,Taux de la Liste des Prix,
+Price List Rate,Prix de la Liste des Prix,
Price List master.,Données de Base des Listes de Prix,
Price List must be applicable for Buying or Selling,La Liste de Prix doit être applicable pour les Achats et les Ventes,
Price List {0} is disabled or does not exist,Liste des Prix {0} est désactivée ou n'existe pas,
@@ -2288,8 +2288,8 @@
Quotes to Leads or Customers.,Devis de Prospects ou Clients.,
RFQs are not allowed for {0} due to a scorecard standing of {1},Les Appels d'Offres ne sont pas autorisés pour {0} en raison d'une note de {1} sur la fiche d'évaluation,
Range,Plage,
-Rate,Taux,
-Rate:,Taux:,
+Rate,Prix,
+Rate:,Prix:,
Rating,Évaluation,
Raw Material,Matières Premières,
Raw Materials,Matières premières,
@@ -2426,7 +2426,7 @@
Row # {0}: ,Ligne # {0} :,
Row # {0}: Batch No must be same as {1} {2},Ligne # {0} : Le N° de Lot doit être le même que {1} {2},
Row # {0}: Cannot return more than {1} for Item {2},Ligne # {0} : Vous ne pouvez pas retourner plus de {1} pour l’Article {2},
-Row # {0}: Rate cannot be greater than the rate used in {1} {2},Ligne # {0}: Le Taux ne peut pas être supérieur au taux utilisé dans {1} {2},
+Row # {0}: Rate cannot be greater than the rate used in {1} {2},Ligne # {0}: Le prix ne peut pas être supérieur au prix utilisé dans {1} {2},
Row # {0}: Serial No is mandatory,Ligne # {0} : N° de série est obligatoire,
Row # {0}: Serial No {1} does not match with {2} {3},Ligne # {0} : N° de série {1} ne correspond pas à {2} {3},
Row #{0} (Payment Table): Amount must be negative,Row # {0} (Table de paiement): le montant doit être négatif,
@@ -2434,7 +2434,7 @@
Row #{0}: Account {1} does not belong to company {2},Ligne # {0}: le compte {1} n'appartient pas à la société {2},
Row #{0}: Allocated Amount cannot be greater than outstanding amount.,Ligne # {0}: montant attribué ne peut pas être supérieur au montant en souffrance.,
"Row #{0}: Asset {1} cannot be submitted, it is already {2}","Ligne #{0} : L’Actif {1} ne peut pas être soumis, il est déjà {2}",
-Row #{0}: Cannot set Rate if amount is greater than billed amount for Item {1}.,Ligne n ° {0}: impossible de définir le tarif si le montant est supérieur au montant facturé pour l'élément {1}.,
+Row #{0}: Cannot set Rate if amount is greater than billed amount for Item {1}.,Ligne n ° {0}: impossible de définir le prix si le montant est supérieur au montant facturé pour l'élément {1}.,
Row #{0}: Clearance date {1} cannot be before Cheque Date {2},Ligne #{0} : Date de compensation {1} ne peut pas être antérieure à la Date du Chèque {2},
Row #{0}: Duplicate entry in References {1} {2},Ligne # {0}: entrée en double dans les références {1} {2},
Row #{0}: Expected Delivery Date cannot be before Purchase Order Date,Ligne {0}: la date de livraison prévue ne peut pas être avant la date de commande,
@@ -2898,7 +2898,7 @@
Syntax error in condition: {0},Erreur de syntaxe dans la condition: {0},
Syntax error in formula or condition: {0},Erreur de syntaxe dans la formule ou condition : {0},
System Manager,Responsable Système,
-TDS Rate %,Taux de TDS%,
+TDS Rate %,Pourcentage de TDS,
Tap items to add them here,Choisissez des articles pour les ajouter ici,
Target,Cible,
Target ({}),Cible ({}),
@@ -3190,7 +3190,7 @@
Update Response,Mettre à jour la réponse,
Update bank payment dates with journals.,Mettre à jour les dates de paiement bancaires avec les journaux.,
Update in progress. It might take a while.,Mise à jour en cours. Ça peut prendre un moment.,
-Update rate as per last purchase,Taux de mise à jour selon le dernier achat,
+Update rate as per last purchase,Mettre à jour les prix selon le dernier prix achat,
Update stock must be enable for the purchase invoice {0},La mise à jour du stock doit être activée pour la facture d'achat {0},
Updating Variants...,Mise à jour des variantes ...,
Upload your letter head and logo. (you can edit them later).,Charger votre en-tête et logo. (vous pouvez les modifier ultérieurement).,
@@ -3326,7 +3326,6 @@
You are not authorized to approve leaves on Block Dates,Vous n'êtes pas autorisé à approuver les congés sur les Dates Bloquées,
You are not authorized to set Frozen value,Vous n'êtes pas autorisé à définir des valeurs gelées,
You are not present all day(s) between compensatory leave request days,Vous n'êtes pas présent(e) tous les jours vos demandes de congé compensatoire,
-You can not change rate if BOM mentioned agianst any item,Vous ne pouvez pas modifier le taux si la nomenclature est mentionnée pour un article,
You can not enter current voucher in 'Against Journal Entry' column,Vous ne pouvez pas entrer le bon actuel dans la colonne 'Pour l'Écriture de Journal',
You can only have Plans with the same billing cycle in a Subscription,Vous ne pouvez avoir que des plans ayant le même cycle de facturation dans le même abonnement,
You can only redeem max {0} points in this order.,Vous pouvez uniquement échanger un maximum de {0} points dans cet commande.,
@@ -3882,7 +3881,7 @@
Only users with the {0} role can create backdated leave applications,Seuls les utilisateurs avec le rôle {0} peuvent créer des demandes de congé antidatées,
Open,Ouvert,
Open Contact,Contact ouvert,
-Open Lead,Ouvrir le fil,
+Open Lead,Ouvrir le Prospect,
Opening and Closing,Ouverture et fermeture,
Operating Cost as per Work Order / BOM,Coût d'exploitation selon l'ordre de fabrication / nomenclature,
Order Amount,Montant de la commande,
@@ -3971,7 +3970,7 @@
Quick Entry,Écriture Rapide,
Quiz {0} does not exist,Le questionnaire {0} n'existe pas,
Quotation Amount,Montant du devis,
-Rate or Discount is required for the price discount.,Le taux ou la remise est requis pour la remise de prix.,
+Rate or Discount is required for the price discount.,Le prix ou la remise est requis pour la remise.,
Reason,Raison,
Reconcile Entries,Réconcilier les entrées,
Reconcile this account,Réconcilier ce compte,
@@ -4348,7 +4347,7 @@
Valid From date not in Fiscal Year {0},Date de début de validité non comprise dans l'exercice {0},
Valid Upto date not in Fiscal Year {0},Valable jusqu'à la date hors exercice {0},
Group Roll No,Groupe Roll Non,
-Maintain Same Rate Throughout Sales Cycle,Maintenir le Même Taux Durant le Cycle de Vente,
+Maintain Same Rate Throughout Sales Cycle,Maintenir le même prix Durant le Cycle de Vente,
"Row {1}: Quantity ({0}) cannot be a fraction. To allow this, disable '{2}' in UOM {3}.","Ligne {1}: la quantité ({0}) ne peut pas être une fraction. Pour autoriser cela, désactivez «{2}» dans UdM {3}.",
Must be Whole Number,Doit être un Nombre Entier,
Please setup Razorpay Plan ID,Veuillez configurer l'ID du plan Razorpay,
@@ -4961,7 +4960,7 @@
System will notify to increase or decrease quantity or amount ,Le système notifiera d'augmenter ou de diminuer la quantité ou le montant,
"Higher the number, higher the priority","Plus le nombre est grand, plus la priorité est haute",
Apply Multiple Pricing Rules,Appliquer plusieurs règles de tarification,
-Apply Discount on Rate,Appliquer une réduction sur le taux,
+Apply Discount on Rate,Appliquer une réduction sur le prix,
Validate Applied Rule,Valider la règle appliquée,
Rule Description,Description de la règle,
Pricing Rule Help,Aide pour les Règles de Tarification,
@@ -5050,19 +5049,18 @@
Update Auto Repeat Reference,Mettre à jour la référence de répétition automatique,
Purchase Invoice Advance,Avance sur Facture d’Achat,
Purchase Invoice Item,Article de la Facture d'Achat,
-Quantity and Rate,Quantité et Taux,
+Quantity and Rate,Quantité et Prix,
Received Qty,Qté Reçue,
Accepted Qty,Quantité acceptée,
Rejected Qty,Qté Rejetée,
UOM Conversion Factor,Facteur de Conversion de l'UDM,
Discount on Price List Rate (%),Remise sur la Liste des Prix (%),
Price List Rate (Company Currency),Taux de la Liste de Prix (Devise Société),
-Rate ,Taux,
Rate (Company Currency),Prix (Devise Société),
Amount (Company Currency),Montant (Devise de la Société),
Is Free Item,Est un article gratuit,
-Net Rate,Taux Net,
-Net Rate (Company Currency),Taux Net (Devise Société),
+Net Rate,Prix Net,
+Net Rate (Company Currency),Prix Net (Devise Société),
Net Amount (Company Currency),Montant Net (Devise Société),
Item Tax Amount Included in Value,Montant de la taxe incluse dans la valeur,
Landed Cost Voucher Amount,Montant de la Référence de Coût au Débarquement,
@@ -5080,7 +5078,7 @@
Service Start Date,Date de début du service,
Service End Date,Date de fin du service,
Allow Zero Valuation Rate,Autoriser un Taux de Valorisation Égal à Zéro,
-Item Tax Rate,Taux de la Taxe sur l'Article,
+Item Tax Rate,Prix de la Taxe sur l'Article,
Tax detail table fetched from item master as a string and stored in this field.\nUsed for Taxes and Charges,La table de détails de taxe est récupérée depuis les données de base de l'article comme une chaîne de caractères et stockée dans ce champ. Elle est utilisée pour les Taxes et Frais.,
Purchase Order Item,Article de la Commande d'Achat,
Purchase Receipt Detail,Détail du reçu d'achat,
@@ -5098,8 +5096,8 @@
On Previous Row Total,Le Total de la Rangée Précédente,
On Item Quantity,Sur quantité d'article,
Reference Row #,Ligne de Référence #,
-Is this Tax included in Basic Rate?,Cette Taxe est-elle incluse dans le Taux de Base ?,
-"If checked, the tax amount will be considered as already included in the Print Rate / Print Amount","Si cochée, le montant de la taxe sera considéré comme déjà inclus dans le Taux d'Impression / Prix d'Impression",
+Is this Tax included in Basic Rate?,Cette Taxe est-elle incluse dans le Prix de Base ?,
+"If checked, the tax amount will be considered as already included in the Print Rate / Print Amount","Si cochée, le montant de la taxe sera considéré comme déjà inclus dans le Taux / Prix des documents (PDF, impressions)",
Account Head,Compte Principal,
Tax Amount After Discount Amount,Montant de la Taxe après Remise,
Item Wise Tax Detail ,Détail de la taxe de l'article Wise,
@@ -5147,7 +5145,7 @@
Debit To,Débit Pour,
Is Opening Entry,Est Écriture Ouverte,
C-Form Applicable,Formulaire-C Applicable,
-Commission Rate (%),Taux de Commission (%),
+Commission Rate (%),Pourcentage de Commission,
Sales Team1,Équipe des Ventes 1,
Against Income Account,Pour le Compte de Produits,
Sales Invoice Advance,Avance sur Facture de Vente,
@@ -5157,9 +5155,9 @@
Brand Name,Nom de la Marque,
Qty as per Stock UOM,Qté par UDM du Stock,
Discount and Margin,Remise et Marge,
-Rate With Margin,Tarif Avec Marge,
-Discount (%) on Price List Rate with Margin,Remise (%) sur le Tarif de la Liste de Prix avec la Marge,
-Rate With Margin (Company Currency),Taux avec marge (devise de l'entreprise),
+Rate With Margin,Prix Avec Marge,
+Discount (%) on Price List Rate with Margin,Remise (%) sur le prix de la Liste de Prix avec la Marge,
+Rate With Margin (Company Currency),Prix avec marge (devise de l'entreprise),
Delivered By Supplier,Livré par le Fournisseur,
Deferred Revenue,Produits comptabilisés d'avance,
Deferred Revenue Account,Compte de produits comptabilisés d'avance,
@@ -5721,7 +5719,7 @@
Enter name of campaign if source of enquiry is campaign,Entrez le nom de la campagne si la source de l'enquête est une campagne,
Opportunity Date,Date d'Opportunité,
Opportunity Item,Article de l'Opportunité,
-Basic Rate,Taux de Base,
+Basic Rate,Prix de Base,
Stage Name,Nom de scène,
Social Media Post,Publication sur les réseaux sociaux,
Post Status,Statut du message,
@@ -7218,8 +7216,8 @@
Include Item In Manufacturing,Inclure l'article dans la fabrication,
BOM Item,Article de la nomenclature,
Item operation,Opération de l'article,
-Rate & Amount,Taux et Montant,
-Basic Rate (Company Currency),Taux de Base (Devise de la Société ),
+Rate & Amount,Prix et Montant,
+Basic Rate (Company Currency),Prix de Base (Devise de la Société ),
Scrap %,% de Rebut,
Original Item,Article original,
BOM Operation,Opération de la nomenclature (gamme),
@@ -7464,8 +7462,8 @@
Attribute,Attribut,
Website Filter Field,Champ de filtrage de site Web,
Activity Cost,Coût de l'Activité,
-Billing Rate,Taux de Facturation,
-Costing Rate,Taux des Coûts,
+Billing Rate,Prix de Facturation,
+Costing Rate,Tarifs des Coûts,
title,Titre,
Projects User,Utilisateur/Intervenant Projets,
Default Costing Rate,Coût de Revient par Défaut,
@@ -7963,7 +7961,7 @@
Actual Quantity,Quantité Réelle,
Requested Quantity,Quantité Demandée,
Reserved Qty for sub contract,Qté réservée pour le sous-contrat,
-Moving Average Rate,Taux Mobile Moyen,
+Moving Average Rate,Prix moyen pondéré,
FCFS Rate,Montant PAPS,
Customs Tariff Number,Tarifs Personnalisés,
Tariff Number,Tarif,
@@ -8311,7 +8309,7 @@
Customer or Supplier Details,Détails du Client ou du Fournisseur,
Per Transferred,Par transféré,
Stock Entry Detail,Détails de l'Écriture de Stock,
-Basic Rate (as per Stock UOM),Taux de base (comme l’UDM du Stock),
+Basic Rate (as per Stock UOM),Prix de base (comme l’UDM du Stock),
Basic Amount,Montant de Base,
Additional Cost,Frais Supplémentaire,
Serial No / Batch,N° de Série / Lot,
@@ -8323,7 +8321,7 @@
PO Supplied Item,PO article fourni,
Reference Purchase Receipt,Reçu d'achat de référence,
Stock Ledger Entry,Écriture du Livre d'Inventaire,
-Outgoing Rate,Taux Sortant,
+Outgoing Rate,Prix Sortant,
Actual Qty After Transaction,Qté Réelle Après Transaction,
Stock Value Difference,Différence de Valeur du Sock,
Stock Queue (FIFO),File d'Attente du Stock (FIFO),
@@ -8509,7 +8507,7 @@
Item Prices,Prix des Articles,
Item Shortage Report,Rapport de Rupture de Stock d'Article,
Item Variant Details,Détails de la variante de l'article,
-Item-wise Price List Rate,Taux de la Liste des Prix par Article,
+Item-wise Price List Rate,Prix de la Liste des Prix par Article,
Item-wise Purchase History,Historique d'Achats par Article,
Item-wise Purchase Register,Registre des Achats par Article,
Item-wise Sales History,Historique des Ventes par Article,
@@ -8561,7 +8559,7 @@
Sales Partner Transaction Summary,Récapitulatif des transactions du partenaire commercial,
Sales Partners Commission,Commission des Partenaires de Vente,
Invoiced Amount (Exclusive Tax),Montant facturé (taxe exclusive),
-Average Commission Rate,Taux Moyen de la Commission,
+Average Commission Rate,Coût Moyen de la Commission,
Sales Payment Summary,Résumé du paiement des ventes,
Sales Person Commission Summary,Récapitulatif de la commission des ventes,
Sales Person Target Variance Based On Item Group,Écart cible du commercial basé sur le groupe de postes,
@@ -8815,7 +8813,7 @@
New invoices will be generated as per schedule even if current invoices are unpaid or past due date,"De nouvelles factures seront générées selon le calendrier, même si les factures actuelles sont impayées ou en retard",
Document Type ,Type de document,
Subscription Price Based On,Prix d'abonnement basé sur,
-Fixed Rate,Taux fixe,
+Fixed Rate,Tarif fixe,
Based On Price List,Basé sur la liste de prix,
Monthly Rate,Tarif mensuel,
Cancel Subscription After Grace Period,Annuler l'abonnement après la période de grâce,
@@ -8886,10 +8884,6 @@
Enter a name for the Clinical Procedure Template,Entrez un nom pour le modèle de procédure clinique,
Set the Item Code which will be used for billing the Clinical Procedure.,Définissez le code article qui sera utilisé pour facturer la procédure clinique.,
Select an Item Group for the Clinical Procedure Item.,Sélectionnez un groupe d'articles pour l'article de procédure clinique.,
-Clinical Procedure Rate,Taux de procédure clinique,
-Check this if the Clinical Procedure is billable and also set the rate.,Cochez cette case si la procédure clinique est facturable et définissez également le tarif.,
-Check this if the Clinical Procedure utilises consumables. Click ,Vérifiez ceci si la procédure clinique utilise des consommables. Cliquez sur,
- to know more,en savoir plus,
"You can also set the Medical Department for the template. After saving the document, an Item will automatically be created for billing this Clinical Procedure. You can then use this template while creating Clinical Procedures for Patients. Templates save you from filling up redundant data every single time. You can also create templates for other operations like Lab Tests, Therapy Sessions, etc.","Vous pouvez également définir le service médical du modèle. Après avoir enregistré le document, un élément sera automatiquement créé pour facturer cette procédure clinique. Vous pouvez ensuite utiliser ce modèle lors de la création de procédures cliniques pour les patients. Les modèles vous évitent de remplir des données redondantes à chaque fois. Vous pouvez également créer des modèles pour d'autres opérations telles que des tests de laboratoire, des séances de thérapie, etc.",
Descriptive Test Result,Résultat du test descriptif,
Allow Blank,Autoriser le blanc,
@@ -9033,7 +9027,7 @@
This Warehouse will be auto-updated in the Work In Progress Warehouse field of Work Orders.,Cet entrepôt sera mis à jour automatiquement dans le champ Entrepôt de travaux en cours des bons de travail.,
Finished Goods Warehouse,Entrepôt de produits finis,
This Warehouse will be auto-updated in the Target Warehouse field of Work Order.,Cet entrepôt sera mis à jour automatiquement dans le champ Entrepôt cible de l'ordre de fabrication.
-"If ticked, the BOM cost will be automatically updated based on Valuation Rate / Price List Rate / last purchase rate of raw materials.","Si coché, le coût de la nomenclature sera automatiquement mis à jour en fonction du taux de valorisation / tarif tarifaire / dernier taux d'achat des matières premières.",
+"If ticked, the BOM cost will be automatically updated based on Valuation Rate / Price List Rate / last purchase rate of raw materials.","Si coché, le coût de la nomenclature sera automatiquement mis à jour en fonction du taux de valorisation / prix de la liste prix / dernier prix d'achat des matières premières.",
Source Warehouses (Optional),Entrepôts d'origine (facultatif),
"System will pickup the materials from the selected warehouses. If not specified, system will create material request for purchase.","Le système ramassera les matériaux dans les entrepôts sélectionnés. S'il n'est pas spécifié, le système créera une demande de matériel pour l'achat.",
Lead Time,Délai de mise en œuvre,
@@ -9107,7 +9101,7 @@
Track this Purchase Receipt against any Project,Suivre ce reçu d'achat par rapport à n'importe quel projet,
Please Select a Supplier,Veuillez sélectionner un fournisseur,
Add to Transit,Ajouter à Transit,
-Set Basic Rate Manually,Définir manuellement le taux de base,
+Set Basic Rate Manually,Définir manuellement le prix de base,
"By default, the Item Name is set as per the Item Code entered. If you want Items to be named by a ","Par défaut, le nom de l'article est défini selon le code d'article entré. Si vous souhaitez que les éléments soient nommés par un",
Set a Default Warehouse for Inventory Transactions. This will be fetched into the Default Warehouse in the Item master.,Définissez un entrepôt par défaut pour les mouvements de stock. Ce sera récupéré dans l'entrepôt par défaut dans la base d'articles.,
"This will allow stock items to be displayed in negative values. Using this option depends on your use case. With this option unchecked, the system warns before obstructing a transaction that is causing negative stock.","Cela permettra aux articles en stock d'être affichés avec des valeurs négatives. L'utilisation de cette option dépend de votre cas d'utilisation. Lorsque cette option n'est pas cochée, le système avertit avant d'entraver une transaction entraînant un stock négatif.",
@@ -9495,7 +9489,7 @@
Row #{0}: Check Out datetime cannot be less than Check In datetime,Ligne n ° {0}: la date de sortie ne peut pas être inférieure à la date de sortie,
"Missing required details, did not create Inpatient Record","Détails requis manquants, n'a pas créé de dossier d'hospitalisation",
Unbilled Invoices,Factures non facturées,
-Standard Selling Rate should be greater than zero.,Le taux de vente standard doit être supérieur à zéro.,
+Standard Selling Rate should be greater than zero.,Le prix de vente standard doit être supérieur à zéro.,
Conversion Factor is mandatory,Le facteur de conversion est obligatoire,
Row #{0}: Conversion Factor is mandatory,Ligne n ° {0}: le facteur de conversion est obligatoire,
Sample Quantity cannot be negative or 0,La quantité d'échantillon ne peut pas être négative ou 0,
@@ -9559,7 +9553,7 @@
Regards,Cordialement,
Please click on the following button to set your new password,Veuillez cliquer sur le bouton suivant pour définir votre nouveau mot de passe,
Update Password,Mettre à jour le mot de passe,
-Row #{}: Selling rate for item {} is lower than its {}. Selling {} should be atleast {},Ligne n ° {}: le taux de vente de l'article {} est inférieur à son {}. La vente {} doit être au moins {},
+Row #{}: Selling rate for item {} is lower than its {}. Selling {} should be atleast {},Ligne n ° {}: le prix de vente de l'article {} est inférieur à son {}. La vente {} doit être au moins {},
You can alternatively disable selling price validation in {} to bypass this validation.,Vous pouvez également désactiver la validation du prix de vente dans {} pour contourner cette validation.,
Invalid Selling Price,Prix de vente invalide,
Address needs to be linked to a Company. Please add a row for Company in the Links table.,L'adresse doit être liée à une entreprise. Veuillez ajouter une ligne pour Entreprise dans le tableau Liens.,
@@ -9581,7 +9575,7 @@
Payment Channel,Canal de paiement,
Is Purchase Order Required for Purchase Invoice & Receipt Creation?,Une Commande d'Achat est-il requis pour la création de factures d'achat et de reçus?,
Is Purchase Receipt Required for Purchase Invoice Creation?,Un reçu d'achat est-il requis pour la création d'une facture d'achat?,
-Maintain Same Rate Throughout the Purchase Cycle,Maintenir le même taux tout au long du cycle d'achat,
+Maintain Same Rate Throughout the Purchase Cycle,Maintenir les même prix tout au long du cycle d'achat,
Allow Item To Be Added Multiple Times in a Transaction,Autoriser l'ajout d'un article plusieurs fois dans une transaction,
Suppliers,Fournisseurs,
Send Emails to Suppliers,Envoyer des e-mails aux fournisseurs,
@@ -9633,7 +9627,7 @@
Time Between Operations (Mins),Temps entre les opérations (minutes),
Default: 10 mins,Par défaut: 10 minutes,
Overproduction for Sales and Work Order,Surproduction pour les ventes et les bons de travail,
-"Update BOM cost automatically via scheduler, based on the latest Valuation Rate/Price List Rate/Last Purchase Rate of raw materials","Mettre à jour automatiquement le coût de la nomenclature via le planificateur, en fonction du dernier taux de valorisation / tarif tarifaire / dernier taux d'achat de matières premières",
+"Update BOM cost automatically via scheduler, based on the latest Valuation Rate/Price List Rate/Last Purchase Rate of raw materials","Mettre à jour automatiquement le coût de la nomenclature via le planificateur, en fonction du dernier taux de valorisation / prix de la liste de prix / dernier prix d'achat de matières premières",
Purchase Order already created for all Sales Order items,Commande d'Achat déjà créé pour tous les articles de commande client,
Select Items,Sélectionner des éléments,
Against Default Supplier,Contre le fournisseur par défaut,
@@ -9641,14 +9635,14 @@
Is Sales Order Required for Sales Invoice & Delivery Note Creation?,Une commande client est-elle requise pour la création de factures clients et de bons de livraison?,
Is Delivery Note Required for Sales Invoice Creation?,Un bon de livraison est-il nécessaire pour la création de factures de vente?,
How often should Project and Company be updated based on Sales Transactions?,À quelle fréquence le projet et l'entreprise doivent-ils être mis à jour en fonction des transactions de vente?,
-Allow User to Edit Price List Rate in Transactions,Autoriser l'utilisateur à modifier le tarif tarifaire dans les transactions,
+Allow User to Edit Price List Rate in Transactions,Autoriser l'utilisateur à modifier le prix de la liste prix dans les transactions,
Allow Item to Be Added Multiple Times in a Transaction,Autoriser l'ajout d'un article plusieurs fois dans une transaction,
Allow Multiple Sales Orders Against a Customer's Purchase Order,Autoriser plusieurs commandes client par rapport à la commande d'achat d'un client,
-Validate Selling Price for Item Against Purchase Rate or Valuation Rate,Valider le prix de vente de l'article par rapport au taux d'achat ou au taux de valorisation,
+Validate Selling Price for Item Against Purchase Rate or Valuation Rate,Valider le prix de vente de l'article par rapport au prix d'achat ou au taux de valorisation,
Hide Customer's Tax ID from Sales Transactions,Masquer le numéro d'identification fiscale du client dans les transactions de vente,
"The percentage you are allowed to receive or deliver more against the quantity ordered. For example, if you have ordered 100 units, and your Allowance is 10%, then you are allowed to receive 110 units.","Le pourcentage que vous êtes autorisé à recevoir ou à livrer plus par rapport à la quantité commandée. Par exemple, si vous avez commandé 100 unités et que votre allocation est de 10%, vous êtes autorisé à recevoir 110 unités.",
Action If Quality Inspection Is Not Submitted,Action si l'inspection qualité n'est pas soumise,
-Auto Insert Price List Rate If Missing,Taux de liste de prix d'insertion automatique s'il est manquant,
+Auto Insert Price List Rate If Missing,Insérer automatiquement le prix dand liste de prix s'il est manquant,
Automatically Set Serial Nos Based on FIFO,Définir automatiquement les numéros de série en fonction de FIFO,
Set Qty in Transactions Based on Serial No Input,Définir la quantité dans les transactions en fonction du numéro de série,
Raise Material Request When Stock Reaches Re-order Level,Augmenter la demande d'article lorsque le stock atteint le niveau de commande,
@@ -9722,7 +9716,7 @@
An Inpatient Medication Order {0} against Patient Encounter {1} already exists.,Une ordonnance de médicament pour patients hospitalisés {0} contre rencontre avec un patient {1} existe déjà.,
Allow In Returns,Autoriser les retours,
Hide Unavailable Items,Masquer les éléments non disponibles,
-Apply Discount on Discounted Rate,Appliquer une remise sur un tarif réduit,
+Apply Discount on Discounted Rate,Appliquer une remise sur un prix réduit,
Therapy Plan Template,Modèle de plan de thérapie,
Fetching Template Details,Récupération des détails du modèle,
Linked Item Details,Détails de l'élément lié,
@@ -9927,4 +9921,3 @@
Enable Recommendations,Activer les recommendations
Item Search Settings,Paramétrage de la recherche d'article
Purchase demande,Demande de materiel
-Calendar,Calendier