diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.js b/erpnext/accounts/doctype/journal_entry/journal_entry.js
index 3798b0fbdf8..5c0d3265680 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.js
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.js
@@ -149,22 +149,6 @@ frappe.ui.form.on("Journal Entry", {
}
});
}
- else if(frm.doc.voucher_type=="Opening Entry") {
- return frappe.call({
- type:"GET",
- method: "erpnext.accounts.doctype.journal_entry.journal_entry.get_opening_accounts",
- args: {
- "company": frm.doc.company
- },
- callback: function(r) {
- frappe.model.clear_table(frm.doc, "accounts");
- if(r.message) {
- update_jv_details(frm.doc, r.message);
- }
- cur_frm.set_value("is_opening", "Yes");
- }
- });
- }
}
},
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.json b/erpnext/accounts/doctype/journal_entry/journal_entry.json
index 4493c722544..8e5ba3718f7 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.json
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.json
@@ -137,7 +137,8 @@
"fieldname": "finance_book",
"fieldtype": "Link",
"label": "Finance Book",
- "options": "Finance Book"
+ "options": "Finance Book",
+ "read_only": 1
},
{
"fieldname": "2_add_edit_gl_entries",
@@ -538,7 +539,7 @@
"idx": 176,
"is_submittable": 1,
"links": [],
- "modified": "2022-04-06 17:18:46.865259",
+ "modified": "2022-06-23 22:01:32.348337",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Journal Entry",
diff --git a/erpnext/accounts/doctype/journal_entry/journal_entry.py b/erpnext/accounts/doctype/journal_entry/journal_entry.py
index 8660c18bf95..c46b185e60a 100644
--- a/erpnext/accounts/doctype/journal_entry/journal_entry.py
+++ b/erpnext/accounts/doctype/journal_entry/journal_entry.py
@@ -1192,24 +1192,6 @@ def get_payment_entry(ref_doc, args):
return je if args.get("journal_entry") else je.as_dict()
-@frappe.whitelist()
-def get_opening_accounts(company):
- """get all balance sheet accounts for opening entry"""
- accounts = frappe.db.sql_list(
- """select
- name from tabAccount
- where
- is_group=0 and report_type='Balance Sheet' and company={0} and
- name not in (select distinct account from tabWarehouse where
- account is not null and account != '')
- order by name asc""".format(
- frappe.db.escape(company)
- )
- )
-
- return [{"account": a, "balance": get_balance_on(a)} for a in accounts]
-
-
@frappe.whitelist()
@frappe.validate_and_sanitize_search_inputs
def get_against_jv(doctype, txt, searchfield, start, page_len, filters):
diff --git a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py
index 20f7643a1c9..9d2deea523b 100644
--- a/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py
+++ b/erpnext/accounts/report/bank_clearance_summary/bank_clearance_summary.py
@@ -43,7 +43,7 @@ def get_columns():
"options": "Account",
"width": 170,
},
- {"label": _("Amount"), "fieldname": "amount", "width": 120},
+ {"label": _("Amount"), "fieldname": "amount", "fieldtype": "Currency", "width": 120},
]
return columns
diff --git a/erpnext/accounts/report/general_ledger/general_ledger.py b/erpnext/accounts/report/general_ledger/general_ledger.py
index ccee7d9c548..6a3b38ae7b5 100644
--- a/erpnext/accounts/report/general_ledger/general_ledger.py
+++ b/erpnext/accounts/report/general_ledger/general_ledger.py
@@ -468,7 +468,7 @@ def get_accountwise_gle(filters, accounting_dimensions, gl_entries, gle_map):
update_value_in_dict(totals, "opening", gle)
update_value_in_dict(totals, "closing", gle)
- elif gle.posting_date <= to_date:
+ elif gle.posting_date <= to_date or (cstr(gle.is_opening) == "Yes" and show_opening_entries):
if not group_by_voucher_consolidated:
update_value_in_dict(gle_map[group_by_value].totals, "total", gle)
update_value_in_dict(gle_map[group_by_value].totals, "closing", gle)
diff --git a/erpnext/assets/doctype/asset/asset.py b/erpnext/assets/doctype/asset/asset.py
index 651ab9b00e8..4d63a285534 100644
--- a/erpnext/assets/doctype/asset/asset.py
+++ b/erpnext/assets/doctype/asset/asset.py
@@ -257,6 +257,7 @@ class Asset(AccountsController):
number_of_pending_depreciations += 1
skip_row = False
+ should_get_last_day = is_last_day_of_the_month(finance_book.depreciation_start_date)
for n in range(start[finance_book.idx - 1], number_of_pending_depreciations):
# If depreciation is already completed (for double declining balance)
@@ -270,6 +271,9 @@ class Asset(AccountsController):
finance_book.depreciation_start_date, n * cint(finance_book.frequency_of_depreciation)
)
+ if should_get_last_day:
+ schedule_date = get_last_day(schedule_date)
+
# schedule date will be a year later from start date
# so monthly schedule date is calculated by removing 11 months from it
monthly_schedule_date = add_months(schedule_date, -finance_book.frequency_of_depreciation + 1)
@@ -845,14 +849,9 @@ class Asset(AccountsController):
if args.get("rate_of_depreciation") and on_validate:
return args.get("rate_of_depreciation")
- no_of_years = (
- flt(args.get("total_number_of_depreciations") * flt(args.get("frequency_of_depreciation")))
- / 12
- )
value = flt(args.get("expected_value_after_useful_life")) / flt(self.gross_purchase_amount)
- # square root of flt(salvage_value) / flt(asset_cost)
- depreciation_rate = math.pow(value, 1.0 / flt(no_of_years, 2))
+ depreciation_rate = math.pow(value, 1.0 / flt(args.get("total_number_of_depreciations"), 2))
return 100 * (1 - flt(depreciation_rate, float_precision))
@@ -1103,9 +1102,18 @@ def is_cwip_accounting_enabled(asset_category):
def get_total_days(date, frequency):
period_start_date = add_months(date, cint(frequency) * -1)
+ if is_last_day_of_the_month(date):
+ period_start_date = get_last_day(period_start_date)
+
return date_diff(date, period_start_date)
+def is_last_day_of_the_month(date):
+ last_day_of_the_month = get_last_day(date)
+
+ return getdate(last_day_of_the_month) == getdate(date)
+
+
@erpnext.allow_regional
def get_depreciation_amount(asset, depreciable_value, row):
if row.depreciation_method in ("Straight Line", "Manual"):
diff --git a/erpnext/assets/doctype/asset/test_asset.py b/erpnext/assets/doctype/asset/test_asset.py
index 226a38a5849..9bfafaf8657 100644
--- a/erpnext/assets/doctype/asset/test_asset.py
+++ b/erpnext/assets/doctype/asset/test_asset.py
@@ -701,6 +701,39 @@ class TestDepreciationMethods(AssetSetup):
asset.reload()
self.assertEquals(asset.finance_books[0].value_after_depreciation, 98000.0)
+ def test_monthly_depreciation_by_wdv_method(self):
+ asset = create_asset(
+ calculate_depreciation=1,
+ available_for_use_date="2022-02-15",
+ purchase_date="2022-02-15",
+ depreciation_method="Written Down Value",
+ gross_purchase_amount=10000,
+ expected_value_after_useful_life=5000,
+ depreciation_start_date="2022-02-28",
+ total_number_of_depreciations=5,
+ frequency_of_depreciation=1,
+ )
+
+ expected_schedules = [
+ ["2022-02-28", 645.0, 645.0],
+ ["2022-03-31", 1206.8, 1851.8],
+ ["2022-04-30", 1051.12, 2902.92],
+ ["2022-05-31", 915.52, 3818.44],
+ ["2022-06-30", 797.42, 4615.86],
+ ["2022-07-15", 384.14, 5000.0],
+ ]
+
+ schedules = [
+ [
+ cstr(d.schedule_date),
+ flt(d.depreciation_amount, 2),
+ flt(d.accumulated_depreciation_amount, 2),
+ ]
+ for d in asset.get("schedules")
+ ]
+
+ self.assertEqual(schedules, expected_schedules)
+
class TestDepreciationBasics(AssetSetup):
def test_depreciation_without_pro_rata(self):
@@ -787,7 +820,7 @@ class TestDepreciationBasics(AssetSetup):
expected_values = [["2020-12-31", 30000.0], ["2021-12-31", 30000.0], ["2022-12-31", 30000.0]]
for i, schedule in enumerate(asset.schedules):
- self.assertEqual(expected_values[i][0], schedule.schedule_date)
+ self.assertEqual(getdate(expected_values[i][0]), schedule.schedule_date)
self.assertEqual(expected_values[i][1], schedule.depreciation_amount)
def test_set_accumulated_depreciation(self):
@@ -1261,6 +1294,32 @@ class TestDepreciationBasics(AssetSetup):
asset.cost_center = "Main - _TC"
asset.submit()
+ def test_depreciation_on_final_day_of_the_month(self):
+ """Tests if final day of the month is picked each time, if the depreciation start date is the last day of the month."""
+
+ asset = create_asset(
+ item_code="Macbook Pro",
+ calculate_depreciation=1,
+ purchase_date="2020-01-30",
+ available_for_use_date="2020-02-15",
+ depreciation_start_date="2020-02-29",
+ frequency_of_depreciation=1,
+ total_number_of_depreciations=5,
+ submit=1,
+ )
+
+ expected_dates = [
+ "2020-02-29",
+ "2020-03-31",
+ "2020-04-30",
+ "2020-05-31",
+ "2020-06-30",
+ "2020-07-15",
+ ]
+
+ for i, schedule in enumerate(asset.schedules):
+ self.assertEqual(getdate(expected_dates[i]), getdate(schedule.schedule_date))
+
def create_asset_data():
if not frappe.db.exists("Asset Category", "Computers"):
diff --git a/erpnext/buying/doctype/purchase_order/test_purchase_order.py b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
index e4fb970c3f7..590370e808c 100644
--- a/erpnext/buying/doctype/purchase_order/test_purchase_order.py
+++ b/erpnext/buying/doctype/purchase_order/test_purchase_order.py
@@ -140,6 +140,43 @@ class TestPurchaseOrder(FrappeTestCase):
# ordered qty decreases as ordered qty is 0 (deleted row)
self.assertEqual(get_ordered_qty(), existing_ordered_qty - 10) # 0
+ def test_supplied_items_validations_on_po_update_after_submit(self):
+ po = create_purchase_order(item_code="_Test FG Item", is_subcontracted="Yes", qty=5, rate=100)
+ item = po.items[0]
+
+ original_supplied_items = {po.name: po.required_qty for po in po.supplied_items}
+
+ # Just update rate
+ trans_item = [
+ {
+ "item_code": "_Test FG Item",
+ "rate": 20,
+ "qty": 5,
+ "conversion_factor": 1.0,
+ "docname": item.name,
+ }
+ ]
+ update_child_qty_rate("Purchase Order", json.dumps(trans_item), po.name)
+ po.reload()
+
+ new_supplied_items = {po.name: po.required_qty for po in po.supplied_items}
+ self.assertEqual(set(original_supplied_items.keys()), set(new_supplied_items.keys()))
+
+ # Update qty to 2x
+ trans_item[0]["qty"] *= 2
+ update_child_qty_rate("Purchase Order", json.dumps(trans_item), po.name)
+ po.reload()
+
+ new_supplied_items = {po.name: po.required_qty for po in po.supplied_items}
+ self.assertEqual(2 * sum(original_supplied_items.values()), sum(new_supplied_items.values()))
+
+ # Set transfer qty and attempt to update qty, shouldn't be allowed
+ po.supplied_items[0].supplied_qty = 2
+ po.supplied_items[0].db_update()
+ trans_item[0]["qty"] *= 2
+ with self.assertRaises(frappe.ValidationError):
+ update_child_qty_rate("Purchase Order", json.dumps(trans_item), po.name)
+
def test_update_child(self):
mr = make_material_request(qty=10)
po = make_purchase_order(mr.name)
diff --git a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js
index ca3be03da6e..721e54e46f5 100644
--- a/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js
+++ b/erpnext/buying/report/purchase_order_analysis/purchase_order_analysis.js
@@ -59,6 +59,7 @@ frappe.query_reports["Purchase Order Analysis"] = {
for (let option of status){
options.push({
"value": option,
+ "label": __(option),
"description": ""
})
}
diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py
index 71901c52bf6..dce6d7525d0 100644
--- a/erpnext/controllers/accounts_controller.py
+++ b/erpnext/controllers/accounts_controller.py
@@ -2438,7 +2438,7 @@ def update_bin_on_delete(row, doctype):
update_bin_qty(row.item_code, row.warehouse, qty_dict)
-def validate_and_delete_children(parent, data):
+def validate_and_delete_children(parent, data) -> bool:
deleted_children = []
updated_item_names = [d.get("docname") for d in data]
for item in parent.items:
@@ -2457,6 +2457,8 @@ def validate_and_delete_children(parent, data):
for d in deleted_children:
update_bin_on_delete(d, parent.doctype)
+ return bool(deleted_children)
+
@frappe.whitelist()
def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, child_docname="items"):
@@ -2520,13 +2522,38 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
):
frappe.throw(_("Cannot set quantity less than received quantity"))
+ def should_update_supplied_items(doc) -> bool:
+ """Subcontracted PO can allow following changes *after submit*:
+
+ 1. Change rate of subcontracting - regardless of other changes.
+ 2. Change qty and/or add new items and/or remove items
+ Exception: Transfer/Consumption is already made, qty change not allowed.
+ """
+
+ supplied_items_processed = any(
+ item.supplied_qty or item.consumed_qty or item.returned_qty for item in doc.supplied_items
+ )
+
+ update_supplied_items = (
+ any_qty_changed or items_added_or_removed or any_conversion_factor_changed
+ )
+ if update_supplied_items and supplied_items_processed:
+ frappe.throw(_("Item qty can not be updated as raw materials are already processed."))
+
+ return update_supplied_items
+
data = json.loads(trans_items)
+ any_qty_changed = False # updated to true if any item's qty changes
+ items_added_or_removed = False # updated to true if any new item is added or removed
+ any_conversion_factor_changed = False
+
sales_doctypes = ["Sales Order", "Sales Invoice", "Delivery Note", "Quotation"]
parent = frappe.get_doc(parent_doctype, parent_doctype_name)
check_doc_permissions(parent, "write")
- validate_and_delete_children(parent, data)
+ _removed_items = validate_and_delete_children(parent, data)
+ items_added_or_removed |= _removed_items
for d in data:
new_child_flag = False
@@ -2537,6 +2564,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
if not d.get("docname"):
new_child_flag = True
+ items_added_or_removed = True
check_doc_permissions(parent, "create")
child_item = get_new_child_item(d)
else:
@@ -2559,6 +2587,7 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
qty_unchanged = prev_qty == new_qty
uom_unchanged = prev_uom == new_uom
conversion_factor_unchanged = prev_con_fac == new_con_fac
+ any_conversion_factor_changed |= not conversion_factor_unchanged
date_unchanged = (
prev_date == getdate(new_date) if prev_date and new_date else False
) # in case of delivery note etc
@@ -2572,6 +2601,8 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
continue
validate_quantity(child_item, d)
+ if flt(child_item.get("qty")) != flt(d.get("qty")):
+ any_qty_changed = True
child_item.qty = flt(d.get("qty"))
rate_precision = child_item.precision("rate") or 2
@@ -2677,8 +2708,9 @@ def update_child_qty_rate(parent_doctype, trans_items, parent_doctype_name, chil
parent.update_ordered_and_reserved_qty()
parent.update_receiving_percentage()
if parent.is_subcontracted == "Yes":
- parent.update_reserved_qty_for_subcontract()
- parent.create_raw_materials_supplied("supplied_items")
+ if should_update_supplied_items(parent):
+ parent.update_reserved_qty_for_subcontract()
+ parent.create_raw_materials_supplied("supplied_items")
parent.save()
else: # Sales Order
parent.validate_warehouse()
diff --git a/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.json b/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.json
index c32e383b08a..a926e69ee69 100644
--- a/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.json
+++ b/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.json
@@ -7,11 +7,11 @@
"editable_grid": 1,
"engine": "InnoDB",
"field_order": [
- "current_bom",
- "new_bom",
- "column_break_3",
"update_type",
"status",
+ "column_break_3",
+ "current_bom",
+ "new_bom",
"error_log",
"progress_section",
"current_level",
@@ -37,6 +37,7 @@
"options": "BOM"
},
{
+ "depends_on": "eval:doc.update_type === \"Replace BOM\"",
"fieldname": "column_break_3",
"fieldtype": "Column Break"
},
@@ -87,6 +88,7 @@
"options": "BOM Update Batch"
},
{
+ "depends_on": "eval:doc.status !== \"Completed\"",
"fieldname": "current_level",
"fieldtype": "Int",
"label": "Current Level"
@@ -96,7 +98,7 @@
"index_web_pages_for_search": 1,
"is_submittable": 1,
"links": [],
- "modified": "2022-06-06 15:15:23.883251",
+ "modified": "2022-06-20 15:43:55.696388",
"modified_by": "Administrator",
"module": "Manufacturing",
"name": "BOM Update Log",
diff --git a/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.py b/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.py
index 9c9c24044aa..af5ff8e1c21 100644
--- a/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.py
+++ b/erpnext/manufacturing/doctype/bom_update_log/bom_update_log.py
@@ -77,7 +77,11 @@ class BOMUpdateLog(Document):
now=frappe.flags.in_test,
)
else:
- process_boms_cost_level_wise(self)
+ frappe.enqueue(
+ method="erpnext.manufacturing.doctype.bom_update_log.bom_update_log.process_boms_cost_level_wise",
+ update_doc=self,
+ now=frappe.flags.in_test,
+ )
def run_replace_bom_job(
@@ -112,28 +116,31 @@ def process_boms_cost_level_wise(
current_boms = {}
values = {}
- if update_doc.status == "Queued":
- # First level yet to process. On Submit.
- current_level = 0
- current_boms = get_leaf_boms()
- values = {
- "processed_boms": json.dumps({}),
- "status": "In Progress",
- "current_level": current_level,
- }
- else:
- # Resume next level. via Cron Job.
- if not parent_boms:
- return
+ try:
+ if update_doc.status == "Queued":
+ # First level yet to process. On Submit.
+ current_level = 0
+ current_boms = get_leaf_boms()
+ values = {
+ "processed_boms": json.dumps({}),
+ "status": "In Progress",
+ "current_level": current_level,
+ }
+ else:
+ # Resume next level. via Cron Job.
+ if not parent_boms:
+ return
- current_level = cint(update_doc.current_level) + 1
+ current_level = cint(update_doc.current_level) + 1
- # Process the next level BOMs. Stage parents as current BOMs.
- current_boms = parent_boms.copy()
- values = {"current_level": current_level}
+ # Process the next level BOMs. Stage parents as current BOMs.
+ current_boms = parent_boms.copy()
+ values = {"current_level": current_level}
- set_values_in_log(update_doc.name, values, commit=True)
- queue_bom_cost_jobs(current_boms, update_doc, current_level)
+ set_values_in_log(update_doc.name, values, commit=True)
+ queue_bom_cost_jobs(current_boms, update_doc, current_level)
+ except Exception:
+ handle_exception(update_doc)
def queue_bom_cost_jobs(
@@ -199,16 +206,22 @@ def resume_bom_cost_update_jobs():
current_boms, processed_boms = get_processed_current_boms(log, bom_batches)
parent_boms = get_next_higher_level_boms(child_boms=current_boms, processed_boms=processed_boms)
- # Unset processed BOMs if log is complete, it is used for next level BOMs
+ # Unset processed BOMs (it is used for next level BOMs) & change status if log is complete
+ status = "Completed" if not parent_boms else "In Progress"
+ processed_boms = json.dumps([] if not parent_boms else processed_boms)
set_values_in_log(
log.name,
values={
- "processed_boms": json.dumps([] if not parent_boms else processed_boms),
- "status": "Completed" if not parent_boms else "In Progress",
+ "processed_boms": processed_boms,
+ "status": status,
},
commit=True,
)
+ # clear progress section
+ if status == "Completed":
+ frappe.db.delete("BOM Update Batch", {"parent": log.name})
+
if parent_boms: # there is a next level to process
process_boms_cost_level_wise(
update_doc=frappe.get_doc("BOM Update Log", log.name), parent_boms=parent_boms
diff --git a/erpnext/manufacturing/doctype/production_plan/production_plan.py b/erpnext/manufacturing/doctype/production_plan/production_plan.py
index ff739e9d72b..b7e3b4280d6 100644
--- a/erpnext/manufacturing/doctype/production_plan/production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/production_plan.py
@@ -25,6 +25,7 @@ from six import iteritems
from erpnext.manufacturing.doctype.bom.bom import get_children, validate_bom_no
from erpnext.manufacturing.doctype.work_order.work_order import get_item_details
from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
+from erpnext.utilities.transaction_base import validate_uom_is_integer
class ProductionPlan(Document):
@@ -33,6 +34,7 @@ class ProductionPlan(Document):
self.calculate_total_planned_qty()
self.set_status()
self._rename_temporary_references()
+ validate_uom_is_integer(self, "stock_uom", "planned_qty")
def set_pending_qty_in_row_without_reference(self):
"Set Pending Qty in independent rows (not from SO or MR)."
diff --git a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
index dadb8e2781f..fa40b3d56ce 100644
--- a/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
+++ b/erpnext/manufacturing/doctype/production_plan/test_production_plan.py
@@ -630,15 +630,23 @@ class TestProductionPlan(FrappeTestCase):
self.assertFalse(pp.all_items_completed())
def test_production_plan_planned_qty(self):
- pln = create_production_plan(item_code="_Test FG Item", planned_qty=0.55)
- pln.make_work_order()
- work_order = frappe.db.get_value("Work Order", {"production_plan": pln.name}, "name")
- wo_doc = frappe.get_doc("Work Order", work_order)
- wo_doc.update(
- {"wip_warehouse": "Work In Progress - _TC", "fg_warehouse": "Finished Goods - _TC"}
+ # Case 1: When Planned Qty is non-integer and UOM is integer.
+ from erpnext.utilities.transaction_base import UOMMustBeIntegerError
+
+ self.assertRaises(
+ UOMMustBeIntegerError, create_production_plan, item_code="_Test FG Item", planned_qty=0.55
)
- wo_doc.submit()
- self.assertEqual(wo_doc.qty, 0.55)
+
+ # Case 2: When Planned Qty is non-integer and UOM is also non-integer.
+ from erpnext.stock.doctype.item.test_item import make_item
+
+ fg_item = make_item(properties={"is_stock_item": 1, "stock_uom": "_Test UOM 1"}).name
+ bom_item = make_item().name
+
+ make_bom(item=fg_item, raw_materials=[bom_item], source_warehouse="_Test Warehouse - _TC")
+
+ pln = create_production_plan(item_code=fg_item, planned_qty=0.55, stock_uom="_Test UOM 1")
+ self.assertEqual(pln.po_items[0].planned_qty, 0.55)
def test_temporary_name_relinking(self):
@@ -702,6 +710,7 @@ def create_production_plan(**args):
"bom_no": frappe.db.get_value("Item", args.item_code, "default_bom"),
"planned_qty": args.planned_qty or 1,
"planned_start_date": args.planned_start_date or now_datetime(),
+ "stock_uom": args.stock_uom or "Nos",
},
)
diff --git a/erpnext/payroll/doctype/salary_slip/salary_slip.py b/erpnext/payroll/doctype/salary_slip/salary_slip.py
index fcb415c00ed..e4c816c8fb4 100644
--- a/erpnext/payroll/doctype/salary_slip/salary_slip.py
+++ b/erpnext/payroll/doctype/salary_slip/salary_slip.py
@@ -625,7 +625,7 @@ class SalarySlip(TransactionBase):
data = self.get_data_for_eval()
for struct_row in self._salary_structure_doc.get(component_type):
amount = self.eval_condition_and_formula(struct_row, data)
- if amount and struct_row.statistical_component == 0:
+ if amount is not None and struct_row.statistical_component == 0:
self.update_component_row(struct_row, amount, component_type)
def get_data_for_eval(self):
@@ -857,6 +857,10 @@ class SalarySlip(TransactionBase):
component_row, joining_date, relieving_date
)[0]
+ # remove 0 valued components that have been updated later
+ if component_row.amount == 0:
+ self.remove(component_row)
+
def set_precision_for_component_amounts(self):
for component_type in ("earnings", "deductions"):
for component_row in self.get(component_type):
diff --git a/erpnext/selling/doctype/product_bundle/product_bundle.js b/erpnext/selling/doctype/product_bundle/product_bundle.js
index 7a04c6ab06d..3096b692a7e 100644
--- a/erpnext/selling/doctype/product_bundle/product_bundle.js
+++ b/erpnext/selling/doctype/product_bundle/product_bundle.js
@@ -1,19 +1,13 @@
-// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+// Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and Contributors
// License: GNU General Public License v3. See license.txt
-cur_frm.cscript.refresh = function(doc, cdt, cdn) {
- cur_frm.toggle_enable('new_item_code', doc.__islocal);
-}
-
-cur_frm.fields_dict.new_item_code.get_query = function() {
- return{
- query: "erpnext.selling.doctype.product_bundle.product_bundle.get_new_item_code"
- }
-}
-cur_frm.fields_dict.new_item_code.query_description = __('Please select Item where "Is Stock Item" is "No" and "Is Sales Item" is "Yes" and there is no other Product Bundle');
-
-cur_frm.cscript.onload = function() {
- // set add fetch for item_code's item_name and description
- cur_frm.add_fetch('item_code', 'stock_uom', 'uom');
- cur_frm.add_fetch('item_code', 'description', 'description');
-}
+frappe.ui.form.on("Product Bundle", {
+ refresh: function (frm) {
+ frm.toggle_enable("new_item_code", frm.is_new());
+ frm.set_query("new_item_code", () => {
+ return {
+ query: "erpnext.selling.doctype.product_bundle.product_bundle.get_new_item_code",
+ };
+ });
+ },
+});
diff --git a/erpnext/selling/doctype/product_bundle_item/product_bundle_item.json b/erpnext/selling/doctype/product_bundle_item/product_bundle_item.json
index dc071e4d65e..fc8caeb31d9 100644
--- a/erpnext/selling/doctype/product_bundle_item/product_bundle_item.json
+++ b/erpnext/selling/doctype/product_bundle_item/product_bundle_item.json
@@ -33,6 +33,8 @@
"reqd": 1
},
{
+ "fetch_from": "item_code.description",
+ "fetch_if_empty": 1,
"fieldname": "description",
"fieldtype": "Text Editor",
"in_list_view": 1,
@@ -51,6 +53,8 @@
"print_hide": 1
},
{
+ "fetch_from": "item_code.stock_uom",
+ "fetch_if_empty": 1,
"fieldname": "uom",
"fieldtype": "Link",
"in_list_view": 1,
@@ -64,7 +68,7 @@
"idx": 1,
"istable": 1,
"links": [],
- "modified": "2020-02-28 14:06:05.725655",
+ "modified": "2022-06-27 05:30:18.475150",
"modified_by": "Administrator",
"module": "Selling",
"name": "Product Bundle Item",
@@ -72,5 +76,6 @@
"permissions": [],
"sort_field": "modified",
"sort_order": "DESC",
+ "states": [],
"track_changes": 1
}
\ No newline at end of file
diff --git a/erpnext/selling/doctype/quotation/quotation.py b/erpnext/selling/doctype/quotation/quotation.py
index d1530395d8a..ce40d1122ed 100644
--- a/erpnext/selling/doctype/quotation/quotation.py
+++ b/erpnext/selling/doctype/quotation/quotation.py
@@ -204,6 +204,15 @@ def make_sales_order(source_name, target_doc=None):
def _make_sales_order(source_name, target_doc=None, ignore_permissions=False):
customer = _make_customer(source_name, ignore_permissions)
+ ordered_items = frappe._dict(
+ frappe.db.get_all(
+ "Sales Order Item",
+ {"prevdoc_docname": source_name, "docstatus": 1},
+ ["item_code", "sum(qty)"],
+ group_by="item_code",
+ as_list=1,
+ )
+ )
def set_missing_values(source, target):
if customer:
@@ -219,7 +228,9 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False):
target.run_method("calculate_taxes_and_totals")
def update_item(obj, target, source_parent):
- target.stock_qty = flt(obj.qty) * flt(obj.conversion_factor)
+ balance_qty = obj.qty - ordered_items.get(obj.item_code, 0.0)
+ target.qty = balance_qty if balance_qty > 0 else 0
+ target.stock_qty = flt(target.qty) * flt(obj.conversion_factor)
if obj.against_blanket_order:
target.against_blanket_order = obj.against_blanket_order
@@ -235,6 +246,7 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False):
"doctype": "Sales Order Item",
"field_map": {"parent": "prevdoc_docname"},
"postprocess": update_item,
+ "condition": lambda doc: doc.qty > 0,
},
"Sales Taxes and Charges": {"doctype": "Sales Taxes and Charges", "add_if_empty": True},
"Sales Team": {"doctype": "Sales Team", "add_if_empty": True},
diff --git a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js
index 76a5bb51ca1..91748bc7be2 100644
--- a/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js
+++ b/erpnext/selling/report/sales_order_analysis/sales_order_analysis.js
@@ -55,6 +55,7 @@ frappe.query_reports["Sales Order Analysis"] = {
for (let option of status){
options.push({
"value": option,
+ "label": __(option),
"description": ""
})
}
diff --git a/erpnext/stock/doctype/item/test_item.py b/erpnext/stock/doctype/item/test_item.py
index 29d6bf2df8b..179f7237f96 100644
--- a/erpnext/stock/doctype/item/test_item.py
+++ b/erpnext/stock/doctype/item/test_item.py
@@ -712,6 +712,7 @@ def create_item(
item_code,
is_stock_item=1,
valuation_rate=0,
+ stock_uom="Nos",
warehouse="_Test Warehouse - _TC",
is_customer_provided_item=None,
customer=None,
@@ -727,6 +728,7 @@ def create_item(
item.item_name = item_code
item.description = item_code
item.item_group = "All Item Groups"
+ item.stock_uom = stock_uom
item.is_stock_item = is_stock_item
item.is_fixed_asset = is_fixed_asset
item.asset_category = asset_category
diff --git a/erpnext/stock/doctype/stock_entry/stock_entry.py b/erpnext/stock/doctype/stock_entry/stock_entry.py
index 92f90a1c7d6..e0bdc9229f5 100644
--- a/erpnext/stock/doctype/stock_entry/stock_entry.py
+++ b/erpnext/stock/doctype/stock_entry/stock_entry.py
@@ -590,7 +590,7 @@ class StockEntry(StockController):
)
+ "
"
+ _("Available quantity is {0}, you need {1}").format(
- frappe.bold(d.actual_qty), frappe.bold(d.transfer_qty)
+ frappe.bold(flt(d.actual_qty, d.precision("actual_qty"))), frappe.bold(d.transfer_qty)
),
NegativeStockError,
title=_("Insufficient Stock"),
diff --git a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
index 9fa61098a0b..e810c7933cc 100644
--- a/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
+++ b/erpnext/stock/doctype/stock_ledger_entry/test_stock_ledger_entry.py
@@ -7,7 +7,7 @@ import frappe
from frappe.core.page.permission_manager.permission_manager import reset
from frappe.custom.doctype.property_setter.property_setter import make_property_setter
from frappe.tests.utils import FrappeTestCase, change_settings
-from frappe.utils import add_days, add_to_date, today
+from frappe.utils import add_days, add_to_date, flt, today
from erpnext.accounts.doctype.gl_entry.gl_entry import rename_gle_sle_docs
from erpnext.stock.doctype.delivery_note.test_delivery_note import create_delivery_note
@@ -40,6 +40,9 @@ class TestStockLedgerEntry(FrappeTestCase, StockTestMixin):
"delete from `tabBin` where item_code in (%s)" % (", ".join(["%s"] * len(items))), items
)
+ def tearDown(self):
+ frappe.db.rollback()
+
def assertSLEs(self, doc, expected_sles, sle_filters=None):
"""Compare sorted SLEs, useful for vouchers that create multiple SLEs for same line"""
@@ -754,6 +757,93 @@ class TestStockLedgerEntry(FrappeTestCase, StockTestMixin):
)
self.assertEqual(abs(sles[0].stock_value_difference), sles[1].stock_value_difference)
+ @change_settings("System Settings", {"float_precision": 4})
+ def test_negative_qty_with_precision(self):
+ "Test if system precision is respected while validating negative qty."
+ from erpnext.stock.doctype.item.test_item import create_item
+ from erpnext.stock.utils import get_stock_balance
+
+ item_code = "ItemPrecisionTest"
+ warehouse = "_Test Warehouse - _TC"
+ create_item(item_code, is_stock_item=1, stock_uom="Kg")
+
+ create_stock_reconciliation(item_code=item_code, warehouse=warehouse, qty=559.8327, rate=100)
+
+ make_stock_entry(item_code=item_code, source=warehouse, qty=470.84, rate=100)
+ self.assertEqual(get_stock_balance(item_code, warehouse), 88.9927)
+
+ settings = frappe.get_doc("System Settings")
+ settings.float_precision = 3
+ settings.save()
+
+ # To deliver 100 qty we fall short of 11.0073 qty (11.007 with precision 3)
+ # Stock up with 11.007 (balance in db becomes 99.9997, on UI it will show as 100)
+ make_stock_entry(item_code=item_code, target=warehouse, qty=11.007, rate=100)
+ self.assertEqual(get_stock_balance(item_code, warehouse), 99.9997)
+
+ # See if delivery note goes through
+ # Negative qty error should not be raised as 99.9997 is 100 with precision 3 (system precision)
+ dn = create_delivery_note(
+ item_code=item_code,
+ qty=100,
+ rate=150,
+ warehouse=warehouse,
+ company="_Test Company",
+ expense_account="Cost of Goods Sold - _TC",
+ cost_center="Main - _TC",
+ do_not_submit=True,
+ )
+ dn.submit()
+
+ self.assertEqual(flt(get_stock_balance(item_code, warehouse), 3), 0.000)
+
+ @change_settings("System Settings", {"float_precision": 4})
+ def test_future_negative_qty_with_precision(self):
+ """
+ Ledger:
+ | Voucher | Qty | Balance
+ -------------------
+ | Reco | 559.8327| 559.8327
+ | SE | -470.84 | [Backdated] (new bal: 88.9927)
+ | SE | 11.007 | 570.8397 (new bal: 99.9997)
+ | DN | -100 | 470.8397 (new bal: -0.0003)
+
+ Check if future negative qty is asserted as per precision 3.
+ -0.0003 should be considered as 0.000
+ """
+ from erpnext.stock.doctype.item.test_item import create_item
+
+ item_code = "ItemPrecisionTest"
+ warehouse = "_Test Warehouse - _TC"
+ create_item(item_code, is_stock_item=1, stock_uom="Kg")
+
+ create_stock_reconciliation(
+ item_code=item_code,
+ warehouse=warehouse,
+ qty=559.8327,
+ rate=100,
+ posting_date=add_days(today(), -2),
+ )
+ make_stock_entry(item_code=item_code, target=warehouse, qty=11.007, rate=100)
+ create_delivery_note(
+ item_code=item_code,
+ qty=100,
+ rate=150,
+ warehouse=warehouse,
+ company="_Test Company",
+ expense_account="Cost of Goods Sold - _TC",
+ cost_center="Main - _TC",
+ )
+
+ settings = frappe.get_doc("System Settings")
+ settings.float_precision = 3
+ settings.save()
+
+ # Make backdated SE and make sure SE goes through as per precision (no negative qty error)
+ make_stock_entry(
+ item_code=item_code, source=warehouse, qty=470.84, rate=100, posting_date=add_days(today(), -1)
+ )
+
def create_repack_entry(**args):
args = frappe._dict(args)
diff --git a/erpnext/stock/stock_ledger.py b/erpnext/stock/stock_ledger.py
index 7bdf2078c4a..da57ba054dc 100644
--- a/erpnext/stock/stock_ledger.py
+++ b/erpnext/stock/stock_ledger.py
@@ -1,4 +1,4 @@
-# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
+# Copyright (c) 2022, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
import copy
@@ -360,7 +360,7 @@ class update_entries_after(object):
self.args["name"] = self.args.sle_id
self.company = frappe.get_cached_value("Warehouse", self.args.warehouse, "company")
- self.get_precision()
+ self.set_precision()
self.valuation_method = get_valuation_method(self.item_code)
self.new_items_found = False
@@ -371,10 +371,10 @@ class update_entries_after(object):
self.initialize_previous_data(self.args)
self.build()
- def get_precision(self):
- company_base_currency = frappe.get_cached_value("Company", self.company, "default_currency")
- self.precision = get_field_precision(
- frappe.get_meta("Stock Ledger Entry").get_field("stock_value"), currency=company_base_currency
+ def set_precision(self):
+ self.flt_precision = cint(frappe.db.get_default("float_precision")) or 2
+ self.currency_precision = get_field_precision(
+ frappe.get_meta("Stock Ledger Entry").get_field("stock_value")
)
def initialize_previous_data(self, args):
@@ -571,7 +571,7 @@ class update_entries_after(object):
)
# rounding as per precision
- self.wh_data.stock_value = flt(self.wh_data.stock_value, self.precision)
+ self.wh_data.stock_value = flt(self.wh_data.stock_value, self.currency_precision)
if not self.wh_data.qty_after_transaction:
self.wh_data.stock_value = 0.0
stock_value_difference = self.wh_data.stock_value - self.wh_data.prev_stock_value
@@ -595,6 +595,7 @@ class update_entries_after(object):
will not consider cancelled entries
"""
diff = self.wh_data.qty_after_transaction + flt(sle.actual_qty)
+ diff = flt(diff, self.flt_precision) # respect system precision
if diff < 0 and abs(diff) > 0.0001:
# negative stock!
@@ -1347,7 +1348,8 @@ def validate_negative_qty_in_future_sle(args, allow_negative_stock=False):
return
neg_sle = get_future_sle_with_negative_qty(args)
- if neg_sle:
+
+ if is_negative_with_precision(neg_sle):
message = _(
"{0} units of {1} needed in {2} on {3} {4} for {5} to complete this transaction."
).format(
@@ -1365,7 +1367,7 @@ def validate_negative_qty_in_future_sle(args, allow_negative_stock=False):
return
neg_batch_sle = get_future_sle_with_negative_batch_qty(args)
- if neg_batch_sle:
+ if is_negative_with_precision(neg_batch_sle, is_batch=True):
message = _(
"{0} units of {1} needed in {2} on {3} {4} for {5} to complete this transaction."
).format(
@@ -1379,6 +1381,22 @@ def validate_negative_qty_in_future_sle(args, allow_negative_stock=False):
frappe.throw(message, NegativeStockError, title=_("Insufficient Stock for Batch"))
+def is_negative_with_precision(neg_sle, is_batch=False):
+ """
+ Returns whether system precision rounded qty is insufficient.
+ E.g: -0.0003 in precision 3 (0.000) is sufficient for the user.
+ """
+
+ if not neg_sle:
+ return False
+
+ field = "cumulative_total" if is_batch else "qty_after_transaction"
+ precision = cint(frappe.db.get_default("float_precision")) or 2
+ qty_deficit = flt(neg_sle[0][field], precision)
+
+ return qty_deficit < 0 and abs(qty_deficit) > 0.0001
+
+
def get_future_sle_with_negative_qty(args):
return frappe.db.sql(
"""
diff --git a/erpnext/translations/ru.csv b/erpnext/translations/ru.csv
index 8c614c02e88..656c15a7908 100644
--- a/erpnext/translations/ru.csv
+++ b/erpnext/translations/ru.csv
@@ -44,7 +44,7 @@ Accessable Value,Доступная стоимость,
Account,Аккаунт,
Account Number,Номер аккаунта,
Account Number {0} already used in account {1},"Номер счета {0}, уже использованный в учетной записи {1}",
-Account Pay Only,Счет Оплатить только,
+Account Pay Only,Только оплатить счет,
Account Type,Тип учетной записи,
Account Type for {0} must be {1},Тип счета для {0} должен быть {1},
"Account balance already in Credit, you are not allowed to set 'Balance Must Be' as 'Debit'","Баланс счета в Кредите, запрещена установка 'Баланс должен быть' как 'Дебет'",
@@ -117,7 +117,7 @@ Add Item,Добавить продукт,
Add Items,Добавить продукты,
Add Leads,Добавить лид,
Add Multiple Tasks,Добавить несколько задач,
-Add Row,Добавить ряд,
+Add Row,Добавить строку,
Add Sales Partners,Добавить партнеров по продажам,
Add Serial No,Добавить серийный номер,
Add Students,Добавить студентов,
@@ -692,7 +692,7 @@ Created {0} scorecards for {1} between: ,Созданы {0} оценочные
Creating Company and Importing Chart of Accounts,Создание компании и импорт плана счетов,
Creating Fees,Создание сборов,
Creating Payment Entries......,Создание платежных записей......,
-Creating Salary Slips...,Создание зарплатных листков...,
+Creating Salary Slips...,Создание зарплатных ведомостей...,
Creating student groups,Создание групп студентов,
Creating {0} Invoice,Создание {0} счета,
Credit,Кредит,
@@ -995,7 +995,7 @@ Expenses,Расходы,
Expenses Included In Asset Valuation,"Расходы, включенные в оценку активов",
Expenses Included In Valuation,"Затрат, включаемых в оценке",
Expired Batches,Просроченные партии,
-Expires On,Годен до,
+Expires On,Актуален до,
Expiring On,Срок действия,
Expiry (In Days),Срок действия (в днях),
Explore,Обзор,
@@ -1411,7 +1411,7 @@ Lab Test UOM,Лабораторная проверка UOM,
Lab Tests and Vital Signs,Лабораторные тесты и жизненные знаки,
Lab result datetime cannot be before testing datetime,Лабораторный результат datetime не может быть до тестирования даты и времени,
Lab testing datetime cannot be before collection datetime,Лабораторное тестирование datetime не может быть до даты сбора данных,
-Label,Ярлык,
+Label,Метка,
Laboratory,Лаборатория,
Language Name,Название языка,
Large,Большой,
@@ -2874,7 +2874,7 @@ Supplier Id,Id поставщика,
Supplier Invoice Date cannot be greater than Posting Date,"Дата Поставщик Счет не может быть больше, чем Дата публикации",
Supplier Invoice No,Поставщик Счет №,
Supplier Invoice No exists in Purchase Invoice {0},Номер счета поставщика отсутствует в счете на покупку {0},
-Supplier Name,наименование поставщика,
+Supplier Name,Наименование поставщика,
Supplier Part No,Деталь поставщика №,
Supplier Quotation,Предложение поставщика,
Supplier Scorecard,Оценочная карта поставщика,
@@ -3091,7 +3091,7 @@ Total Payment Amount in Payment Schedule must be equal to Grand / Rounded Total,
Total Payments,Всего платежей,
Total Present,Итого Текущая,
Total Qty,Общее количество,
-Total Quantity,Общая численность,
+Total Quantity,Общее количество,
Total Revenue,Общий доход,
Total Student,Всего учеников,
Total Target,Всего Target,
@@ -3498,7 +3498,7 @@ Postal,Почтовый,
Postal Code,Почтовый индекс,
Previous,Предыдущая,
Provider,Поставщик,
-Read Only,Только чтения,
+Read Only,Только чтение,
Recipient,Сторона-реципиент,
Reviews,Отзывы,
Sender,Отправитель,
@@ -3879,7 +3879,7 @@ On Lead Creation,Создание лида,
On Supplier Creation,Создание поставщика,
On Customer Creation,Создание клиента,
Only .csv and .xlsx files are supported currently,В настоящее время поддерживаются только файлы .csv и .xlsx,
-Only expired allocation can be cancelled,Только истекшее распределение может быть отменено,
+Only expired allocation can be cancelled,Отменить можно только просроченное распределение,
Only users with the {0} role can create backdated leave applications,Только пользователи с ролью {0} могут создавать оставленные приложения с задним сроком действия,
Open,Открыт,
Open Contact,Открытый контакт,
@@ -4046,7 +4046,7 @@ Server Error,Ошибка сервера,
Service Level Agreement has been changed to {0}.,Соглашение об уровне обслуживания изменено на {0}.,
Service Level Agreement was reset.,Соглашение об уровне обслуживания было сброшено.,
Service Level Agreement with Entity Type {0} and Entity {1} already exists.,Соглашение об уровне обслуживания с типом объекта {0} и объектом {1} уже существует.,
-Set,Задать,
+Set,Комплект,
Set Meta Tags,Установить метатеги,
Set {0} in company {1},Установить {0} в компании {1},
Setup,Настройки,
@@ -4059,7 +4059,7 @@ Show Stock Ageing Data,Показать данные о старении зап
Show Warehouse-wise Stock,Показать складской запас,
Size,Размер,
Something went wrong while evaluating the quiz.,Что-то пошло не так при оценке теста.,
-Sr,Sr,
+Sr,№,
Start,Начать,
Start Date cannot be before the current date,Дата начала не может быть раньше текущей даты,
Start Time,Время начала,
@@ -4513,7 +4513,7 @@ Mandatory For Profit and Loss Account,Обязательно для счета
Accounting Period,Период учета,
Period Name,Название периода,
Closed Documents,Закрытые документы,
-Accounts Settings,Настройки аккаунта,
+Accounts Settings,Настройка счетов,
Settings for Accounts,Настройки для счетов,
Make Accounting Entry For Every Stock Movement,Создавать бухгалтерские проводки при каждом перемещении запасов,
Users with this role are allowed to set frozen accounts and create / modify accounting entries against frozen accounts,"Пользователи с этой ролью могут замороживать счета, а также создавать / изменять бухгалтерские проводки замороженных счетов",
@@ -5084,8 +5084,8 @@ Allow Zero Valuation Rate,Разрешить нулевую оценку,
Item Tax Rate,Ставка налогов на продукт,
Tax detail table fetched from item master as a string and stored in this field.\nUsed for Taxes and Charges,Налоговый Подробная таблица выбирается из мастера элемента в виде строки и хранится в этой области.\n Используется по налогам и сборам,
Purchase Order Item,Заказ товара,
-Purchase Receipt Detail,Деталь квитанции о покупке,
-Item Weight Details,Деталь Вес Подробности,
+Purchase Receipt Detail,Сведения о квитанции о покупке,
+Item Weight Details,Сведения о весе товара,
Weight Per Unit,Вес на единицу,
Total Weight,Общий вес,
Weight UOM,Вес Единица измерения,
@@ -5198,7 +5198,7 @@ Address and Contacts,Адрес и контакты,
Contact List,Список контактов,
Hidden list maintaining the list of contacts linked to Shareholder,"Скрытый список, поддерживающий список контактов, связанных с Акционером",
Specify conditions to calculate shipping amount,Укажите условия для расчета суммы доставки,
-Shipping Rule Label,Название правила доставки,
+Shipping Rule Label,Метка правила доставки,
example: Next Day Shipping,Пример: доставка на следующий день,
Shipping Rule Type,Тип правила доставки,
Shipping Account,Счет доставки,
@@ -5236,7 +5236,7 @@ Billing Interval,Интервал выставления счетов,
Billing Interval Count,Счет интервала фактурирования,
"Number of intervals for the interval field e.g if Interval is 'Days' and Billing Interval Count is 3, invoices will be generated every 3 days","Количество интервалов для поля интервалов, например, если Interval является «Days», а количество интервалов фактурирования - 3, счета-фактуры будут генерироваться каждые 3 дня",
Payment Plan,Платежный план,
-Subscription Plan Detail,Деталь плана подписки,
+Subscription Plan Detail,Сведения о плана подписки,
Plan,План,
Subscription Settings,Настройки подписки,
Grace Period,Льготный период,
@@ -5802,7 +5802,7 @@ Make Academic Term Mandatory,Сделать академический срок
Skip User creation for new Student,Пропустить создание пользователя для нового студента,
"By default, a new User is created for every new Student. If enabled, no new User will be created when a new Student is created.","По умолчанию для каждого нового Студента создается новый Пользователь. Если этот параметр включен, при создании нового Студента новый Пользователь не создается.",
Instructor Records to be created by,Записи инструкторов должны быть созданы,
-Employee Number,Общее число сотрудников,
+Employee Number,Номер сотрудника,
Fee Category,Категория платы,
Fee Component,Компонент платы,
Fees Category,Категория плат,
@@ -6196,7 +6196,7 @@ Inpatient Occupancy,Стационарное размещение,
Occupancy Status,Статус занятости,
Vacant,Вакантно,
Occupied,Занято,
-Item Details,Детальная информация о товаре,
+Item Details,Детальная информация о продукте,
UOM Conversion in Hours,Преобразование UOM в часы,
Rate / UOM,Скорость / UOM,
Change in Item,Изменение продукта,
@@ -6868,8 +6868,8 @@ Only Tax Impact (Cannot Claim But Part of Taxable Income),Только нало
Create Separate Payment Entry Against Benefit Claim,Создать отдельную заявку на подачу заявки на получение пособия,
Condition and Formula,Состояние и формула,
Amount based on formula,Сумма на основе формулы,
-Formula,формула,
-Salary Detail,Заработная плата: Подробности,
+Formula,Формула,
+Salary Detail,Подробно об заработной плате,
Component,Компонент,
Do not include in total,Не включать в общей сложности,
Default Amount,По умолчанию количество,
@@ -6891,7 +6891,7 @@ Total Principal Amount,Общая сумма,
Total Interest Amount,Общая сумма процентов,
Total Loan Repayment,Общая сумма погашения кредита,
net pay info,Чистая информация платить,
-Gross Pay - Total Deduction - Loan Repayment,Gross Pay - Итого Вычет - Погашение кредита,
+Gross Pay - Total Deduction - Loan Repayment,Валовая заработная плата - Общий вычет - Погашение кредита,
Total in words,Всего в словах,
Net Pay (in words) will be visible once you save the Salary Slip.,"Чистая Платное (прописью) будут видны, как только вы сохраните Зарплата Слип.",
Salary Component for timesheet based payroll.,Заработная плата Компонент для расчета заработной платы на основе расписания.,
@@ -6961,7 +6961,7 @@ Trainer Email,Электронная почта Тренера,
Attendees,Присутствующие,
Employee Emails,Электронные почты сотрудников,
Training Event Employee,Обучение сотрудников Событие,
-Invited,приглашенный,
+Invited,Приглашенный,
Feedback Submitted,Отзыв отправлен,
Optional,Необязательный,
Training Result Employee,Результат обучения сотрудника,
@@ -7184,8 +7184,8 @@ Blanket Order Item,Элемент заказа одеяла,
Ordered Quantity,Заказанное количество,
Item to be manufactured or repacked,Продукт должен быть произведен или переупакован,
Quantity of item obtained after manufacturing / repacking from given quantities of raw materials,Количество пункта получены после изготовления / переупаковка от заданных величин сырья,
-Set rate of sub-assembly item based on BOM,Установить скорость элемента подзаголовки на основе спецификации,
-Allow Alternative Item,Разрешить альтернативный элемент,
+Set rate of sub-assembly item based on BOM,Установить скорость сборки на основе спецификации,
+Allow Alternative Item,Разрешить альтернативный продукт,
Item UOM,Единиц продукта,
Conversion Rate,Коэффициент конверсии,
Rate Of Materials Based On,Оценить материалов на основе,
@@ -7600,7 +7600,7 @@ Invoices with no Place Of Supply,Счета без места поставки,
Import Supplier Invoice,Импортная накладная поставщика,
Invoice Series,Серия счетов,
Upload XML Invoices,Загрузить XML-счета,
-Zip File,Zip-файл,
+Zip File,Zip файл,
Import Invoices,Импорт счетов,
Click on Import Invoices button once the zip file has been attached to the document. Any errors related to processing will be shown in the Error Log.,"Нажмите кнопку «Импортировать счета-фактуры», когда файл zip прикреплен к документу. Любые ошибки, связанные с обработкой, будут отображаться в журнале ошибок.",
Lower Deduction Certificate,Свидетельство о нижнем удержании,
@@ -7635,7 +7635,7 @@ Restaurant Order Entry Item,Номер заказа заказа рестора
Served,Подается,
Restaurant Reservation,Бронирование ресторанов,
Waitlisted,Лист ожидания,
-No Show,Нет шоу,
+No Show,Не показывать,
No of People,Нет людей,
Reservation Time,Время резервирования,
Reservation End Time,Время окончания бронирования,
@@ -7873,8 +7873,8 @@ Disable In Words,Отключить в словах,
"If disable, 'In Words' field will not be visible in any transaction","Если отключить, "В словах" поле не будет видно в любой сделке",
Item Classification,Продуктовая классификация,
General Settings,Основные настройки,
-Item Group Name,Пункт Название группы,
-Parent Item Group,Родитель Пункт Группа,
+Item Group Name,Название группы продуктов,
+Parent Item Group,Родительская группа продукта,
Item Group Defaults,Элемент группы по умолчанию,
Item Tax,Налог на продукт,
Check this if you want to show in website,"Проверьте это, если вы хотите показать в веб-сайт",
@@ -7971,13 +7971,13 @@ Customs Tariff Number,Номер таможенного тарифа,
Tariff Number,Тарифный номер,
Delivery To,Доставка,
MAT-DN-.YYYY.-,MAT-DN-.YYYY.-,
-Is Return,Является Вернуться,
+Is Return,Возврат,
Issue Credit Note,Кредитная кредитная карта,
-Return Against Delivery Note,Вернуться На накладной,
-Customer's Purchase Order No,Клиентам Заказ Нет,
+Return Against Delivery Note,Возврат по накладной,
+Customer's Purchase Order No,Заказ клиента №,
Billing Address Name,Название адреса для выставления счета,
Required only for sample item.,Требуется только для образца пункта.,
-"If you have created a standard template in Sales Taxes and Charges Template, select one and click on the button below.","Если вы создали стандартный шаблон в шаблонах Налоги с налогами и сбором платежей, выберите его и нажмите кнопку ниже.",
+"If you have created a standard template in Sales Taxes and Charges Template, select one and click on the button below.","Если вы создали стандартный шаблон в Шаблоне налогов и сборов с продаж, выберите его и нажмите кнопку ниже.",
In Words will be visible once you save the Delivery Note.,По словам будет виден только вы сохраните накладной.,
In Words (Export) will be visible once you save the Delivery Note.,В Слов (Экспорт) будут видны только вы сохраните накладной.,
Transporter Info,Информация для транспортировки,
@@ -7991,8 +7991,8 @@ Installation Status,Состояние установки,
Excise Page Number,Количество Акцизный Страница,
Instructions,Инструкции,
From Warehouse,Со склада,
-Against Sales Order,По Сделке,
-Against Sales Order Item,По Продукту Сделки,
+Against Sales Order,По сделке,
+Against Sales Order Item,По позиции сделки,
Against Sales Invoice,Повторная накладная,
Against Sales Invoice Item,Счет на продажу продукта,
Available Batch Qty at From Warehouse,Доступные Пакетная Кол-во на со склада,
@@ -8008,7 +8008,7 @@ Delivery Stop,Остановить доставку,
Lock,Заблокировано,
Visited,Посещен,
Order Information,запросить информацию,
-Contact Information,Контакты,
+Contact Information,Контактная информация,
Email sent to,Письмо отправлено,
Dispatch Information,Информация о доставке,
Estimated Arrival,ожидаемое прибытие,
@@ -8121,7 +8121,7 @@ Two-way,Двусторонний,
Alternative Item Name,Альтернативное название продукта,
Attribute Name,Название атрибута,
Numeric Values,Числовые значения,
-From Range,От хребта,
+From Range,Из диапазона,
Increment,Приращение,
To Range,В диапазоне,
Item Attribute Values,Пункт значений атрибутов,
@@ -8143,7 +8143,7 @@ Default Supplier,Поставщик по умолчанию,
Default Expense Account,Счет учета затрат по умолчанию,
Sales Defaults,По умолчанию,
Default Selling Cost Center,По умолчанию Продажа Стоимость центр,
-Item Manufacturer,Пункт Производитель,
+Item Manufacturer,Производитель товара,
Item Price,Цена продукта,
Packing Unit,Упаковочный блок,
Quantity that must be bought or sold per UOM,"Количество, которое необходимо купить или продать за UOM",
@@ -8177,7 +8177,7 @@ Purchase Receipts,Покупка Поступления,
Purchase Receipt Items,Покупка продуктов,
Get Items From Purchase Receipts,Получить продукты из покупки.,
Distribute Charges Based On,Распределите платежи на основе,
-Landed Cost Help,Земельные Стоимость Помощь,
+Landed Cost Help,Справка по стоимости доставки,
Manufacturers used in Items,Производители использовали в пунктах,
Limited to 12 characters,Ограничено до 12 символов,
MAT-MR-.YYYY.-,МАТ-MR-.YYYY.-,
@@ -8186,13 +8186,13 @@ Transferred,Переданы,
% Ordered,% заказано,
Terms and Conditions Content,Условия Содержимое,
Quantity and Warehouse,Количество и Склад,
-Lead Time Date,Время и Дата Лида,
-Min Order Qty,Минимальный заказ Кол-во,
+Lead Time Date,Дата выполнения заказа,
+Min Order Qty,Минимальное количество для заказа,
Packed Item,Упаковано,
To Warehouse (Optional),На склад (Необязательно),
Actual Batch Quantity,Фактическое количество партий,
Prevdoc DocType,Prevdoc DocType,
-Parent Detail docname,Родитель Деталь DOCNAME,
+Parent Detail docname,Сведения о родителе docname,
"Generate packing slips for packages to be delivered. Used to notify package number, package contents and its weight.","Создаёт упаковочные листы к упаковкам для доставки. Содержит номер упаковки, перечень содержимого и вес.",
Indicates that the package is a part of this delivery (Only Draft),"Указывает, что пакет является частью этой поставки (только проект)",
MAT-PAC-.YYYY.-,MAT-PAC-.YYYY.-,
@@ -8353,7 +8353,7 @@ Automatically Set Serial Nos based on FIFO,Автоматически устан
Auto Material Request,Автоматический запрос материалов,
Inter Warehouse Transfer Settings,Настройки передачи между складами,
Freeze Stock Entries,Замораживание поступления запасов,
-Stock Frozen Upto,Фото Замороженные До,
+Stock Frozen Upto,Остатки заморожены до,
Batch Identification,Идентификация партии,
Use Naming Series,Использовать Идентификаторы по Имени,
Naming Series Prefix,Префикс Идентификации по Имени,
@@ -8372,7 +8372,7 @@ Issue Split From,Выпуск Сплит От,
Service Level,Уровень обслуживания,
Response By,Ответ от,
Response By Variance,Ответ по отклонениям,
-Ongoing,постоянный,
+Ongoing,Постоянный,
Resolution By,Разрешение по,
Resolution By Variance,Разрешение по отклонениям,
Service Level Agreement Creation,Создание соглашения об уровне обслуживания,