mirror of
https://github.com/frappe/erpnext.git
synced 2026-04-25 17:48:30 +00:00
Merge branch 'develop' into holiday-list-assignment
This commit is contained in:
@@ -523,6 +523,9 @@ class PurchaseOrder(BuyingController):
|
||||
if self.is_against_so():
|
||||
self.update_status_updater()
|
||||
|
||||
if self.is_against_pp():
|
||||
self.update_status_updater_if_from_pp()
|
||||
|
||||
if self.has_drop_ship_item():
|
||||
self.update_delivered_qty_in_sales_order()
|
||||
|
||||
@@ -1007,6 +1010,13 @@ def get_mapped_subcontracting_order(source_name, target_doc=None):
|
||||
"Job Card", item.job_card, "wip_warehouse"
|
||||
)
|
||||
|
||||
production_plan = set([item.production_plan for item in source_doc.items if item.production_plan])
|
||||
if production_plan:
|
||||
target_doc.production_plan = production_plan.pop()
|
||||
target_doc.reserve_stock = frappe.get_single_value(
|
||||
"Stock Settings", "auto_reserve_stock"
|
||||
) or frappe.get_value("Production Plan", target_doc.production_plan, "reserve_stock")
|
||||
|
||||
if target_doc and isinstance(target_doc, str):
|
||||
target_doc = json.loads(target_doc)
|
||||
for key in ["service_items", "items", "supplied_items"]:
|
||||
|
||||
@@ -830,6 +830,7 @@
|
||||
"fieldname": "production_plan",
|
||||
"fieldtype": "Link",
|
||||
"label": "Production Plan",
|
||||
"no_copy": 1,
|
||||
"options": "Production Plan",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
@@ -948,7 +949,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2025-10-12 10:57:31.552812",
|
||||
"modified": "2025-10-30 16:51:56.761673",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Buying",
|
||||
"name": "Purchase Order Item",
|
||||
|
||||
@@ -12,7 +12,6 @@ from frappe.utils import cint, flt, format_datetime, get_datetime
|
||||
|
||||
import erpnext
|
||||
from erpnext.stock.serial_batch_bundle import get_batches_from_bundle
|
||||
from erpnext.stock.serial_batch_bundle import get_serial_nos as get_serial_nos_from_bundle
|
||||
from erpnext.stock.utils import get_combine_datetime, get_incoming_rate, get_valuation_method
|
||||
|
||||
|
||||
@@ -145,7 +144,7 @@ def validate_returned_items(doc):
|
||||
ref.rate
|
||||
and flt(d.rate) > ref.rate
|
||||
and doc.doctype in ("Delivery Note", "Sales Invoice")
|
||||
and get_valuation_method(ref.item_code) != "Moving Average"
|
||||
and get_valuation_method(ref.item_code, doc.company) != "Moving Average"
|
||||
):
|
||||
frappe.throw(
|
||||
_("Row # {0}: Rate cannot be greater than the rate used in {1} {2}").format(
|
||||
|
||||
@@ -524,7 +524,7 @@ class SellingController(StockController):
|
||||
)
|
||||
|
||||
if not self.get("return_against") or (
|
||||
get_valuation_method(d.item_code) == "Moving Average"
|
||||
get_valuation_method(d.item_code, self.company) == "Moving Average"
|
||||
and self.get("is_return")
|
||||
and not item_details.has_serial_no
|
||||
and not item_details.has_batch_no
|
||||
@@ -535,7 +535,10 @@ class SellingController(StockController):
|
||||
if (
|
||||
not d.incoming_rate
|
||||
or self.is_internal_transfer()
|
||||
or (get_valuation_method(d.item_code) == "Moving Average" and self.get("is_return"))
|
||||
or (
|
||||
get_valuation_method(d.item_code, self.company) == "Moving Average"
|
||||
and self.get("is_return")
|
||||
)
|
||||
):
|
||||
d.incoming_rate = get_incoming_rate(
|
||||
{
|
||||
@@ -560,7 +563,7 @@ class SellingController(StockController):
|
||||
not d.incoming_rate
|
||||
and self.get("return_against")
|
||||
and self.get("is_return")
|
||||
and get_valuation_method(d.item_code) == "Moving Average"
|
||||
and get_valuation_method(d.item_code, self.company) == "Moving Average"
|
||||
):
|
||||
d.incoming_rate = get_rate_for_return(
|
||||
self.doctype, self.name, d.item_code, self.return_against, item_row=d
|
||||
|
||||
@@ -1645,6 +1645,128 @@ class StockController(AccountsController):
|
||||
|
||||
gl_entries.append(self.get_gl_dict(gl_entry, item=item))
|
||||
|
||||
def update_stock_reservation_entries(self):
|
||||
def get_sre_list():
|
||||
table = frappe.qb.DocType("Stock Reservation Entry")
|
||||
query = (
|
||||
frappe.qb.from_(table)
|
||||
.select(table.name)
|
||||
.where(
|
||||
(table.docstatus == 1)
|
||||
& (table.voucher_type == data_map[purpose or self.doctype]["voucher_type"])
|
||||
& (
|
||||
table.voucher_no
|
||||
== data_map[purpose or self.doctype].get(
|
||||
"voucher_no", item.get("subcontracting_order")
|
||||
)
|
||||
)
|
||||
)
|
||||
.orderby(table.creation)
|
||||
)
|
||||
if reference_field := data_map[purpose or self.doctype].get("voucher_detail_no_field"):
|
||||
query = query.where(table.voucher_detail_no == item.get(reference_field))
|
||||
else:
|
||||
query = query.where(
|
||||
(table.item_code == item.rm_item_code) & (table.warehouse == self.supplier_warehouse)
|
||||
)
|
||||
|
||||
return query.run(pluck="name")
|
||||
|
||||
def get_data_map():
|
||||
return {
|
||||
"Subcontracting Delivery": {
|
||||
"table_name": "items",
|
||||
"voucher_type": "Subcontracting Inward Order",
|
||||
"voucher_no": self.get("subcontracting_inward_order"),
|
||||
"voucher_detail_no_field": "scio_detail",
|
||||
"field": "delivered_qty",
|
||||
},
|
||||
"Send to Subcontractor": {
|
||||
"table_name": "items",
|
||||
"voucher_type": "Subcontracting Order",
|
||||
"voucher_no": self.get("subcontracting_order"),
|
||||
"voucher_detail_no_field": "sco_rm_detail",
|
||||
"field": "transferred_qty",
|
||||
},
|
||||
"Subcontracting Receipt": {
|
||||
"table_name": "supplied_items",
|
||||
"voucher_type": "Subcontracting Order",
|
||||
"field": "consumed_qty",
|
||||
},
|
||||
}
|
||||
|
||||
purpose = self.get("purpose")
|
||||
if (
|
||||
purpose == "Subcontracting Delivery"
|
||||
or (
|
||||
purpose == "Send to Subcontractor"
|
||||
and frappe.get_value("Subcontracting Order", self.subcontracting_order, "reserve_stock")
|
||||
)
|
||||
or (self.doctype == "Subcontracting Receipt" and self.has_reserved_stock() and not self.is_return)
|
||||
):
|
||||
data_map = get_data_map()
|
||||
|
||||
field = data_map[purpose or self.doctype]["field"]
|
||||
for item in self.get(data_map[purpose or self.doctype]["table_name"]):
|
||||
sre_list = get_sre_list()
|
||||
|
||||
if not sre_list:
|
||||
continue
|
||||
|
||||
qty = item.get("transfer_qty", item.get("consumed_qty"))
|
||||
for sre in sre_list:
|
||||
if qty <= 0:
|
||||
break
|
||||
|
||||
sre_doc = frappe.get_doc("Stock Reservation Entry", sre)
|
||||
|
||||
working_qty = 0
|
||||
if sre_doc.reservation_based_on == "Serial and Batch":
|
||||
sbb = frappe.get_doc("Serial and Batch Bundle", item.serial_and_batch_bundle)
|
||||
if sre_doc.has_serial_no:
|
||||
serial_nos = [d.serial_no for d in sbb.entries]
|
||||
for entry in sre_doc.sb_entries:
|
||||
if entry.serial_no in serial_nos:
|
||||
entry.delivered_qty = 1 if self._action == "submit" else 0
|
||||
entry.db_update()
|
||||
working_qty += 1
|
||||
serial_nos.remove(entry.serial_no)
|
||||
else:
|
||||
batch_qty = {d.batch_no: -1 * d.qty for d in sbb.entries}
|
||||
for entry in sre_doc.sb_entries:
|
||||
if entry.batch_no in batch_qty:
|
||||
delivered_qty = min(
|
||||
(entry.qty - entry.delivered_qty)
|
||||
if self._action == "submit"
|
||||
else entry.delivered_qty,
|
||||
batch_qty[entry.batch_no],
|
||||
)
|
||||
entry.delivered_qty += (
|
||||
delivered_qty if self._action == "submit" else (-1 * delivered_qty)
|
||||
)
|
||||
entry.db_update()
|
||||
working_qty += delivered_qty
|
||||
batch_qty[entry.batch_no] -= delivered_qty
|
||||
else:
|
||||
working_qty = min(
|
||||
(sre_doc.reserved_qty - sre_doc.get(field))
|
||||
if self._action == "submit"
|
||||
else sre_doc.get(field),
|
||||
qty,
|
||||
)
|
||||
|
||||
sre_doc.set(
|
||||
field,
|
||||
sre_doc.get(field)
|
||||
+ (working_qty if self._action == "submit" else (-1 * working_qty)),
|
||||
)
|
||||
sre_doc.db_update()
|
||||
sre_doc.update_reserved_qty_in_voucher()
|
||||
sre_doc.update_status()
|
||||
sre_doc.update_reserved_stock_in_bin()
|
||||
|
||||
qty -= working_qty
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def show_accounting_ledger_preview(company, doctype, docname):
|
||||
|
||||
@@ -497,11 +497,10 @@ class SubcontractingController(StockController):
|
||||
|
||||
if row.serial_no:
|
||||
details.serial_no.extend(get_serial_nos(row.serial_no))
|
||||
|
||||
elif row.batch_no:
|
||||
if row.batch_no:
|
||||
details.batch_no[row.batch_no] += row.qty
|
||||
|
||||
elif voucher_bundle_data:
|
||||
if not row.serial_no and not row.batch_no and voucher_bundle_data:
|
||||
bundle_key = (row.rm_item_code, row.main_item_code, row.t_warehouse, row.voucher_no)
|
||||
|
||||
bundle_data = voucher_bundle_data.get(bundle_key, frappe._dict())
|
||||
@@ -551,6 +550,8 @@ class SubcontractingController(StockController):
|
||||
frappe.delete_doc("Serial and Batch Bundle", item.serial_and_batch_bundle, force=True)
|
||||
|
||||
def __get_materials_from_bom(self, item_code, bom_no, exploded_item=0):
|
||||
data = []
|
||||
|
||||
doctype = "BOM Item" if not exploded_item else "BOM Explosion Item"
|
||||
fields = [f"`tab{doctype}`.`stock_qty` / `tabBOM`.`quantity` as qty_consumed_per_unit"]
|
||||
|
||||
@@ -559,7 +560,7 @@ class SubcontractingController(StockController):
|
||||
"name": "bom_detail_no",
|
||||
"source_warehouse": "reserve_warehouse",
|
||||
}
|
||||
for field in [
|
||||
fields_list = [
|
||||
"item_code",
|
||||
"name",
|
||||
"rate",
|
||||
@@ -568,7 +569,12 @@ class SubcontractingController(StockController):
|
||||
"description",
|
||||
"item_name",
|
||||
"stock_uom",
|
||||
]:
|
||||
]
|
||||
|
||||
if doctype == "BOM Item":
|
||||
fields_list.extend(["is_phantom_item", "bom_no"])
|
||||
|
||||
for field in fields_list:
|
||||
fields.append(f"`tab{doctype}`.`{field}` As {alias_dict.get(field, field)}")
|
||||
|
||||
filters = [
|
||||
@@ -578,7 +584,19 @@ class SubcontractingController(StockController):
|
||||
[doctype, "sourced_by_supplier", "=", 0],
|
||||
]
|
||||
|
||||
return frappe.get_all("BOM", fields=fields, filters=filters, order_by=f"`tab{doctype}`.`idx`") or []
|
||||
data = frappe.get_all("BOM", fields=fields, filters=filters, order_by=f"`tab{doctype}`.`idx`") or []
|
||||
to_remove = []
|
||||
for item in data:
|
||||
if item.is_phantom_item:
|
||||
data += self.__get_materials_from_bom(
|
||||
item.rm_item_code, item.bom_no, exploded_item=exploded_item
|
||||
)
|
||||
to_remove.append(item)
|
||||
|
||||
for item in to_remove:
|
||||
data.remove(item)
|
||||
|
||||
return data
|
||||
|
||||
def __update_reserve_warehouse(self, row, item):
|
||||
if (
|
||||
|
||||
@@ -556,131 +556,6 @@ class SubcontractingInwardController:
|
||||
item.basic_rate + (item.additional_cost / item.transfer_qty), item.precision("basic_rate")
|
||||
)
|
||||
|
||||
def update_sre_for_subcontracting_delivery(self) -> None:
|
||||
if self.purpose == "Subcontracting Delivery":
|
||||
if self._action == "submit":
|
||||
self.update_sre_for_subcontracting_delivery_submit()
|
||||
elif self._action == "cancel":
|
||||
self.update_sre_for_subcontracting_delivery_cancel()
|
||||
|
||||
def update_sre_for_subcontracting_delivery_submit(self):
|
||||
for item in self.get("items"):
|
||||
table = frappe.qb.DocType("Stock Reservation Entry")
|
||||
query = (
|
||||
frappe.qb.from_(table)
|
||||
.select(table.name)
|
||||
.where(
|
||||
(table.docstatus == 1)
|
||||
& (table.voucher_type == "Subcontracting Inward Order")
|
||||
& (table.voucher_no == self.subcontracting_inward_order)
|
||||
& (table.voucher_detail_no == item.scio_detail)
|
||||
)
|
||||
.orderby(table.creation)
|
||||
)
|
||||
sre_list = query.run(pluck="name")
|
||||
|
||||
if not sre_list:
|
||||
continue
|
||||
|
||||
qty_to_deliver = item.transfer_qty
|
||||
for sre in sre_list:
|
||||
if qty_to_deliver <= 0:
|
||||
break
|
||||
|
||||
sre_doc = frappe.get_doc("Stock Reservation Entry", sre)
|
||||
|
||||
qty_can_be_deliver = 0
|
||||
if sre_doc.reservation_based_on == "Serial and Batch":
|
||||
sbb = frappe.get_doc("Serial and Batch Bundle", item.serial_and_batch_bundle)
|
||||
if sre_doc.has_serial_no:
|
||||
delivered_serial_nos = [d.serial_no for d in sbb.entries]
|
||||
for entry in sre_doc.sb_entries:
|
||||
if entry.serial_no in delivered_serial_nos:
|
||||
entry.delivered_qty = 1
|
||||
entry.db_update()
|
||||
qty_can_be_deliver += 1
|
||||
delivered_serial_nos.remove(entry.serial_no)
|
||||
else:
|
||||
delivered_batch_qty = {d.batch_no: -1 * d.qty for d in sbb.entries}
|
||||
for entry in sre_doc.sb_entries:
|
||||
if entry.batch_no in delivered_batch_qty:
|
||||
delivered_qty = min(
|
||||
(entry.qty - entry.delivered_qty),
|
||||
delivered_batch_qty[entry.batch_no],
|
||||
)
|
||||
entry.delivered_qty += delivered_qty
|
||||
entry.db_update()
|
||||
qty_can_be_deliver += delivered_qty
|
||||
delivered_batch_qty[entry.batch_no] -= delivered_qty
|
||||
else:
|
||||
qty_can_be_deliver = min((sre_doc.reserved_qty - sre_doc.delivered_qty), qty_to_deliver)
|
||||
|
||||
sre_doc.delivered_qty += qty_can_be_deliver
|
||||
sre_doc.db_update()
|
||||
sre_doc.update_status()
|
||||
sre_doc.update_reserved_stock_in_bin()
|
||||
|
||||
qty_to_deliver -= qty_can_be_deliver
|
||||
|
||||
def update_sre_for_subcontracting_delivery_cancel(self):
|
||||
for item in self.get("items"):
|
||||
table = frappe.qb.DocType("Stock Reservation Entry")
|
||||
query = (
|
||||
frappe.qb.from_(table)
|
||||
.select(table.name)
|
||||
.where(
|
||||
(table.docstatus == 1)
|
||||
& (table.voucher_type == "Subcontracting Inward Order")
|
||||
& (table.voucher_no == self.subcontracting_inward_order)
|
||||
& (table.voucher_detail_no == item.scio_detail)
|
||||
& (table.warehouse == item.s_warehouse)
|
||||
)
|
||||
.orderby(table.creation)
|
||||
)
|
||||
sre_list = query.run(pluck="name")
|
||||
|
||||
if not sre_list:
|
||||
continue
|
||||
|
||||
qty_to_undelivered = item.transfer_qty
|
||||
for sre in sre_list:
|
||||
if qty_to_undelivered <= 0:
|
||||
break
|
||||
|
||||
sre_doc = frappe.get_doc("Stock Reservation Entry", sre)
|
||||
|
||||
qty_can_be_undelivered = 0
|
||||
if sre_doc.reservation_based_on == "Serial and Batch":
|
||||
sbb = frappe.get_doc("Serial and Batch Bundle", item.serial_and_batch_bundle)
|
||||
if sre_doc.has_serial_no:
|
||||
serial_nos_to_undelivered = [d.serial_no for d in sbb.entries]
|
||||
for entry in sre_doc.sb_entries:
|
||||
if entry.serial_no in serial_nos_to_undelivered:
|
||||
entry.delivered_qty = 0
|
||||
entry.db_update()
|
||||
qty_can_be_undelivered += 1
|
||||
serial_nos_to_undelivered.remove(entry.serial_no)
|
||||
else:
|
||||
batch_qty_to_undelivered = {d.batch_no: -1 * d.qty for d in sbb.entries}
|
||||
for entry in sre_doc.sb_entries:
|
||||
if entry.batch_no in batch_qty_to_undelivered:
|
||||
undelivered_qty = min(
|
||||
entry.delivered_qty, batch_qty_to_undelivered[entry.batch_no]
|
||||
)
|
||||
entry.delivered_qty -= undelivered_qty
|
||||
entry.db_update()
|
||||
qty_can_be_undelivered += undelivered_qty
|
||||
batch_qty_to_undelivered[entry.batch_no] -= undelivered_qty
|
||||
else:
|
||||
qty_can_be_undelivered = min(sre_doc.delivered_qty, qty_to_undelivered)
|
||||
|
||||
sre_doc.delivered_qty -= qty_can_be_undelivered
|
||||
sre_doc.db_update()
|
||||
sre_doc.update_status()
|
||||
sre_doc.update_reserved_stock_in_bin()
|
||||
|
||||
qty_to_undelivered -= qty_can_be_undelivered
|
||||
|
||||
def validate_receive_from_customer_cancel(self):
|
||||
if self.purpose == "Receive from Customer":
|
||||
for item in self.items:
|
||||
|
||||
@@ -1141,6 +1141,28 @@ class TestSubcontractingController(IntegrationTestCase):
|
||||
itemwise_details.get(doc.items[0].item_code)["serial_no"][5:6],
|
||||
)
|
||||
|
||||
def test_phantom_bom_explosion(self):
|
||||
from erpnext.manufacturing.doctype.bom.test_bom import create_tree_for_phantom_bom_tests
|
||||
|
||||
expected = create_tree_for_phantom_bom_tests()
|
||||
service_items = [
|
||||
{
|
||||
"warehouse": "_Test Warehouse - _TC",
|
||||
"item_code": "Subcontracted Service Item 11",
|
||||
"qty": 5,
|
||||
"rate": 100,
|
||||
"fg_item": "Top Level Parent",
|
||||
"fg_item_qty": 5,
|
||||
},
|
||||
]
|
||||
sco = get_subcontracting_order(service_items=service_items, do_not_submit=True)
|
||||
sco.items[0].include_exploded_items = 0
|
||||
sco.save()
|
||||
sco.submit()
|
||||
sco.reload()
|
||||
|
||||
self.assertEqual([item.rm_item_code for item in sco.supplied_items], expected)
|
||||
|
||||
|
||||
def add_second_row_in_scr(scr):
|
||||
item_dict = {}
|
||||
@@ -1308,7 +1330,12 @@ def make_subcontracted_items():
|
||||
"Subcontracted Item SA7": {},
|
||||
"Subcontracted Item SA8": {},
|
||||
"Subcontracted Item SA9": {"stock_uom": "Litre"},
|
||||
"Subcontracted Item SA10": {},
|
||||
"Subcontracted Item SA10": {
|
||||
"has_batch_no": 1,
|
||||
"create_new_batch": 1,
|
||||
"batch_number_series": "SBAT.####",
|
||||
},
|
||||
"Top Level Parent": {},
|
||||
}
|
||||
|
||||
for item, properties in sub_contracted_items.items():
|
||||
@@ -1360,6 +1387,7 @@ def make_service_items():
|
||||
"Subcontracted Service Item 8": {},
|
||||
"Subcontracted Service Item 9": {},
|
||||
"Subcontracted Service Item 10": {},
|
||||
"Subcontracted Service Item 11": {},
|
||||
}
|
||||
|
||||
for item, properties in service_items.items():
|
||||
@@ -1385,6 +1413,7 @@ def make_bom_for_subcontracted_items():
|
||||
"Subcontracted Item SA7": ["Subcontracted SRM Item 1"],
|
||||
"Subcontracted Item SA8": ["Subcontracted SRM Item 8"],
|
||||
"Subcontracted Item SA10": ["Subcontracted SRM Item 10"],
|
||||
"Subcontracted Service Item 11": ["Top Level Parent"],
|
||||
}
|
||||
|
||||
for item_code, raw_materials in boms.items():
|
||||
|
||||
@@ -3,7 +3,7 @@ msgstr ""
|
||||
"Project-Id-Version: frappe\n"
|
||||
"Report-Msgid-Bugs-To: hello@frappe.io\n"
|
||||
"POT-Creation-Date: 2025-11-10 12:11+0000\n"
|
||||
"PO-Revision-Date: 2025-11-10 21:17\n"
|
||||
"PO-Revision-Date: 2025-11-15 21:35\n"
|
||||
"Last-Translator: hello@frappe.io\n"
|
||||
"Language-Team: Arabic\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -1050,7 +1050,7 @@ msgstr ""
|
||||
#. Label of the abbr (Data) field in DocType 'Company'
|
||||
#: erpnext/setup/doctype/company/company.json
|
||||
msgid "Abbr"
|
||||
msgstr "اسم مختصر"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the abbr (Data) field in DocType 'Item Attribute Value'
|
||||
#: erpnext/stock/doctype/item_attribute_value/item_attribute_value.json
|
||||
@@ -1231,7 +1231,7 @@ msgstr "تفاصيل الحساب"
|
||||
#: erpnext/accounts/doctype/purchase_taxes_and_charges/purchase_taxes_and_charges.json
|
||||
#: erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json
|
||||
msgid "Account Head"
|
||||
msgstr "رئيس حساب"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the account_manager (Link) field in DocType 'Customer'
|
||||
#: erpnext/selling/doctype/customer/customer.json
|
||||
@@ -1545,7 +1545,7 @@ msgstr "المحاسبة"
|
||||
#: erpnext/subcontracting/doctype/subcontracting_receipt_item/subcontracting_receipt_item.json
|
||||
#: erpnext/subcontracting/doctype/subcontracting_receipt_supplied_item/subcontracting_receipt_supplied_item.json
|
||||
msgid "Accounting Details"
|
||||
msgstr "تفاصيل المحاسبة"
|
||||
msgstr ""
|
||||
|
||||
#. Name of a DocType
|
||||
#. Label of the accounting_dimension (Select) field in DocType 'Accounting
|
||||
@@ -2150,7 +2150,7 @@ msgstr ""
|
||||
#: erpnext/crm/doctype/opportunity/opportunity.json
|
||||
#: erpnext/crm/doctype/prospect/prospect.json
|
||||
msgid "Activities"
|
||||
msgstr "أنشطة"
|
||||
msgstr ""
|
||||
|
||||
#. Name of a DocType
|
||||
#. Label of a Link in the Projects Workspace
|
||||
@@ -3009,7 +3009,7 @@ msgstr ""
|
||||
#. Label of the advance_account (Link) field in DocType 'Party Account'
|
||||
#: erpnext/accounts/doctype/party_account/party_account.json
|
||||
msgid "Advance Account"
|
||||
msgstr "حساب مقدم"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/utilities/transaction_base.py:215
|
||||
msgid "Advance Account: {0} must be in either customer billing currency: {1} or Company default currency: {2}"
|
||||
@@ -3027,7 +3027,7 @@ msgstr "المبلغ مقدما"
|
||||
#: erpnext/buying/doctype/purchase_order/purchase_order.json
|
||||
#: erpnext/selling/doctype/sales_order/sales_order.json
|
||||
msgid "Advance Paid"
|
||||
msgstr "مسبقا المدفوعة"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/buying/doctype/purchase_order/purchase_order_list.js:75
|
||||
#: erpnext/selling/doctype/sales_order/sales_order_list.js:122
|
||||
@@ -3130,7 +3130,7 @@ msgstr ""
|
||||
#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
|
||||
#: erpnext/accounts/doctype/sales_invoice/sales_invoice.json
|
||||
msgid "Advances"
|
||||
msgstr "الدفعات المقدمة"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/setup/setup_wizard/data/marketing_source.txt:3
|
||||
msgid "Advertisement"
|
||||
@@ -6818,7 +6818,7 @@ msgstr "معلومات الحساب البنكي"
|
||||
#: erpnext/accounts/doctype/payment_entry/payment_entry.json
|
||||
#: erpnext/accounts/doctype/payment_request/payment_request.json
|
||||
msgid "Bank Account No"
|
||||
msgstr "رقم الحساب البنكي"
|
||||
msgstr ""
|
||||
|
||||
#. Name of a DocType
|
||||
#: erpnext/accounts/doctype/bank_account_subtype/bank_account_subtype.json
|
||||
@@ -6920,7 +6920,7 @@ msgstr "نوع الضمان المصرفي"
|
||||
#: erpnext/accounts/doctype/cheque_print_template/cheque_print_template.json
|
||||
#: erpnext/setup/doctype/employee/employee.json
|
||||
msgid "Bank Name"
|
||||
msgstr "اسم المصرف"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py:98
|
||||
#: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py:142
|
||||
@@ -7409,7 +7409,7 @@ msgstr "قبل المصالحة"
|
||||
#. Label of the start (Int) field in DocType 'Task'
|
||||
#: erpnext/projects/doctype/task/task.json
|
||||
msgid "Begin On (Days)"
|
||||
msgstr "ابدأ (بالأيام)"
|
||||
msgstr ""
|
||||
|
||||
#. Option for the 'Generate Invoice At' (Select) field in DocType
|
||||
#. 'Subscription'
|
||||
@@ -7795,7 +7795,7 @@ msgstr "فصيلة الدم"
|
||||
#. Accounts'
|
||||
#: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json
|
||||
msgid "Body"
|
||||
msgstr "الجسم"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the body_text (Text Editor) field in DocType 'Dunning'
|
||||
#. Label of the body_text (Text Editor) field in DocType 'Dunning Letter Text'
|
||||
@@ -10418,7 +10418,7 @@ msgstr "وصف الشركة"
|
||||
#. 'Employee'
|
||||
#: erpnext/setup/doctype/employee/employee.json
|
||||
msgid "Company Details"
|
||||
msgstr "تفاصيل الشركة"
|
||||
msgstr ""
|
||||
|
||||
#. Option for the 'Preferred Contact Email' (Select) field in DocType
|
||||
#. 'Employee'
|
||||
@@ -11010,7 +11010,7 @@ msgstr "اسم جهة الاتصال"
|
||||
#. Label of the contact_no (Data) field in DocType 'Sales Team'
|
||||
#: erpnext/selling/doctype/sales_team/sales_team.json
|
||||
msgid "Contact No."
|
||||
msgstr "الاتصال رقم"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the contact_person (Link) field in DocType 'Dunning'
|
||||
#. Label of the contact_person (Link) field in DocType 'POS Invoice'
|
||||
@@ -11343,7 +11343,7 @@ msgstr ""
|
||||
#. Label of the cost (Currency) field in DocType 'Subscription Plan'
|
||||
#: erpnext/accounts/doctype/subscription_plan/subscription_plan.json
|
||||
msgid "Cost"
|
||||
msgstr "كلفة"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the cost_center (Link) field in DocType 'Account Closing Balance'
|
||||
#. Label of the cost_center (Link) field in DocType 'Advance Taxes and Charges'
|
||||
@@ -11658,7 +11658,7 @@ msgstr ""
|
||||
#: erpnext/manufacturing/doctype/bom_operation/bom_operation.json
|
||||
#: erpnext/projects/doctype/task/task.json
|
||||
msgid "Costing"
|
||||
msgstr "تكلف"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the costing_amount (Currency) field in DocType 'Timesheet Detail'
|
||||
#. Label of the base_costing_amount (Currency) field in DocType 'Timesheet
|
||||
@@ -12350,7 +12350,7 @@ msgstr "الدائنين"
|
||||
#. Label of the criteria (Table) field in DocType 'Supplier Scorecard Period'
|
||||
#: erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.json
|
||||
msgid "Criteria"
|
||||
msgstr "المعايير"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the formula (Small Text) field in DocType 'Supplier Scorecard
|
||||
#. Criteria'
|
||||
@@ -13499,7 +13499,7 @@ msgstr ""
|
||||
#. Label of the date_of_birth (Date) field in DocType 'Employee'
|
||||
#: erpnext/setup/doctype/employee/employee.json
|
||||
msgid "Date of Birth"
|
||||
msgstr "تاريخ الميلاد"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/setup/doctype/employee/employee.py:147
|
||||
msgid "Date of Birth cannot be greater than today."
|
||||
@@ -13532,7 +13532,7 @@ msgstr "تاريخ الإصدار"
|
||||
#. Label of the date_of_joining (Date) field in DocType 'Employee'
|
||||
#: erpnext/setup/doctype/employee/employee.json
|
||||
msgid "Date of Joining"
|
||||
msgstr "تاريخ الالتحاق بالعمل"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py:273
|
||||
msgid "Date of Transaction"
|
||||
@@ -13851,7 +13851,7 @@ msgstr "الخصومات أو الخسارة"
|
||||
#: erpnext/accounts/doctype/mode_of_payment_account/mode_of_payment_account.json
|
||||
#: erpnext/accounts/doctype/party_account/party_account.json
|
||||
msgid "Default Account"
|
||||
msgstr "الافتراضي حساب"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the default_accounts_section (Section Break) field in DocType
|
||||
#. 'Supplier'
|
||||
@@ -14359,7 +14359,7 @@ msgstr "المصروفات المؤجلة"
|
||||
#: erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
|
||||
#: erpnext/stock/doctype/item_default/item_default.json
|
||||
msgid "Deferred Expense Account"
|
||||
msgstr "حساب المصروفات المؤجلة"
|
||||
msgstr ""
|
||||
|
||||
#. Option for the 'Entry Type' (Select) field in DocType 'Journal Entry'
|
||||
#. Label of the deferred_revenue (Section Break) field in DocType 'POS Invoice
|
||||
@@ -15248,7 +15248,7 @@ msgstr "إيراد مباشر"
|
||||
#: erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.json
|
||||
#: erpnext/stock/doctype/putaway_rule/putaway_rule.json
|
||||
msgid "Disable"
|
||||
msgstr "تعطيل"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the disable_capacity_planning (Check) field in DocType
|
||||
#. 'Manufacturing Settings'
|
||||
@@ -15292,7 +15292,7 @@ msgstr ""
|
||||
#: erpnext/stock/doctype/delivery_note/delivery_note.json
|
||||
#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
|
||||
msgid "Disable Rounded Total"
|
||||
msgstr "تعطيل الاجمالي المقرب"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the disable_serial_no_and_batch_selector (Check) field in DocType
|
||||
#. 'Stock Settings'
|
||||
@@ -16207,7 +16207,7 @@ msgstr "مكرر {0} موجود في الجدول"
|
||||
#. Label of the duration (Int) field in DocType 'Task'
|
||||
#: erpnext/projects/doctype/task/task.json
|
||||
msgid "Duration (Days)"
|
||||
msgstr "المدة (أيام)"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/crm/report/lead_conversion_time/lead_conversion_time.py:66
|
||||
msgid "Duration in Days"
|
||||
@@ -16583,7 +16583,7 @@ msgstr "موظف"
|
||||
#. Account'
|
||||
#: erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
|
||||
msgid "Employee Advance"
|
||||
msgstr "تقدم الموظف"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py:16
|
||||
#: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py:23
|
||||
@@ -16646,7 +16646,7 @@ msgstr "اسم الموظف"
|
||||
#. Label of the employee_number (Data) field in DocType 'Employee'
|
||||
#: erpnext/setup/doctype/employee/employee.json
|
||||
msgid "Employee Number"
|
||||
msgstr "رقم الموظف"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the employee_user_id (Link) field in DocType 'Call Log'
|
||||
#: erpnext/telephony/doctype/call_log/call_log.json
|
||||
@@ -16672,7 +16672,7 @@ msgstr ""
|
||||
|
||||
#: erpnext/manufacturing/doctype/workstation/workstation.js:351
|
||||
msgid "Employees"
|
||||
msgstr "الموظفين"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/stock/doctype/batch/batch_list.js:16
|
||||
msgid "Empty"
|
||||
@@ -16863,7 +16863,7 @@ msgstr ""
|
||||
#. Label of the encashment_date (Date) field in DocType 'Employee'
|
||||
#: erpnext/setup/doctype/employee/employee.json
|
||||
msgid "Encashment Date"
|
||||
msgstr "تاريخ التحصيل"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/crm/doctype/contract/contract.py:70
|
||||
msgid "End Date cannot be before Start Date."
|
||||
@@ -16880,7 +16880,7 @@ msgstr "لا يمكن أن يكون تاريخ الانتهاء قبل تاري
|
||||
#: erpnext/support/doctype/service_day/service_day.json
|
||||
#: erpnext/telephony/doctype/call_log/call_log.json
|
||||
msgid "End Time"
|
||||
msgstr "وقت الانتهاء"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/stock/doctype/stock_entry/stock_entry.js:287
|
||||
msgid "End Transit"
|
||||
@@ -17307,7 +17307,7 @@ msgstr ""
|
||||
#: erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.json
|
||||
#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
|
||||
msgid "Exchange Rate"
|
||||
msgstr "سعر الصرف"
|
||||
msgstr ""
|
||||
|
||||
#. Name of a DocType
|
||||
#. Option for the 'Entry Type' (Select) field in DocType 'Journal Entry'
|
||||
@@ -17582,7 +17582,7 @@ msgstr "حساب المصاريف مفقود"
|
||||
#. Account'
|
||||
#: erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
|
||||
msgid "Expense Claim"
|
||||
msgstr "طلب النفقات"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the expense_account (Link) field in DocType 'Purchase Invoice Item'
|
||||
#: erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
|
||||
@@ -18973,7 +18973,7 @@ msgstr ""
|
||||
#. Label of the from_employee (Link) field in DocType 'Asset Movement Item'
|
||||
#: erpnext/assets/doctype/asset_movement_item/asset_movement_item.json
|
||||
msgid "From Employee"
|
||||
msgstr "من الموظف"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/assets/doctype/asset_movement/asset_movement.py:85
|
||||
msgid "From Employee is required while issuing Asset {0}"
|
||||
@@ -20526,7 +20526,7 @@ msgstr "اسم قائمة العطلات"
|
||||
#. Label of the holidays (Table) field in DocType 'Holiday List'
|
||||
#: erpnext/setup/doctype/holiday_list/holiday_list.json
|
||||
msgid "Holidays"
|
||||
msgstr "العطلات"
|
||||
msgstr ""
|
||||
|
||||
#. Option for the 'Forecasting Method' (Select) field in DocType 'Sales
|
||||
#. Forecast'
|
||||
@@ -20556,7 +20556,7 @@ msgstr ""
|
||||
#: erpnext/manufacturing/doctype/job_card/job_card.json
|
||||
#: erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json
|
||||
msgid "Hour Rate"
|
||||
msgstr "سعرالساعة"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the hours (Float) field in DocType 'Workstation Working Hour'
|
||||
#: erpnext/manufacturing/doctype/workstation_working_hour/workstation_working_hour.json
|
||||
@@ -21293,7 +21293,7 @@ msgstr "في المئة"
|
||||
#: erpnext/manufacturing/doctype/work_order/work_order.json
|
||||
#: erpnext/stock/doctype/quality_inspection/quality_inspection.json
|
||||
msgid "In Process"
|
||||
msgstr "في عملية"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/stock/report/item_variant_details/item_variant_details.py:107
|
||||
msgid "In Production"
|
||||
@@ -22156,7 +22156,7 @@ msgstr "إعدادات نقل المستودعات الداخلية"
|
||||
#. Label of the interest (Currency) field in DocType 'Overdue Payment'
|
||||
#: erpnext/accounts/doctype/overdue_payment/overdue_payment.json
|
||||
msgid "Interest"
|
||||
msgstr "فائدة"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/accounts/doctype/payment_entry/payment_entry.py:3052
|
||||
msgid "Interest and/or dunning fee"
|
||||
@@ -23001,7 +23001,7 @@ msgstr "هو المورد الداخلي"
|
||||
#. Label of the is_mandatory (Check) field in DocType 'Applicable On Account'
|
||||
#: erpnext/accounts/doctype/applicable_on_account/applicable_on_account.json
|
||||
msgid "Is Mandatory"
|
||||
msgstr "إلزامي"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the is_milestone (Check) field in DocType 'Task'
|
||||
#: erpnext/projects/doctype/task/task.json
|
||||
@@ -23055,7 +23055,7 @@ msgstr ""
|
||||
#. Label of the is_paid (Check) field in DocType 'Purchase Invoice'
|
||||
#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
|
||||
msgid "Is Paid"
|
||||
msgstr "مدفوع"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the is_paused (Check) field in DocType 'Job Card'
|
||||
#: erpnext/manufacturing/doctype/job_card/job_card.json
|
||||
@@ -24992,7 +24992,7 @@ msgstr "بدأ العمل"
|
||||
#: erpnext/crm/doctype/lead/lead.json
|
||||
#: erpnext/crm/doctype/opportunity/opportunity.json
|
||||
msgid "Job Title"
|
||||
msgstr "المسمى الوظيفي"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the supplier (Link) field in DocType 'Subcontracting Order'
|
||||
#. Label of the supplier (Link) field in DocType 'Subcontracting Receipt'
|
||||
@@ -25782,7 +25782,7 @@ msgstr "رقم الرخصة"
|
||||
#. Label of the license_plate (Data) field in DocType 'Vehicle'
|
||||
#: erpnext/setup/doctype/vehicle/vehicle.json
|
||||
msgid "License Plate"
|
||||
msgstr "لوحة الترخيص"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/controllers/status_updater.py:459
|
||||
msgid "Limit Crossed"
|
||||
@@ -25911,7 +25911,7 @@ msgstr ""
|
||||
#. Account'
|
||||
#: erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
|
||||
msgid "Loan"
|
||||
msgstr "قرض"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the loan_end_date (Date) field in DocType 'Invoice Discounting'
|
||||
#: erpnext/accounts/doctype/invoice_discounting/invoice_discounting.json
|
||||
@@ -28002,7 +28002,7 @@ msgstr "طريقة الدفع"
|
||||
#. Label of the model (Data) field in DocType 'Vehicle'
|
||||
#: erpnext/setup/doctype/vehicle/vehicle.json
|
||||
msgid "Model"
|
||||
msgstr "الموديل"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the section_break_11 (Section Break) field in DocType 'POS Closing
|
||||
#. Entry'
|
||||
@@ -29597,7 +29597,7 @@ msgstr "قراءة عداد المسافات (الأخيرة)"
|
||||
#. Label of the scheduled_confirmation_date (Date) field in DocType 'Employee'
|
||||
#: erpnext/setup/doctype/employee/employee.json
|
||||
msgid "Offer Date"
|
||||
msgstr "تاريخ العرض"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py:29
|
||||
#: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py:42
|
||||
@@ -29639,7 +29639,7 @@ msgstr ""
|
||||
#: erpnext/setup/doctype/supplier_group/supplier_group.json
|
||||
#: erpnext/stock/doctype/warehouse/warehouse.json
|
||||
msgid "Old Parent"
|
||||
msgstr "الحساب الأب السابق"
|
||||
msgstr ""
|
||||
|
||||
#. Option for the 'Reconciliation Takes Effect On' (Select) field in DocType
|
||||
#. 'Company'
|
||||
@@ -30606,7 +30606,7 @@ msgstr "البند الأصلي"
|
||||
#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
|
||||
#: erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json
|
||||
msgid "Other Details"
|
||||
msgstr "تفاصيل أخرى"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the other_info_tab (Tab Break) field in DocType 'Asset'
|
||||
#. Label of the other_info_tab (Tab Break) field in DocType 'Stock Entry'
|
||||
@@ -30639,7 +30639,7 @@ msgstr "تقارير أخرى"
|
||||
#. 'Manufacturing Settings'
|
||||
#: erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json
|
||||
msgid "Other Settings"
|
||||
msgstr "اعدادات اخرى"
|
||||
msgstr ""
|
||||
|
||||
#. Name of a UOM
|
||||
#: erpnext/setup/setup_wizard/data/uom_data.json
|
||||
@@ -30928,7 +30928,7 @@ msgstr "زيادة الإنتاج للمبيعات وطلب العمل"
|
||||
#. Option for the 'Current Address Is' (Select) field in DocType 'Employee'
|
||||
#: erpnext/setup/doctype/employee/employee.json
|
||||
msgid "Owned"
|
||||
msgstr "مملوك"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/accounts/report/sales_payment_summary/sales_payment_summary.js:29
|
||||
#: erpnext/accounts/report/sales_payment_summary/sales_payment_summary.py:23
|
||||
@@ -32060,7 +32060,7 @@ msgstr ""
|
||||
#. Label of the passport_number (Data) field in DocType 'Employee'
|
||||
#: erpnext/setup/doctype/employee/employee.json
|
||||
msgid "Passport Number"
|
||||
msgstr "رقم جواز السفر"
|
||||
msgstr ""
|
||||
|
||||
#. Option for the 'Status' (Select) field in DocType 'Subscription'
|
||||
#: erpnext/accounts/doctype/subscription/subscription.json
|
||||
@@ -32177,7 +32177,7 @@ msgstr "دفع"
|
||||
#: erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.json
|
||||
#: erpnext/accounts/doctype/payment_request/payment_request.json
|
||||
msgid "Payment Account"
|
||||
msgstr "حساب الدفع"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the payment_amount (Currency) field in DocType 'Overdue Payment'
|
||||
#. Label of the payment_amount (Currency) field in DocType 'Payment Schedule'
|
||||
@@ -32768,7 +32768,7 @@ msgstr ""
|
||||
#. Account'
|
||||
#: erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
|
||||
msgid "Payroll Entry"
|
||||
msgstr "دخول الرواتب"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py:88
|
||||
#: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py:119
|
||||
@@ -35502,7 +35502,7 @@ msgstr ""
|
||||
#. Request'
|
||||
#: erpnext/stock/doctype/material_request/material_request.json
|
||||
msgid "Printing Details"
|
||||
msgstr "تفاصيل الطباعة"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the printing_settings_section (Section Break) field in DocType
|
||||
#. 'Dunning'
|
||||
@@ -37971,7 +37971,7 @@ msgstr "رفع طلب المواد عندما يصل المخزون إلى مس
|
||||
#. Label of the complaint_raised_by (Data) field in DocType 'Warranty Claim'
|
||||
#: erpnext/support/doctype/warranty_claim/warranty_claim.json
|
||||
msgid "Raised By"
|
||||
msgstr "التي أثارها"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the raised_by (Data) field in DocType 'Issue'
|
||||
#: erpnext/support/doctype/issue/issue.json
|
||||
@@ -39282,7 +39282,7 @@ msgstr "يجب أن يكون تاريخ الإصدار في المستقبل"
|
||||
#. Label of the relieving_date (Date) field in DocType 'Employee'
|
||||
#: erpnext/setup/doctype/employee/employee.json
|
||||
msgid "Relieving Date"
|
||||
msgstr "تاريخ المغادرة"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/public/js/bank_reconciliation_tool/dialog_manager.js:125
|
||||
msgid "Remaining"
|
||||
@@ -40150,7 +40150,7 @@ msgstr "إعادة ضبط اتفاقية مستوى الخدمة."
|
||||
#. Label of the resignation_letter_date (Date) field in DocType 'Employee'
|
||||
#: erpnext/setup/doctype/employee/employee.json
|
||||
msgid "Resignation Letter Date"
|
||||
msgstr "تاريخ رسالة الإستقالة"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the sb_00 (Section Break) field in DocType 'Quality Action'
|
||||
#. Label of the resolution (Text Editor) field in DocType 'Quality Action
|
||||
@@ -40173,7 +40173,7 @@ msgstr "القرار بواسطة"
|
||||
#: erpnext/support/doctype/issue/issue.json
|
||||
#: erpnext/support/doctype/warranty_claim/warranty_claim.json
|
||||
msgid "Resolution Date"
|
||||
msgstr "تاريخ القرار"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the section_break_19 (Section Break) field in DocType 'Issue'
|
||||
#. Label of the resolution_details (Text Editor) field in DocType 'Issue'
|
||||
@@ -40181,7 +40181,7 @@ msgstr "تاريخ القرار"
|
||||
#: erpnext/support/doctype/issue/issue.json
|
||||
#: erpnext/support/doctype/warranty_claim/warranty_claim.json
|
||||
msgid "Resolution Details"
|
||||
msgstr "قرار تفاصيل"
|
||||
msgstr ""
|
||||
|
||||
#. Option for the 'Service Level Agreement Status' (Select) field in DocType
|
||||
#. 'Issue'
|
||||
@@ -40222,7 +40222,7 @@ msgstr "تم الحل"
|
||||
#. Label of the resolved_by (Link) field in DocType 'Warranty Claim'
|
||||
#: erpnext/support/doctype/warranty_claim/warranty_claim.json
|
||||
msgid "Resolved By"
|
||||
msgstr "حلها عن طريق"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the response_by (Datetime) field in DocType 'Issue'
|
||||
#: erpnext/support/doctype/issue/issue.json
|
||||
@@ -40494,7 +40494,7 @@ msgstr ""
|
||||
#: erpnext/selling/page/point_of_sale/pos_past_order_summary.js:138
|
||||
#: erpnext/stock/doctype/shipment/shipment.json
|
||||
msgid "Returned"
|
||||
msgstr "تم إرجاعه"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the returned_against (Data) field in DocType 'Serial and Batch
|
||||
#. Bundle'
|
||||
@@ -40816,7 +40816,7 @@ msgstr "تقريب إجمالي"
|
||||
#: erpnext/stock/doctype/delivery_note/delivery_note.json
|
||||
#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
|
||||
msgid "Rounded Total (Company Currency)"
|
||||
msgstr "المشاركات تقريب (العملة الشركة)"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the rounding_adjustment (Currency) field in DocType 'POS Invoice'
|
||||
#. Label of the rounding_adjustment (Currency) field in DocType 'Purchase
|
||||
@@ -48069,7 +48069,7 @@ msgstr ""
|
||||
#: erpnext/setup/doctype/driver/driver.json
|
||||
#: erpnext/setup/doctype/employee/employee.json
|
||||
msgid "Suspended"
|
||||
msgstr "معلق"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/selling/page/point_of_sale/pos_payment.js:442
|
||||
msgid "Switch Between Payment Modes"
|
||||
@@ -48420,7 +48420,7 @@ msgstr "نوع المهمة"
|
||||
#. Option for the '% Complete Method' (Select) field in DocType 'Project'
|
||||
#: erpnext/projects/doctype/project/project.json
|
||||
msgid "Task Weight"
|
||||
msgstr "وزن المهمة"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/projects/doctype/project_template/project_template.py:41
|
||||
msgid "Task {0} depends on Task {1}. Please add Task {1} to the Tasks list."
|
||||
@@ -48959,7 +48959,7 @@ msgstr ""
|
||||
#: erpnext/accounts/doctype/payment_terms_template/payment_terms_template.json
|
||||
#: erpnext/quality_management/doctype/quality_feedback_template/quality_feedback_template.json
|
||||
msgid "Template Name"
|
||||
msgstr "اسم القالب"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the template_task (Data) field in DocType 'Task'
|
||||
#: erpnext/projects/doctype/task/task.json
|
||||
@@ -49037,7 +49037,7 @@ msgstr "تفاصيل الشروط"
|
||||
#: erpnext/stock/doctype/material_request/material_request.json
|
||||
#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
|
||||
msgid "Terms"
|
||||
msgstr "الشروط"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the terms_section_break (Section Break) field in DocType 'Purchase
|
||||
#. Order'
|
||||
@@ -50056,7 +50056,7 @@ msgstr "الوقت المطلوب (بالدقائق)"
|
||||
#. Label of the time_sheet (Link) field in DocType 'Sales Invoice Timesheet'
|
||||
#: erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json
|
||||
msgid "Time Sheet"
|
||||
msgstr "ورقة الوقت"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the time_sheet_list (Section Break) field in DocType 'POS Invoice'
|
||||
#. Label of the time_sheet_list (Section Break) field in DocType 'Sales
|
||||
@@ -50718,7 +50718,7 @@ msgstr "إجمالي مبلغ الفاتورة (عبر فواتير المبيع
|
||||
#. Label of the total_billed_hours (Float) field in DocType 'Timesheet'
|
||||
#: erpnext/projects/doctype/timesheet/timesheet.json
|
||||
msgid "Total Billed Hours"
|
||||
msgstr "مجموع الساعات وصفت"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the total_billing_amount (Currency) field in DocType 'POS Invoice'
|
||||
#. Label of the total_billing_amount (Currency) field in DocType 'Sales
|
||||
@@ -50870,7 +50870,7 @@ msgstr "إجمالي وقت الانتظار"
|
||||
#. Label of the total_holidays (Int) field in DocType 'Holiday List'
|
||||
#: erpnext/setup/doctype/holiday_list/holiday_list.json
|
||||
msgid "Total Holidays"
|
||||
msgstr "مجموع العطلات"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py:115
|
||||
msgid "Total Income"
|
||||
@@ -51211,7 +51211,7 @@ msgstr "مجموع الضرائب"
|
||||
#: erpnext/stock/doctype/delivery_note/delivery_note.json
|
||||
#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
|
||||
msgid "Total Taxes and Charges"
|
||||
msgstr "مجموع الضرائب والرسوم"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the base_total_taxes_and_charges (Currency) field in DocType
|
||||
#. 'Payment Entry'
|
||||
@@ -51323,7 +51323,7 @@ msgstr ""
|
||||
#: erpnext/manufacturing/doctype/workstation/workstation.json
|
||||
#: erpnext/projects/doctype/timesheet/timesheet.json
|
||||
msgid "Total Working Hours"
|
||||
msgstr "مجموع ساعات العمل"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the total_workstation_time (Int) field in DocType 'Item Lead Time'
|
||||
#: erpnext/stock/doctype/item_lead_time/item_lead_time.json
|
||||
@@ -53764,7 +53764,7 @@ msgstr ""
|
||||
|
||||
#: erpnext/setup/setup_wizard/data/marketing_source.txt:10
|
||||
msgid "Walk In"
|
||||
msgstr "عميل غير مسجل"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js:4
|
||||
msgid "Warehouse Capacity Summary"
|
||||
|
||||
@@ -3,7 +3,7 @@ msgstr ""
|
||||
"Project-Id-Version: frappe\n"
|
||||
"Report-Msgid-Bugs-To: hello@frappe.io\n"
|
||||
"POT-Creation-Date: 2025-11-10 12:11+0000\n"
|
||||
"PO-Revision-Date: 2025-11-11 21:15\n"
|
||||
"PO-Revision-Date: 2025-11-12 21:29\n"
|
||||
"Last-Translator: hello@frappe.io\n"
|
||||
"Language-Team: Bosnian\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -32241,7 +32241,7 @@ msgstr "Plati / Uplata od"
|
||||
#: erpnext/accounts/report/account_balance/account_balance.js:54
|
||||
#: erpnext/setup/doctype/party_type/party_type.json
|
||||
msgid "Payable"
|
||||
msgstr "Plaća se"
|
||||
msgstr "Obaveze"
|
||||
|
||||
#: erpnext/accounts/report/accounts_payable/accounts_payable.js:39
|
||||
#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1160
|
||||
@@ -32249,7 +32249,7 @@ msgstr "Plaća se"
|
||||
#: erpnext/accounts/report/purchase_register/purchase_register.py:194
|
||||
#: erpnext/accounts/report/purchase_register/purchase_register.py:235
|
||||
msgid "Payable Account"
|
||||
msgstr "Račun Plaćanja"
|
||||
msgstr "Račun Obaveza"
|
||||
|
||||
#. Name of a Workspace
|
||||
#. Label of the payables (Check) field in DocType 'Email Digest'
|
||||
|
||||
@@ -3,7 +3,7 @@ msgstr ""
|
||||
"Project-Id-Version: frappe\n"
|
||||
"Report-Msgid-Bugs-To: hello@frappe.io\n"
|
||||
"POT-Creation-Date: 2025-11-10 12:11+0000\n"
|
||||
"PO-Revision-Date: 2025-11-10 21:18\n"
|
||||
"PO-Revision-Date: 2025-11-15 21:36\n"
|
||||
"Last-Translator: hello@frappe.io\n"
|
||||
"Language-Team: Spanish\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -6922,7 +6922,7 @@ msgstr "Información de la Cuenta Bancaria"
|
||||
#: erpnext/accounts/doctype/payment_entry/payment_entry.json
|
||||
#: erpnext/accounts/doctype/payment_request/payment_request.json
|
||||
msgid "Bank Account No"
|
||||
msgstr "Número de Cuenta Bancaria"
|
||||
msgstr ""
|
||||
|
||||
#. Name of a DocType
|
||||
#: erpnext/accounts/doctype/bank_account_subtype/bank_account_subtype.json
|
||||
@@ -7024,7 +7024,7 @@ msgstr "Tipo de Garantía Bancaria"
|
||||
#: erpnext/accounts/doctype/cheque_print_template/cheque_print_template.json
|
||||
#: erpnext/setup/doctype/employee/employee.json
|
||||
msgid "Bank Name"
|
||||
msgstr "Nombre del banco"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py:98
|
||||
#: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py:142
|
||||
@@ -7513,7 +7513,7 @@ msgstr "Antes de Reconciliación"
|
||||
#. Label of the start (Int) field in DocType 'Task'
|
||||
#: erpnext/projects/doctype/task/task.json
|
||||
msgid "Begin On (Days)"
|
||||
msgstr "Comience el (días)"
|
||||
msgstr ""
|
||||
|
||||
#. Option for the 'Generate Invoice At' (Select) field in DocType
|
||||
#. 'Subscription'
|
||||
@@ -11762,7 +11762,7 @@ msgstr ""
|
||||
#: erpnext/manufacturing/doctype/bom_operation/bom_operation.json
|
||||
#: erpnext/projects/doctype/task/task.json
|
||||
msgid "Costing"
|
||||
msgstr "Presupuesto"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the costing_amount (Currency) field in DocType 'Timesheet Detail'
|
||||
#. Label of the base_costing_amount (Currency) field in DocType 'Timesheet
|
||||
@@ -12454,7 +12454,7 @@ msgstr "Acreedores"
|
||||
#. Label of the criteria (Table) field in DocType 'Supplier Scorecard Period'
|
||||
#: erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.json
|
||||
msgid "Criteria"
|
||||
msgstr "Criterios"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the formula (Small Text) field in DocType 'Supplier Scorecard
|
||||
#. Criteria'
|
||||
@@ -13579,7 +13579,7 @@ msgstr "Importación de datos y configuraciones"
|
||||
#. Label of the date (Date) field in DocType 'Bulk Transaction Log Detail'
|
||||
#: erpnext/bulk_transaction/doctype/bulk_transaction_log_detail/bulk_transaction_log_detail.json
|
||||
msgid "Date "
|
||||
msgstr "Fecha "
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.js:97
|
||||
msgid "Date Based On"
|
||||
@@ -13603,7 +13603,7 @@ msgstr "La fecha debe estar entre {0} y {1}"
|
||||
#. Label of the date_of_birth (Date) field in DocType 'Employee'
|
||||
#: erpnext/setup/doctype/employee/employee.json
|
||||
msgid "Date of Birth"
|
||||
msgstr "Fecha de nacimiento"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/setup/doctype/employee/employee.py:147
|
||||
msgid "Date of Birth cannot be greater than today."
|
||||
@@ -13636,7 +13636,7 @@ msgstr "Fecha de Emisión."
|
||||
#. Label of the date_of_joining (Date) field in DocType 'Employee'
|
||||
#: erpnext/setup/doctype/employee/employee.json
|
||||
msgid "Date of Joining"
|
||||
msgstr "Fecha de Ingreso"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py:273
|
||||
msgid "Date of Transaction"
|
||||
@@ -13955,7 +13955,7 @@ msgstr "Deducciones o Pérdida"
|
||||
#: erpnext/accounts/doctype/mode_of_payment_account/mode_of_payment_account.json
|
||||
#: erpnext/accounts/doctype/party_account/party_account.json
|
||||
msgid "Default Account"
|
||||
msgstr "Cuenta predeterminada"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the default_accounts_section (Section Break) field in DocType
|
||||
#. 'Supplier'
|
||||
@@ -14463,7 +14463,7 @@ msgstr "Gasto Diferido"
|
||||
#: erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
|
||||
#: erpnext/stock/doctype/item_default/item_default.json
|
||||
msgid "Deferred Expense Account"
|
||||
msgstr "Cuenta de Gastos Diferidos"
|
||||
msgstr ""
|
||||
|
||||
#. Option for the 'Entry Type' (Select) field in DocType 'Journal Entry'
|
||||
#. Label of the deferred_revenue (Section Break) field in DocType 'POS Invoice
|
||||
@@ -15352,7 +15352,7 @@ msgstr "Ingreso directo"
|
||||
#: erpnext/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.json
|
||||
#: erpnext/stock/doctype/putaway_rule/putaway_rule.json
|
||||
msgid "Disable"
|
||||
msgstr "Desactivar"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the disable_capacity_planning (Check) field in DocType
|
||||
#. 'Manufacturing Settings'
|
||||
@@ -15396,7 +15396,7 @@ msgstr "Desactivar última tasa de compra"
|
||||
#: erpnext/stock/doctype/delivery_note/delivery_note.json
|
||||
#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
|
||||
msgid "Disable Rounded Total"
|
||||
msgstr "Desactivar redondeo"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the disable_serial_no_and_batch_selector (Check) field in DocType
|
||||
#. 'Stock Settings'
|
||||
@@ -16311,7 +16311,7 @@ msgstr "Duplicado {0} encontrado en la tabla"
|
||||
#. Label of the duration (Int) field in DocType 'Task'
|
||||
#: erpnext/projects/doctype/task/task.json
|
||||
msgid "Duration (Days)"
|
||||
msgstr "Duración (Días)"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/crm/report/lead_conversion_time/lead_conversion_time.py:66
|
||||
msgid "Duration in Days"
|
||||
@@ -16687,7 +16687,7 @@ msgstr "Empleado"
|
||||
#. Account'
|
||||
#: erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
|
||||
msgid "Employee Advance"
|
||||
msgstr "Avance del Empleado"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py:16
|
||||
#: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py:23
|
||||
@@ -16750,7 +16750,7 @@ msgstr "Nombre de empleado"
|
||||
#. Label of the employee_number (Data) field in DocType 'Employee'
|
||||
#: erpnext/setup/doctype/employee/employee.json
|
||||
msgid "Employee Number"
|
||||
msgstr "Número de empleado"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the employee_user_id (Link) field in DocType 'Call Log'
|
||||
#: erpnext/telephony/doctype/call_log/call_log.json
|
||||
@@ -16776,7 +16776,7 @@ msgstr ""
|
||||
|
||||
#: erpnext/manufacturing/doctype/workstation/workstation.js:351
|
||||
msgid "Employees"
|
||||
msgstr "Empleados"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/stock/doctype/batch/batch_list.js:16
|
||||
msgid "Empty"
|
||||
@@ -16967,7 +16967,7 @@ msgstr ""
|
||||
#. Label of the encashment_date (Date) field in DocType 'Employee'
|
||||
#: erpnext/setup/doctype/employee/employee.json
|
||||
msgid "Encashment Date"
|
||||
msgstr "Fecha de Cobro"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/crm/doctype/contract/contract.py:70
|
||||
msgid "End Date cannot be before Start Date."
|
||||
@@ -17413,7 +17413,7 @@ msgstr ""
|
||||
#: erpnext/stock/doctype/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.json
|
||||
#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
|
||||
msgid "Exchange Rate"
|
||||
msgstr "Tipo de cambio"
|
||||
msgstr ""
|
||||
|
||||
#. Name of a DocType
|
||||
#. Option for the 'Entry Type' (Select) field in DocType 'Journal Entry'
|
||||
@@ -17688,7 +17688,7 @@ msgstr "Falta la cuenta de gastos"
|
||||
#. Account'
|
||||
#: erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
|
||||
msgid "Expense Claim"
|
||||
msgstr "Reembolso de gastos"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the expense_account (Link) field in DocType 'Purchase Invoice Item'
|
||||
#: erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
|
||||
@@ -19079,7 +19079,7 @@ msgstr ""
|
||||
#. Label of the from_employee (Link) field in DocType 'Asset Movement Item'
|
||||
#: erpnext/assets/doctype/asset_movement_item/asset_movement_item.json
|
||||
msgid "From Employee"
|
||||
msgstr "Desde Empleado"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/assets/doctype/asset_movement/asset_movement.py:85
|
||||
msgid "From Employee is required while issuing Asset {0}"
|
||||
@@ -20632,7 +20632,7 @@ msgstr "Nombre de festividad"
|
||||
#. Label of the holidays (Table) field in DocType 'Holiday List'
|
||||
#: erpnext/setup/doctype/holiday_list/holiday_list.json
|
||||
msgid "Holidays"
|
||||
msgstr "Vacaciones"
|
||||
msgstr ""
|
||||
|
||||
#. Option for the 'Forecasting Method' (Select) field in DocType 'Sales
|
||||
#. Forecast'
|
||||
@@ -20662,7 +20662,7 @@ msgstr "Hora"
|
||||
#: erpnext/manufacturing/doctype/job_card/job_card.json
|
||||
#: erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json
|
||||
msgid "Hour Rate"
|
||||
msgstr "Salario por hora"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the hours (Float) field in DocType 'Workstation Working Hour'
|
||||
#: erpnext/manufacturing/doctype/workstation_working_hour/workstation_working_hour.json
|
||||
@@ -21399,7 +21399,7 @@ msgstr "En porcentaje"
|
||||
#: erpnext/manufacturing/doctype/work_order/work_order.json
|
||||
#: erpnext/stock/doctype/quality_inspection/quality_inspection.json
|
||||
msgid "In Process"
|
||||
msgstr "En Proceso"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/stock/report/item_variant_details/item_variant_details.py:107
|
||||
msgid "In Production"
|
||||
@@ -22262,7 +22262,7 @@ msgstr "Configuración de transferencia entre almacenes"
|
||||
#. Label of the interest (Currency) field in DocType 'Overdue Payment'
|
||||
#: erpnext/accounts/doctype/overdue_payment/overdue_payment.json
|
||||
msgid "Interest"
|
||||
msgstr "Interesar"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/accounts/doctype/payment_entry/payment_entry.py:3052
|
||||
msgid "Interest and/or dunning fee"
|
||||
@@ -23107,7 +23107,7 @@ msgstr "Es un Proveedor Interno"
|
||||
#. Label of the is_mandatory (Check) field in DocType 'Applicable On Account'
|
||||
#: erpnext/accounts/doctype/applicable_on_account/applicable_on_account.json
|
||||
msgid "Is Mandatory"
|
||||
msgstr "Es obligatorio"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the is_milestone (Check) field in DocType 'Task'
|
||||
#: erpnext/projects/doctype/task/task.json
|
||||
@@ -23161,7 +23161,7 @@ msgstr ""
|
||||
#. Label of the is_paid (Check) field in DocType 'Purchase Invoice'
|
||||
#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
|
||||
msgid "Is Paid"
|
||||
msgstr "Está pagado"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the is_paused (Check) field in DocType 'Job Card'
|
||||
#: erpnext/manufacturing/doctype/job_card/job_card.json
|
||||
@@ -25098,7 +25098,7 @@ msgstr "Trabajo comenzó"
|
||||
#: erpnext/crm/doctype/lead/lead.json
|
||||
#: erpnext/crm/doctype/opportunity/opportunity.json
|
||||
msgid "Job Title"
|
||||
msgstr "Título del trabajo"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the supplier (Link) field in DocType 'Subcontracting Order'
|
||||
#. Label of the supplier (Link) field in DocType 'Subcontracting Receipt'
|
||||
@@ -25888,7 +25888,7 @@ msgstr "Número de Licencia"
|
||||
#. Label of the license_plate (Data) field in DocType 'Vehicle'
|
||||
#: erpnext/setup/doctype/vehicle/vehicle.json
|
||||
msgid "License Plate"
|
||||
msgstr "Matrículas"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/controllers/status_updater.py:459
|
||||
msgid "Limit Crossed"
|
||||
@@ -26017,7 +26017,7 @@ msgstr ""
|
||||
#. Account'
|
||||
#: erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
|
||||
msgid "Loan"
|
||||
msgstr "Préstamo"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the loan_end_date (Date) field in DocType 'Invoice Discounting'
|
||||
#: erpnext/accounts/doctype/invoice_discounting/invoice_discounting.json
|
||||
@@ -28108,7 +28108,7 @@ msgstr "Modo de pago"
|
||||
#. Label of the model (Data) field in DocType 'Vehicle'
|
||||
#: erpnext/setup/doctype/vehicle/vehicle.json
|
||||
msgid "Model"
|
||||
msgstr "Modelo"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the section_break_11 (Section Break) field in DocType 'POS Closing
|
||||
#. Entry'
|
||||
@@ -29703,7 +29703,7 @@ msgstr "Valor del cuentakilómetros (Última)"
|
||||
#. Label of the scheduled_confirmation_date (Date) field in DocType 'Employee'
|
||||
#: erpnext/setup/doctype/employee/employee.json
|
||||
msgid "Offer Date"
|
||||
msgstr "Fecha de oferta"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py:29
|
||||
#: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py:42
|
||||
@@ -29745,7 +29745,7 @@ msgstr ""
|
||||
#: erpnext/setup/doctype/supplier_group/supplier_group.json
|
||||
#: erpnext/stock/doctype/warehouse/warehouse.json
|
||||
msgid "Old Parent"
|
||||
msgstr "Antiguo Padre"
|
||||
msgstr ""
|
||||
|
||||
#. Option for the 'Reconciliation Takes Effect On' (Select) field in DocType
|
||||
#. 'Company'
|
||||
@@ -30712,7 +30712,7 @@ msgstr "Artículo Original"
|
||||
#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
|
||||
#: erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json
|
||||
msgid "Other Details"
|
||||
msgstr "Otros detalles"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the other_info_tab (Tab Break) field in DocType 'Asset'
|
||||
#. Label of the other_info_tab (Tab Break) field in DocType 'Stock Entry'
|
||||
@@ -30745,7 +30745,7 @@ msgstr "Otros Reportes"
|
||||
#. 'Manufacturing Settings'
|
||||
#: erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json
|
||||
msgid "Other Settings"
|
||||
msgstr "Otros ajustes"
|
||||
msgstr ""
|
||||
|
||||
#. Name of a UOM
|
||||
#: erpnext/setup/setup_wizard/data/uom_data.json
|
||||
@@ -31034,7 +31034,7 @@ msgstr "Sobreproducción para ventas y órdenes de trabajo"
|
||||
#. Option for the 'Current Address Is' (Select) field in DocType 'Employee'
|
||||
#: erpnext/setup/doctype/employee/employee.json
|
||||
msgid "Owned"
|
||||
msgstr "Propiedad"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/accounts/report/sales_payment_summary/sales_payment_summary.js:29
|
||||
#: erpnext/accounts/report/sales_payment_summary/sales_payment_summary.py:23
|
||||
@@ -32166,7 +32166,7 @@ msgstr ""
|
||||
#. Label of the passport_number (Data) field in DocType 'Employee'
|
||||
#: erpnext/setup/doctype/employee/employee.json
|
||||
msgid "Passport Number"
|
||||
msgstr "Número de pasaporte"
|
||||
msgstr ""
|
||||
|
||||
#. Option for the 'Status' (Select) field in DocType 'Subscription'
|
||||
#: erpnext/accounts/doctype/subscription/subscription.json
|
||||
@@ -32283,7 +32283,7 @@ msgstr "Pago"
|
||||
#: erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.json
|
||||
#: erpnext/accounts/doctype/payment_request/payment_request.json
|
||||
msgid "Payment Account"
|
||||
msgstr "Cuenta de pagos"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the payment_amount (Currency) field in DocType 'Overdue Payment'
|
||||
#. Label of the payment_amount (Currency) field in DocType 'Payment Schedule'
|
||||
@@ -32874,7 +32874,7 @@ msgstr ""
|
||||
#. Account'
|
||||
#: erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
|
||||
msgid "Payroll Entry"
|
||||
msgstr "Entrada de Nómina"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py:88
|
||||
#: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py:119
|
||||
@@ -35608,7 +35608,7 @@ msgstr ""
|
||||
#. Request'
|
||||
#: erpnext/stock/doctype/material_request/material_request.json
|
||||
msgid "Printing Details"
|
||||
msgstr "Detalles de impresión"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the printing_settings_section (Section Break) field in DocType
|
||||
#. 'Dunning'
|
||||
@@ -38077,7 +38077,7 @@ msgstr "Aumente la solicitud de material cuando el stock alcance el nivel de ped
|
||||
#. Label of the complaint_raised_by (Data) field in DocType 'Warranty Claim'
|
||||
#: erpnext/support/doctype/warranty_claim/warranty_claim.json
|
||||
msgid "Raised By"
|
||||
msgstr "Propuesto por"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the raised_by (Data) field in DocType 'Issue'
|
||||
#: erpnext/support/doctype/issue/issue.json
|
||||
@@ -39388,7 +39388,7 @@ msgstr "La fecha de lanzamiento debe ser en el futuro"
|
||||
#. Label of the relieving_date (Date) field in DocType 'Employee'
|
||||
#: erpnext/setup/doctype/employee/employee.json
|
||||
msgid "Relieving Date"
|
||||
msgstr "Fecha de relevo"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/public/js/bank_reconciliation_tool/dialog_manager.js:125
|
||||
msgid "Remaining"
|
||||
@@ -40256,7 +40256,7 @@ msgstr "Restablecimiento del acuerdo de nivel de servicio."
|
||||
#. Label of the resignation_letter_date (Date) field in DocType 'Employee'
|
||||
#: erpnext/setup/doctype/employee/employee.json
|
||||
msgid "Resignation Letter Date"
|
||||
msgstr "Fecha de carta de renuncia"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the sb_00 (Section Break) field in DocType 'Quality Action'
|
||||
#. Label of the resolution (Text Editor) field in DocType 'Quality Action
|
||||
@@ -40279,7 +40279,7 @@ msgstr "Resolución por"
|
||||
#: erpnext/support/doctype/issue/issue.json
|
||||
#: erpnext/support/doctype/warranty_claim/warranty_claim.json
|
||||
msgid "Resolution Date"
|
||||
msgstr "Fecha de resolución"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the section_break_19 (Section Break) field in DocType 'Issue'
|
||||
#. Label of the resolution_details (Text Editor) field in DocType 'Issue'
|
||||
@@ -40287,7 +40287,7 @@ msgstr "Fecha de resolución"
|
||||
#: erpnext/support/doctype/issue/issue.json
|
||||
#: erpnext/support/doctype/warranty_claim/warranty_claim.json
|
||||
msgid "Resolution Details"
|
||||
msgstr "Detalles de la resolución"
|
||||
msgstr ""
|
||||
|
||||
#. Option for the 'Service Level Agreement Status' (Select) field in DocType
|
||||
#. 'Issue'
|
||||
@@ -40328,7 +40328,7 @@ msgstr "Resuelto"
|
||||
#. Label of the resolved_by (Link) field in DocType 'Warranty Claim'
|
||||
#: erpnext/support/doctype/warranty_claim/warranty_claim.json
|
||||
msgid "Resolved By"
|
||||
msgstr "Resuelto por"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the response_by (Datetime) field in DocType 'Issue'
|
||||
#: erpnext/support/doctype/issue/issue.json
|
||||
@@ -40600,7 +40600,7 @@ msgstr ""
|
||||
#: erpnext/selling/page/point_of_sale/pos_past_order_summary.js:138
|
||||
#: erpnext/stock/doctype/shipment/shipment.json
|
||||
msgid "Returned"
|
||||
msgstr "Devuelto"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the returned_against (Data) field in DocType 'Serial and Batch
|
||||
#. Bundle'
|
||||
@@ -48178,7 +48178,7 @@ msgstr ""
|
||||
#: erpnext/setup/doctype/driver/driver.json
|
||||
#: erpnext/setup/doctype/employee/employee.json
|
||||
msgid "Suspended"
|
||||
msgstr "Suspendido"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/selling/page/point_of_sale/pos_payment.js:442
|
||||
msgid "Switch Between Payment Modes"
|
||||
@@ -48529,7 +48529,7 @@ msgstr "Tipo de tarea"
|
||||
#. Option for the '% Complete Method' (Select) field in DocType 'Project'
|
||||
#: erpnext/projects/doctype/project/project.json
|
||||
msgid "Task Weight"
|
||||
msgstr "Peso de la Tarea"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/projects/doctype/project_template/project_template.py:41
|
||||
msgid "Task {0} depends on Task {1}. Please add Task {1} to the Tasks list."
|
||||
@@ -49068,7 +49068,7 @@ msgstr ""
|
||||
#: erpnext/accounts/doctype/payment_terms_template/payment_terms_template.json
|
||||
#: erpnext/quality_management/doctype/quality_feedback_template/quality_feedback_template.json
|
||||
msgid "Template Name"
|
||||
msgstr "Nombre de Plantilla"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the template_task (Data) field in DocType 'Task'
|
||||
#: erpnext/projects/doctype/task/task.json
|
||||
@@ -49146,7 +49146,7 @@ msgstr "Detalles de términos y condiciones"
|
||||
#: erpnext/stock/doctype/material_request/material_request.json
|
||||
#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
|
||||
msgid "Terms"
|
||||
msgstr "Términos."
|
||||
msgstr ""
|
||||
|
||||
#. Label of the terms_section_break (Section Break) field in DocType 'Purchase
|
||||
#. Order'
|
||||
@@ -50165,7 +50165,7 @@ msgstr "Tiempo requerido (en minutos)"
|
||||
#. Label of the time_sheet (Link) field in DocType 'Sales Invoice Timesheet'
|
||||
#: erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json
|
||||
msgid "Time Sheet"
|
||||
msgstr "Hoja de horario"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the time_sheet_list (Section Break) field in DocType 'POS Invoice'
|
||||
#. Label of the time_sheet_list (Section Break) field in DocType 'Sales
|
||||
@@ -50827,7 +50827,7 @@ msgstr "Importe Total Facturado (a través de Facturas de Ventas)"
|
||||
#. Label of the total_billed_hours (Float) field in DocType 'Timesheet'
|
||||
#: erpnext/projects/doctype/timesheet/timesheet.json
|
||||
msgid "Total Billed Hours"
|
||||
msgstr "Total de Horas Facturadas"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the total_billing_amount (Currency) field in DocType 'POS Invoice'
|
||||
#. Label of the total_billing_amount (Currency) field in DocType 'Sales
|
||||
@@ -50979,7 +50979,7 @@ msgstr "Tiempo total de espera"
|
||||
#. Label of the total_holidays (Int) field in DocType 'Holiday List'
|
||||
#: erpnext/setup/doctype/holiday_list/holiday_list.json
|
||||
msgid "Total Holidays"
|
||||
msgstr "Vacaciones Totales"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py:115
|
||||
msgid "Total Income"
|
||||
@@ -51320,7 +51320,7 @@ msgstr "Impuesto Total"
|
||||
#: erpnext/stock/doctype/delivery_note/delivery_note.json
|
||||
#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
|
||||
msgid "Total Taxes and Charges"
|
||||
msgstr "Total Impuestos y Cargos"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the base_total_taxes_and_charges (Currency) field in DocType
|
||||
#. 'Payment Entry'
|
||||
@@ -51432,7 +51432,7 @@ msgstr ""
|
||||
#: erpnext/manufacturing/doctype/workstation/workstation.json
|
||||
#: erpnext/projects/doctype/timesheet/timesheet.json
|
||||
msgid "Total Working Hours"
|
||||
msgstr "Horas de trabajo total"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the total_workstation_time (Int) field in DocType 'Item Lead Time'
|
||||
#: erpnext/stock/doctype/item_lead_time/item_lead_time.json
|
||||
@@ -53873,7 +53873,7 @@ msgstr "Esperando Pago..."
|
||||
|
||||
#: erpnext/setup/setup_wizard/data/marketing_source.txt:10
|
||||
msgid "Walk In"
|
||||
msgstr "Entrar"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js:4
|
||||
msgid "Warehouse Capacity Summary"
|
||||
|
||||
@@ -3,7 +3,7 @@ msgstr ""
|
||||
"Project-Id-Version: frappe\n"
|
||||
"Report-Msgid-Bugs-To: hello@frappe.io\n"
|
||||
"POT-Creation-Date: 2025-11-10 12:11+0000\n"
|
||||
"PO-Revision-Date: 2025-11-10 21:18\n"
|
||||
"PO-Revision-Date: 2025-11-16 22:14\n"
|
||||
"Last-Translator: hello@frappe.io\n"
|
||||
"Language-Team: Persian\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -1435,7 +1435,7 @@ msgstr "حساب {0} متعلق به شرکت {1} نیست"
|
||||
|
||||
#: erpnext/accounts/doctype/account/account.py:541
|
||||
msgid "Account {0} exists in parent company {1}."
|
||||
msgstr "حساب {0} در شرکت مادر {1} وجود دارد."
|
||||
msgstr "حساب {0} در شرکت والد {1} وجود دارد."
|
||||
|
||||
#: erpnext/accounts/doctype/budget/budget.py:114
|
||||
msgid "Account {0} has been entered multiple times"
|
||||
@@ -3645,7 +3645,7 @@ msgstr "همه این آیتمها قبلاً صورتحساب/بازگردا
|
||||
#: erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js:85
|
||||
#: erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js:92
|
||||
msgid "Allocate"
|
||||
msgstr ""
|
||||
msgstr "تخصیص"
|
||||
|
||||
#. Label of the allocate_advances_automatically (Check) field in DocType 'POS
|
||||
#. Invoice'
|
||||
@@ -7703,7 +7703,7 @@ msgstr "بیوتکنولوژی"
|
||||
#. Name of a DocType
|
||||
#: erpnext/accounts/doctype/bisect_accounting_statements/bisect_accounting_statements.json
|
||||
msgid "Bisect Accounting Statements"
|
||||
msgstr ""
|
||||
msgstr "صورتهای حسابداری دوبخشی"
|
||||
|
||||
#: erpnext/accounts/doctype/bisect_accounting_statements/bisect_accounting_statements.js:9
|
||||
msgid "Bisect Left"
|
||||
@@ -7812,7 +7812,7 @@ msgstr "گروه خونی"
|
||||
#. Accounts'
|
||||
#: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json
|
||||
msgid "Body"
|
||||
msgstr "بدنه"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the body_text (Text Editor) field in DocType 'Dunning'
|
||||
#. Label of the body_text (Text Editor) field in DocType 'Dunning Letter Text'
|
||||
@@ -9378,7 +9378,7 @@ msgstr "اگر ثبت انتقال مواد مورد نیاز نیست علام
|
||||
#. Label of the warehouse_group (Link) field in DocType 'Item Reorder'
|
||||
#: erpnext/stock/doctype/item_reorder/item_reorder.json
|
||||
msgid "Check in (group)"
|
||||
msgstr "اعلام حضور (گروهی)"
|
||||
msgstr "بررسی در (گروه)"
|
||||
|
||||
#. Description of the 'Must be Whole Number' (Check) field in DocType 'UOM'
|
||||
#: erpnext/setup/doctype/uom/uom.json
|
||||
@@ -12413,7 +12413,7 @@ msgstr ""
|
||||
#. Description of a DocType
|
||||
#: erpnext/setup/doctype/website_item_group/website_item_group.json
|
||||
msgid "Cross Listing of Item in multiple groups"
|
||||
msgstr ""
|
||||
msgstr "لیست کردن متقابل آیتمها در چندین گروه"
|
||||
|
||||
#. Name of a UOM
|
||||
#: erpnext/setup/setup_wizard/data/uom_data.json
|
||||
@@ -14376,7 +14376,7 @@ msgstr "هزینه معوق"
|
||||
#: erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
|
||||
#: erpnext/stock/doctype/item_default/item_default.json
|
||||
msgid "Deferred Expense Account"
|
||||
msgstr "حساب هزینه معوق"
|
||||
msgstr ""
|
||||
|
||||
#. Option for the 'Entry Type' (Select) field in DocType 'Journal Entry'
|
||||
#. Label of the deferred_revenue (Section Break) field in DocType 'POS Invoice
|
||||
@@ -18407,7 +18407,7 @@ msgstr "سال مالی {0} الزامی است"
|
||||
|
||||
#: erpnext/stock/report/incorrect_serial_and_batch_bundle/incorrect_serial_and_batch_bundle.js:28
|
||||
msgid "Fix SABB Entry"
|
||||
msgstr ""
|
||||
msgstr "رفع مشکل ثبت SABB"
|
||||
|
||||
#. Option for the 'Calculate Based On' (Select) field in DocType 'Shipping
|
||||
#. Rule'
|
||||
@@ -20573,7 +20573,7 @@ msgstr "ساعت"
|
||||
#: erpnext/manufacturing/doctype/job_card/job_card.json
|
||||
#: erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json
|
||||
msgid "Hour Rate"
|
||||
msgstr "نرخ ساعت"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the hours (Float) field in DocType 'Workstation Working Hour'
|
||||
#: erpnext/manufacturing/doctype/workstation_working_hour/workstation_working_hour.json
|
||||
@@ -21311,7 +21311,7 @@ msgstr "در درصد"
|
||||
#: erpnext/manufacturing/doctype/work_order/work_order.json
|
||||
#: erpnext/stock/doctype/quality_inspection/quality_inspection.json
|
||||
msgid "In Process"
|
||||
msgstr "در جریان"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/stock/report/item_variant_details/item_variant_details.py:107
|
||||
msgid "In Production"
|
||||
@@ -22361,7 +22361,7 @@ msgstr "ثبتهای دفتر نامعتبر"
|
||||
|
||||
#: erpnext/assets/doctype/asset/asset.py:450
|
||||
msgid "Invalid Net Purchase Amount"
|
||||
msgstr ""
|
||||
msgstr "مبلغ خالص خرید نامعتبر است"
|
||||
|
||||
#: erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py:77
|
||||
#: erpnext/accounts/general_ledger.py:796
|
||||
@@ -22442,7 +22442,7 @@ msgstr "باندل سریال و دسته نامعتبر"
|
||||
#: erpnext/stock/doctype/stock_entry/stock_entry.py:891
|
||||
#: erpnext/stock/doctype/stock_entry/stock_entry.py:913
|
||||
msgid "Invalid Source and Target Warehouse"
|
||||
msgstr ""
|
||||
msgstr "انبار منبع و هدف نامعتبر"
|
||||
|
||||
#: erpnext/controllers/item_variant.py:145
|
||||
msgid "Invalid Value"
|
||||
@@ -23073,7 +23073,7 @@ msgstr ""
|
||||
#. Label of the is_paid (Check) field in DocType 'Purchase Invoice'
|
||||
#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
|
||||
msgid "Is Paid"
|
||||
msgstr "پرداخت شده"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the is_paused (Check) field in DocType 'Job Card'
|
||||
#: erpnext/manufacturing/doctype/job_card/job_card.json
|
||||
@@ -26776,7 +26776,7 @@ msgstr "شماره قطعه تولید کننده <b>{0}</b> نامعتبر اس
|
||||
#. Description of a DocType
|
||||
#: erpnext/stock/doctype/manufacturer/manufacturer.json
|
||||
msgid "Manufacturers used in Items"
|
||||
msgstr ""
|
||||
msgstr "تولیدکنندگان مورد استفاده در آیتمها"
|
||||
|
||||
#. Label of the work_order_details_section (Section Break) field in DocType
|
||||
#. 'Production Plan Sub Assembly Item'
|
||||
@@ -28020,7 +28020,7 @@ msgstr "نحوه پرداختها"
|
||||
#. Label of the model (Data) field in DocType 'Vehicle'
|
||||
#: erpnext/setup/doctype/vehicle/vehicle.json
|
||||
msgid "Model"
|
||||
msgstr "مدل"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the section_break_11 (Section Break) field in DocType 'POS Closing
|
||||
#. Entry'
|
||||
@@ -28491,7 +28491,7 @@ msgstr "مبلغ خالص خرید"
|
||||
|
||||
#: erpnext/assets/doctype/asset/asset.py:385
|
||||
msgid "Net Purchase Amount is mandatory"
|
||||
msgstr ""
|
||||
msgstr "مبلغ خالص خرید الزامی است"
|
||||
|
||||
#: erpnext/assets/doctype/asset/asset.py:445
|
||||
msgid "Net Purchase Amount should be <b>equal</b> to purchase amount of one single Asset."
|
||||
@@ -29657,7 +29657,7 @@ msgstr "جبران برای بعد حسابداری"
|
||||
#: erpnext/setup/doctype/supplier_group/supplier_group.json
|
||||
#: erpnext/stock/doctype/warehouse/warehouse.json
|
||||
msgid "Old Parent"
|
||||
msgstr "مرجع پیشین"
|
||||
msgstr ""
|
||||
|
||||
#. Option for the 'Reconciliation Takes Effect On' (Select) field in DocType
|
||||
#. 'Company'
|
||||
@@ -31522,7 +31522,7 @@ msgstr "دسته والد"
|
||||
#. Label of the parent_company (Link) field in DocType 'Company'
|
||||
#: erpnext/setup/doctype/company/company.json
|
||||
msgid "Parent Company"
|
||||
msgstr "شرکت مادر"
|
||||
msgstr "شرکت والد"
|
||||
|
||||
#: erpnext/setup/doctype/company/company.py:555
|
||||
msgid "Parent Company must be a group company"
|
||||
@@ -32196,7 +32196,7 @@ msgstr "پرداخت"
|
||||
#: erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.json
|
||||
#: erpnext/accounts/doctype/payment_request/payment_request.json
|
||||
msgid "Payment Account"
|
||||
msgstr "حساب پرداخت"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the payment_amount (Currency) field in DocType 'Overdue Payment'
|
||||
#. Label of the payment_amount (Currency) field in DocType 'Payment Schedule'
|
||||
@@ -33687,7 +33687,7 @@ msgstr "لطفا بیش از 500 آیتم را همزمان ایجاد نکنی
|
||||
|
||||
#: erpnext/accounts/doctype/budget/budget.py:133
|
||||
msgid "Please enable Applicable on Booking Actual Expenses"
|
||||
msgstr "لطفاً Applicable on Booking Actual Expenses را فعال کنید"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/accounts/doctype/budget/budget.py:129
|
||||
msgid "Please enable Applicable on Purchase Order and Applicable on Booking Actual Expenses"
|
||||
@@ -33711,7 +33711,7 @@ msgstr "لطفاً {} را در {} فعال کنید تا یک مورد در چ
|
||||
|
||||
#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:373
|
||||
msgid "Please ensure that the {0} account is a Balance Sheet account. You can change the parent account to a Balance Sheet account or select a different account."
|
||||
msgstr ""
|
||||
msgstr "لطفاً مطمئن شوید که حساب {0} یک حساب ترازنامه است. می توانید حساب مادر را به حساب ترازنامه تغییر دهید یا حساب دیگری را انتخاب کنید."
|
||||
|
||||
#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py:381
|
||||
msgid "Please ensure that the {0} account {1} is a Payable account. You can change the account type to Payable or select a different account."
|
||||
@@ -34190,7 +34190,7 @@ msgstr ""
|
||||
|
||||
#: erpnext/stock/report/incorrect_serial_and_batch_bundle/incorrect_serial_and_batch_bundle.js:33
|
||||
msgid "Please select at least one row to fix"
|
||||
msgstr ""
|
||||
msgstr "لطفا حداقل یک ردیف را برای اصلاح انتخاب کنید"
|
||||
|
||||
#: erpnext/selling/doctype/sales_order/sales_order.js:1274
|
||||
msgid "Please select atleast one item to continue"
|
||||
@@ -35748,7 +35748,7 @@ msgstr "فرآیندها"
|
||||
#. Voucher Detail'
|
||||
#: erpnext/accounts/doctype/process_period_closing_voucher_detail/process_period_closing_voucher_detail.json
|
||||
msgid "Processing Date"
|
||||
msgstr ""
|
||||
msgstr "تاریخ پردازش"
|
||||
|
||||
#: erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py:52
|
||||
msgid "Processing XML Files"
|
||||
@@ -37990,7 +37990,7 @@ msgstr "ایجاد درخواست مواد زمانی که موجودی به س
|
||||
#. Label of the complaint_raised_by (Data) field in DocType 'Warranty Claim'
|
||||
#: erpnext/support/doctype/warranty_claim/warranty_claim.json
|
||||
msgid "Raised By"
|
||||
msgstr "مطرح شده توسط"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the raised_by (Data) field in DocType 'Issue'
|
||||
#: erpnext/support/doctype/issue/issue.json
|
||||
@@ -39160,11 +39160,11 @@ msgstr "منابع"
|
||||
|
||||
#: erpnext/stock/doctype/delivery_note/delivery_note.py:387
|
||||
msgid "References to Sales Invoices are Incomplete"
|
||||
msgstr ""
|
||||
msgstr "ارجاعات به فاکتورهای فروش ناقص است"
|
||||
|
||||
#: erpnext/stock/doctype/delivery_note/delivery_note.py:382
|
||||
msgid "References to Sales Orders are Incomplete"
|
||||
msgstr ""
|
||||
msgstr "ارجاعات به سفارشهای فروش ناقص است"
|
||||
|
||||
#: erpnext/accounts/doctype/payment_entry/payment_entry.py:733
|
||||
msgid "References {0} of type {1} had no outstanding amount left before submitting the Payment Entry. Now they have a negative outstanding amount."
|
||||
@@ -40169,7 +40169,7 @@ msgstr "بازنشانی قرارداد سطح سرویس."
|
||||
#. Label of the resignation_letter_date (Date) field in DocType 'Employee'
|
||||
#: erpnext/setup/doctype/employee/employee.json
|
||||
msgid "Resignation Letter Date"
|
||||
msgstr "تاریخ استعفا نامه"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the sb_00 (Section Break) field in DocType 'Quality Action'
|
||||
#. Label of the resolution (Text Editor) field in DocType 'Quality Action
|
||||
@@ -40192,7 +40192,7 @@ msgstr "حل و فصل توسط"
|
||||
#: erpnext/support/doctype/issue/issue.json
|
||||
#: erpnext/support/doctype/warranty_claim/warranty_claim.json
|
||||
msgid "Resolution Date"
|
||||
msgstr "تاریخ حل و فصل"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the section_break_19 (Section Break) field in DocType 'Issue'
|
||||
#. Label of the resolution_details (Text Editor) field in DocType 'Issue'
|
||||
@@ -40241,7 +40241,7 @@ msgstr "حل شد"
|
||||
#. Label of the resolved_by (Link) field in DocType 'Warranty Claim'
|
||||
#: erpnext/support/doctype/warranty_claim/warranty_claim.json
|
||||
msgid "Resolved By"
|
||||
msgstr "حل شده توسط"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the response_by (Datetime) field in DocType 'Issue'
|
||||
#: erpnext/support/doctype/issue/issue.json
|
||||
@@ -40513,7 +40513,7 @@ msgstr ""
|
||||
#: erpnext/selling/page/point_of_sale/pos_past_order_summary.js:138
|
||||
#: erpnext/stock/doctype/shipment/shipment.json
|
||||
msgid "Returned"
|
||||
msgstr "بازگشت"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the returned_against (Data) field in DocType 'Serial and Batch
|
||||
#. Bundle'
|
||||
@@ -40835,7 +40835,7 @@ msgstr "مجموع گرد شده"
|
||||
#: erpnext/stock/doctype/delivery_note/delivery_note.json
|
||||
#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
|
||||
msgid "Rounded Total (Company Currency)"
|
||||
msgstr "کل گرد شده (ارز شرکت)"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the rounding_adjustment (Currency) field in DocType 'POS Invoice'
|
||||
#. Label of the rounding_adjustment (Currency) field in DocType 'Purchase
|
||||
@@ -41976,7 +41976,7 @@ msgstr "ردیف {0}: {1} {2} با {3} مطابقت ندارد"
|
||||
|
||||
#: erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py:102
|
||||
msgid "Row {0}: {2} Item {1} does not exist in {2} {3}"
|
||||
msgstr ""
|
||||
msgstr "ردیف {0}: {2} آیتم {1} در {2} {3} وجود ندارد"
|
||||
|
||||
#: erpnext/utilities/transaction_base.py:558
|
||||
msgid "Row {1}: Quantity ({0}) cannot be a fraction. To allow this, disable '{2}' in UOM {3}."
|
||||
@@ -42158,7 +42158,7 @@ msgstr "حقوق"
|
||||
#. Label of the salary_currency (Link) field in DocType 'Employee'
|
||||
#: erpnext/setup/doctype/employee/employee.json
|
||||
msgid "Salary Currency"
|
||||
msgstr "ارز حقوق و دستمزد"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the salary_mode (Select) field in DocType 'Employee'
|
||||
#: erpnext/setup/doctype/employee/employee.json
|
||||
@@ -43579,7 +43579,7 @@ msgstr "حسابی را برای چاپ با ارز حساب انتخاب کنی
|
||||
|
||||
#: erpnext/selling/page/point_of_sale/pos_past_order_summary.js:19
|
||||
msgid "Select an invoice to load summary data"
|
||||
msgstr ""
|
||||
msgstr "برای بارگیری خلاصه دادهها، فاکتور را انتخاب کنید"
|
||||
|
||||
#: erpnext/selling/doctype/quotation/quotation.js:340
|
||||
msgid "Select an item from each set to be used in the Sales Order."
|
||||
@@ -44136,7 +44136,7 @@ msgstr "شماره های سریال در ورودی های رزرو موجود
|
||||
|
||||
#: erpnext/stock/doctype/serial_and_batch_bundle/serial_and_batch_bundle.py:333
|
||||
msgid "Serial Nos {0} are already Delivered. You cannot use them again in Manufacture / Repack entry."
|
||||
msgstr ""
|
||||
msgstr "شماره سریالهای {0} قبلاً تحویل داده شدهاند. شما نمیتوانید دوباره از آنها در ثبت ساخت / بستهبندی مجدد استفاده کنید."
|
||||
|
||||
#. Label of the serial_no_series (Data) field in DocType 'Item'
|
||||
#: erpnext/stock/doctype/item/item.json
|
||||
@@ -44443,7 +44443,7 @@ msgstr "کل مبلغ هزینه خدمات"
|
||||
#. 'Asset Capitalization'
|
||||
#: erpnext/assets/doctype/asset_capitalization/asset_capitalization.json
|
||||
msgid "Service Expenses"
|
||||
msgstr "هزینه های خدمات"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the service_item (Link) field in DocType 'Subcontracting BOM'
|
||||
#: erpnext/subcontracting/doctype/subcontracting_bom/subcontracting_bom.json
|
||||
@@ -44679,7 +44679,7 @@ msgstr "تنظیم هزینه عملیاتی بر اساس مقدار BOM"
|
||||
|
||||
#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:88
|
||||
msgid "Set Parent Row No in Items Table"
|
||||
msgstr ""
|
||||
msgstr "تنظیم شماره ردیف والد در جدول آیتمها"
|
||||
|
||||
#. Label of the set_posting_date (Check) field in DocType 'POS Opening Entry'
|
||||
#: erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.json
|
||||
@@ -48089,7 +48089,7 @@ msgstr ""
|
||||
#: erpnext/setup/doctype/driver/driver.json
|
||||
#: erpnext/setup/doctype/employee/employee.json
|
||||
msgid "Suspended"
|
||||
msgstr "معلق"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/selling/page/point_of_sale/pos_payment.js:442
|
||||
msgid "Switch Between Payment Modes"
|
||||
@@ -48372,7 +48372,7 @@ msgstr ""
|
||||
|
||||
#: erpnext/manufacturing/doctype/work_order/work_order.py:741
|
||||
msgid "Target Warehouse is required before Submit"
|
||||
msgstr ""
|
||||
msgstr "انبار هدف قبل از ارسال الزامی است"
|
||||
|
||||
#: erpnext/controllers/selling_controller.py:840
|
||||
msgid "Target Warehouse is set for some items but the customer is not an internal customer."
|
||||
@@ -50076,7 +50076,7 @@ msgstr "زمان مورد نیاز (بر حسب دقیقه)"
|
||||
#. Label of the time_sheet (Link) field in DocType 'Sales Invoice Timesheet'
|
||||
#: erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json
|
||||
msgid "Time Sheet"
|
||||
msgstr "برگه زمان"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the time_sheet_list (Section Break) field in DocType 'POS Invoice'
|
||||
#. Label of the time_sheet_list (Section Break) field in DocType 'Sales
|
||||
@@ -50738,7 +50738,7 @@ msgstr "کل مبلغ صورتحساب (از طریق فاکتور فروش)"
|
||||
#. Label of the total_billed_hours (Float) field in DocType 'Timesheet'
|
||||
#: erpnext/projects/doctype/timesheet/timesheet.json
|
||||
msgid "Total Billed Hours"
|
||||
msgstr "مجموع ساعات صورتحساب"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the total_billing_amount (Currency) field in DocType 'POS Invoice'
|
||||
#. Label of the total_billing_amount (Currency) field in DocType 'Sales
|
||||
@@ -50890,7 +50890,7 @@ msgstr "کل زمان نگهداری"
|
||||
#. Label of the total_holidays (Int) field in DocType 'Holiday List'
|
||||
#: erpnext/setup/doctype/holiday_list/holiday_list.json
|
||||
msgid "Total Holidays"
|
||||
msgstr "کل تعطیلات"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py:115
|
||||
msgid "Total Income"
|
||||
@@ -51231,7 +51231,7 @@ msgstr "کل مالیات"
|
||||
#: erpnext/stock/doctype/delivery_note/delivery_note.json
|
||||
#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
|
||||
msgid "Total Taxes and Charges"
|
||||
msgstr "کل مالیات ها و هزینه ها"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the base_total_taxes_and_charges (Currency) field in DocType
|
||||
#. 'Payment Entry'
|
||||
@@ -52174,7 +52174,7 @@ msgstr "واحد"
|
||||
|
||||
#: erpnext/controllers/accounts_controller.py:3830
|
||||
msgid "Unit Price"
|
||||
msgstr ""
|
||||
msgstr "قیمت واحد"
|
||||
|
||||
#: erpnext/buying/report/procurement_tracker/procurement_tracker.py:68
|
||||
msgid "Unit of Measure"
|
||||
|
||||
@@ -3,7 +3,7 @@ msgstr ""
|
||||
"Project-Id-Version: frappe\n"
|
||||
"Report-Msgid-Bugs-To: hello@frappe.io\n"
|
||||
"POT-Creation-Date: 2025-11-10 12:11+0000\n"
|
||||
"PO-Revision-Date: 2025-11-10 21:17\n"
|
||||
"PO-Revision-Date: 2025-11-15 21:35\n"
|
||||
"Last-Translator: hello@frappe.io\n"
|
||||
"Language-Team: French\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -820,7 +820,7 @@ msgstr ""
|
||||
#. Header text in the Stock Workspace
|
||||
#: erpnext/stock/workspace/stock/stock.json
|
||||
msgid "<span class=\"h4\"><b>Masters & Reports</b></span>"
|
||||
msgstr "<span class=\"h4\"><b>Rapports & Fonctionnalités principales</b></span>"
|
||||
msgstr ""
|
||||
|
||||
#. Header text in the Selling Workspace
|
||||
#. Header text in the Stock Workspace
|
||||
@@ -3056,7 +3056,7 @@ msgstr "Montant de l'Avance"
|
||||
#: erpnext/buying/doctype/purchase_order/purchase_order.json
|
||||
#: erpnext/selling/doctype/sales_order/sales_order.json
|
||||
msgid "Advance Paid"
|
||||
msgstr "Avance Payée"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/buying/doctype/purchase_order/purchase_order_list.js:75
|
||||
#: erpnext/selling/doctype/sales_order/sales_order_list.js:122
|
||||
@@ -3159,7 +3159,7 @@ msgstr "Seul les paiements anticipés alloués aux commandes seront uniquement r
|
||||
#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
|
||||
#: erpnext/accounts/doctype/sales_invoice/sales_invoice.json
|
||||
msgid "Advances"
|
||||
msgstr "Avances"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/setup/setup_wizard/data/marketing_source.txt:3
|
||||
msgid "Advertisement"
|
||||
@@ -7824,7 +7824,7 @@ msgstr "Groupe Sanguin"
|
||||
#. Accounts'
|
||||
#: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json
|
||||
msgid "Body"
|
||||
msgstr "Corps"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the body_text (Text Editor) field in DocType 'Dunning'
|
||||
#. Label of the body_text (Text Editor) field in DocType 'Dunning Letter Text'
|
||||
@@ -13528,7 +13528,7 @@ msgstr ""
|
||||
#. Label of the date_of_birth (Date) field in DocType 'Employee'
|
||||
#: erpnext/setup/doctype/employee/employee.json
|
||||
msgid "Date of Birth"
|
||||
msgstr "Date de naissance"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/setup/doctype/employee/employee.py:147
|
||||
msgid "Date of Birth cannot be greater than today."
|
||||
@@ -13561,7 +13561,7 @@ msgstr "Date d'Émission"
|
||||
#. Label of the date_of_joining (Date) field in DocType 'Employee'
|
||||
#: erpnext/setup/doctype/employee/employee.json
|
||||
msgid "Date of Joining"
|
||||
msgstr "Date d'Embauche"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py:273
|
||||
msgid "Date of Transaction"
|
||||
@@ -14388,7 +14388,7 @@ msgstr "Frais différés"
|
||||
#: erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
|
||||
#: erpnext/stock/doctype/item_default/item_default.json
|
||||
msgid "Deferred Expense Account"
|
||||
msgstr "Compte de dépenses différées"
|
||||
msgstr ""
|
||||
|
||||
#. Option for the 'Entry Type' (Select) field in DocType 'Journal Entry'
|
||||
#. Label of the deferred_revenue (Section Break) field in DocType 'POS Invoice
|
||||
@@ -16632,7 +16632,7 @@ msgstr "Formation de l'Employé"
|
||||
#. Label of the exit (Tab Break) field in DocType 'Employee'
|
||||
#: erpnext/setup/doctype/employee/employee.json
|
||||
msgid "Employee Exit"
|
||||
msgstr "Sortie de l’employé"
|
||||
msgstr ""
|
||||
|
||||
#. Name of a DocType
|
||||
#: erpnext/setup/doctype/employee_external_work_history/employee_external_work_history.json
|
||||
@@ -16675,7 +16675,7 @@ msgstr "Nom de l'Employé"
|
||||
#. Label of the employee_number (Data) field in DocType 'Employee'
|
||||
#: erpnext/setup/doctype/employee/employee.json
|
||||
msgid "Employee Number"
|
||||
msgstr "Numéro d'Employé"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the employee_user_id (Link) field in DocType 'Call Log'
|
||||
#: erpnext/telephony/doctype/call_log/call_log.json
|
||||
@@ -16892,7 +16892,7 @@ msgstr ""
|
||||
#. Label of the encashment_date (Date) field in DocType 'Employee'
|
||||
#: erpnext/setup/doctype/employee/employee.json
|
||||
msgid "Encashment Date"
|
||||
msgstr "Date de l'Encaissement"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/crm/doctype/contract/contract.py:70
|
||||
msgid "End Date cannot be before Start Date."
|
||||
@@ -20555,7 +20555,7 @@ msgstr "Nom de la Liste de Vacances"
|
||||
#. Label of the holidays (Table) field in DocType 'Holiday List'
|
||||
#: erpnext/setup/doctype/holiday_list/holiday_list.json
|
||||
msgid "Holidays"
|
||||
msgstr "Jours Fériés"
|
||||
msgstr ""
|
||||
|
||||
#. Option for the 'Forecasting Method' (Select) field in DocType 'Sales
|
||||
#. Forecast'
|
||||
@@ -20585,7 +20585,7 @@ msgstr ""
|
||||
#: erpnext/manufacturing/doctype/job_card/job_card.json
|
||||
#: erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json
|
||||
msgid "Hour Rate"
|
||||
msgstr "Tarif Horaire"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the hours (Float) field in DocType 'Workstation Working Hour'
|
||||
#: erpnext/manufacturing/doctype/workstation_working_hour/workstation_working_hour.json
|
||||
@@ -21322,7 +21322,7 @@ msgstr "En pourcentage"
|
||||
#: erpnext/manufacturing/doctype/work_order/work_order.json
|
||||
#: erpnext/stock/doctype/quality_inspection/quality_inspection.json
|
||||
msgid "In Process"
|
||||
msgstr "En Cours"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/stock/report/item_variant_details/item_variant_details.py:107
|
||||
msgid "In Production"
|
||||
@@ -22185,7 +22185,7 @@ msgstr "Paramètres de transfert entre entrepôts"
|
||||
#. Label of the interest (Currency) field in DocType 'Overdue Payment'
|
||||
#: erpnext/accounts/doctype/overdue_payment/overdue_payment.json
|
||||
msgid "Interest"
|
||||
msgstr "Intérêt"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/accounts/doctype/payment_entry/payment_entry.py:3052
|
||||
msgid "Interest and/or dunning fee"
|
||||
@@ -29626,7 +29626,7 @@ msgstr "Valeur Compteur Kilométrique (Dernier)"
|
||||
#. Label of the scheduled_confirmation_date (Date) field in DocType 'Employee'
|
||||
#: erpnext/setup/doctype/employee/employee.json
|
||||
msgid "Offer Date"
|
||||
msgstr "Date de la Proposition"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py:29
|
||||
#: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py:42
|
||||
@@ -29668,7 +29668,7 @@ msgstr ""
|
||||
#: erpnext/setup/doctype/supplier_group/supplier_group.json
|
||||
#: erpnext/stock/doctype/warehouse/warehouse.json
|
||||
msgid "Old Parent"
|
||||
msgstr "Grand Parent"
|
||||
msgstr ""
|
||||
|
||||
#. Option for the 'Reconciliation Takes Effect On' (Select) field in DocType
|
||||
#. 'Company'
|
||||
@@ -30957,7 +30957,7 @@ msgstr "Surproduction pour les ventes et les bons de travail"
|
||||
#. Option for the 'Current Address Is' (Select) field in DocType 'Employee'
|
||||
#: erpnext/setup/doctype/employee/employee.json
|
||||
msgid "Owned"
|
||||
msgstr "Détenu"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/accounts/report/sales_payment_summary/sales_payment_summary.js:29
|
||||
#: erpnext/accounts/report/sales_payment_summary/sales_payment_summary.py:23
|
||||
@@ -32206,7 +32206,7 @@ msgstr "Paiement"
|
||||
#: erpnext/accounts/doctype/payment_gateway_account/payment_gateway_account.json
|
||||
#: erpnext/accounts/doctype/payment_request/payment_request.json
|
||||
msgid "Payment Account"
|
||||
msgstr "Compte de Paiement"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the payment_amount (Currency) field in DocType 'Overdue Payment'
|
||||
#. Label of the payment_amount (Currency) field in DocType 'Payment Schedule'
|
||||
@@ -32797,7 +32797,7 @@ msgstr ""
|
||||
#. Account'
|
||||
#: erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
|
||||
msgid "Payroll Entry"
|
||||
msgstr "Entrée de la paie"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts.py:88
|
||||
#: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py:119
|
||||
@@ -38000,7 +38000,7 @@ msgstr "Augmenter la demande d'article lorsque le stock atteint le niveau de com
|
||||
#. Label of the complaint_raised_by (Data) field in DocType 'Warranty Claim'
|
||||
#: erpnext/support/doctype/warranty_claim/warranty_claim.json
|
||||
msgid "Raised By"
|
||||
msgstr "Créé par"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the raised_by (Data) field in DocType 'Issue'
|
||||
#: erpnext/support/doctype/issue/issue.json
|
||||
@@ -39311,7 +39311,7 @@ msgstr "La date de sortie doit être dans le futur"
|
||||
#. Label of the relieving_date (Date) field in DocType 'Employee'
|
||||
#: erpnext/setup/doctype/employee/employee.json
|
||||
msgid "Relieving Date"
|
||||
msgstr "Date de Relève"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/public/js/bank_reconciliation_tool/dialog_manager.js:125
|
||||
msgid "Remaining"
|
||||
@@ -40179,7 +40179,7 @@ msgstr "Réinitialisation de l'accord de niveau de service."
|
||||
#. Label of the resignation_letter_date (Date) field in DocType 'Employee'
|
||||
#: erpnext/setup/doctype/employee/employee.json
|
||||
msgid "Resignation Letter Date"
|
||||
msgstr "Date de la Lettre de Démission"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the sb_00 (Section Break) field in DocType 'Quality Action'
|
||||
#. Label of the resolution (Text Editor) field in DocType 'Quality Action
|
||||
@@ -40523,7 +40523,7 @@ msgstr ""
|
||||
#: erpnext/selling/page/point_of_sale/pos_past_order_summary.js:138
|
||||
#: erpnext/stock/doctype/shipment/shipment.json
|
||||
msgid "Returned"
|
||||
msgstr "retourné"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the returned_against (Data) field in DocType 'Serial and Batch
|
||||
#. Bundle'
|
||||
@@ -40845,7 +40845,7 @@ msgstr "Total arrondi"
|
||||
#: erpnext/stock/doctype/delivery_note/delivery_note.json
|
||||
#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
|
||||
msgid "Rounded Total (Company Currency)"
|
||||
msgstr "Total Arrondi (Devise Société)"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the rounding_adjustment (Currency) field in DocType 'POS Invoice'
|
||||
#. Label of the rounding_adjustment (Currency) field in DocType 'Purchase
|
||||
@@ -48449,7 +48449,7 @@ msgstr "Type de tâche"
|
||||
#. Option for the '% Complete Method' (Select) field in DocType 'Project'
|
||||
#: erpnext/projects/doctype/project/project.json
|
||||
msgid "Task Weight"
|
||||
msgstr "Poids de la Tâche"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/projects/doctype/project_template/project_template.py:41
|
||||
msgid "Task {0} depends on Task {1}. Please add Task {1} to the Tasks list."
|
||||
@@ -48988,7 +48988,7 @@ msgstr ""
|
||||
#: erpnext/accounts/doctype/payment_terms_template/payment_terms_template.json
|
||||
#: erpnext/quality_management/doctype/quality_feedback_template/quality_feedback_template.json
|
||||
msgid "Template Name"
|
||||
msgstr "Nom du Modèle"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the template_task (Data) field in DocType 'Task'
|
||||
#: erpnext/projects/doctype/task/task.json
|
||||
@@ -49066,7 +49066,7 @@ msgstr "Détails du Terme"
|
||||
#: erpnext/stock/doctype/material_request/material_request.json
|
||||
#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
|
||||
msgid "Terms"
|
||||
msgstr "Termes"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the terms_section_break (Section Break) field in DocType 'Purchase
|
||||
#. Order'
|
||||
@@ -51537,7 +51537,7 @@ msgstr ""
|
||||
|
||||
#: erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.py:45
|
||||
msgid "Transaction Name"
|
||||
msgstr "Nom de la transaction"
|
||||
msgstr ""
|
||||
|
||||
#. Label of the transaction_settings_section (Tab Break) field in DocType
|
||||
#. 'Buying Settings'
|
||||
@@ -53793,7 +53793,7 @@ msgstr ""
|
||||
|
||||
#: erpnext/setup/setup_wizard/data/marketing_source.txt:10
|
||||
msgid "Walk In"
|
||||
msgstr "Spontané"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js:4
|
||||
msgid "Warehouse Capacity Summary"
|
||||
|
||||
@@ -3,7 +3,7 @@ msgstr ""
|
||||
"Project-Id-Version: frappe\n"
|
||||
"Report-Msgid-Bugs-To: hello@frappe.io\n"
|
||||
"POT-Creation-Date: 2025-11-10 12:11+0000\n"
|
||||
"PO-Revision-Date: 2025-11-10 21:17\n"
|
||||
"PO-Revision-Date: 2025-11-14 21:33\n"
|
||||
"Last-Translator: hello@frappe.io\n"
|
||||
"Language-Team: Hungarian\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -25,11 +25,11 @@ msgstr ""
|
||||
|
||||
#: erpnext/selling/doctype/quotation/quotation.js:73
|
||||
msgid " Address"
|
||||
msgstr ""
|
||||
msgstr " Cím"
|
||||
|
||||
#: erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py:677
|
||||
msgid " Amount"
|
||||
msgstr ""
|
||||
msgstr " Összeg"
|
||||
|
||||
#: erpnext/public/js/bom_configurator/bom_configurator.bundle.js:114
|
||||
msgid " BOM"
|
||||
|
||||
@@ -3,7 +3,7 @@ msgstr ""
|
||||
"Project-Id-Version: frappe\n"
|
||||
"Report-Msgid-Bugs-To: hello@frappe.io\n"
|
||||
"POT-Creation-Date: 2025-11-10 12:11+0000\n"
|
||||
"PO-Revision-Date: 2025-11-10 21:18\n"
|
||||
"PO-Revision-Date: 2025-11-15 21:35\n"
|
||||
"Last-Translator: hello@frappe.io\n"
|
||||
"Language-Team: Indonesian\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -16771,7 +16771,7 @@ msgstr ""
|
||||
|
||||
#: erpnext/manufacturing/doctype/workstation/workstation.js:351
|
||||
msgid "Employees"
|
||||
msgstr "Para karyawan"
|
||||
msgstr ""
|
||||
|
||||
#: erpnext/stock/doctype/batch/batch_list.js:16
|
||||
msgid "Empty"
|
||||
@@ -19907,7 +19907,7 @@ msgstr "Sasaran dan Prosedur"
|
||||
#. Group in Quality Procedure's connections
|
||||
#: erpnext/quality_management/doctype/quality_procedure/quality_procedure.json
|
||||
msgid "Goals"
|
||||
msgstr "tujuan"
|
||||
msgstr ""
|
||||
|
||||
#. Option for the 'Shipment Type' (Select) field in DocType 'Shipment'
|
||||
#: erpnext/stock/doctype/shipment/shipment.json
|
||||
|
||||
@@ -3,7 +3,7 @@ msgstr ""
|
||||
"Project-Id-Version: frappe\n"
|
||||
"Report-Msgid-Bugs-To: hello@frappe.io\n"
|
||||
"POT-Creation-Date: 2025-11-10 12:11+0000\n"
|
||||
"PO-Revision-Date: 2025-11-10 21:17\n"
|
||||
"PO-Revision-Date: 2025-11-15 21:35\n"
|
||||
"Last-Translator: hello@frappe.io\n"
|
||||
"Language-Team: Polish\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -16652,7 +16652,7 @@ msgstr ""
|
||||
#. Label of the exit (Tab Break) field in DocType 'Employee'
|
||||
#: erpnext/setup/doctype/employee/employee.json
|
||||
msgid "Employee Exit"
|
||||
msgstr "Odejście pracownika"
|
||||
msgstr ""
|
||||
|
||||
#. Name of a DocType
|
||||
#: erpnext/setup/doctype/employee_external_work_history/employee_external_work_history.json
|
||||
|
||||
@@ -3,7 +3,7 @@ msgstr ""
|
||||
"Project-Id-Version: frappe\n"
|
||||
"Report-Msgid-Bugs-To: hello@frappe.io\n"
|
||||
"POT-Creation-Date: 2025-11-10 12:11+0000\n"
|
||||
"PO-Revision-Date: 2025-11-10 21:18\n"
|
||||
"PO-Revision-Date: 2025-11-15 21:35\n"
|
||||
"Last-Translator: hello@frappe.io\n"
|
||||
"Language-Team: Turkish\n"
|
||||
"MIME-Version: 1.0\n"
|
||||
@@ -16709,7 +16709,7 @@ msgstr "Eğitim Hayatı"
|
||||
#. Label of the exit (Tab Break) field in DocType 'Employee'
|
||||
#: erpnext/setup/doctype/employee/employee.json
|
||||
msgid "Employee Exit"
|
||||
msgstr "Çalışan Çıkışı"
|
||||
msgstr ""
|
||||
|
||||
#. Name of a DocType
|
||||
#: erpnext/setup/doctype/employee_external_work_history/employee_external_work_history.json
|
||||
|
||||
@@ -45,7 +45,7 @@ frappe.ui.form.on("BOM", {
|
||||
return {
|
||||
query: "erpnext.manufacturing.doctype.bom.bom.item_query",
|
||||
filters: {
|
||||
is_stock_item: 1,
|
||||
is_stock_item: !frm.doc.is_phantom_bom,
|
||||
},
|
||||
};
|
||||
});
|
||||
@@ -183,7 +183,7 @@ frappe.ui.form.on("BOM", {
|
||||
);
|
||||
}
|
||||
|
||||
if (frm.doc.docstatus == 1) {
|
||||
if (frm.doc.docstatus == 1 && !frm.doc.is_phantom_bom) {
|
||||
frm.add_custom_button(
|
||||
__("Work Order"),
|
||||
function () {
|
||||
@@ -529,6 +529,14 @@ frappe.ui.form.on("BOM", {
|
||||
|
||||
frm.set_value("process_loss_qty", qty);
|
||||
},
|
||||
|
||||
is_phantom_bom(frm) {
|
||||
frm.doc.item = "";
|
||||
frm.doc.uom = "";
|
||||
frm.doc.quantity = 1;
|
||||
frm.doc.items = undefined;
|
||||
frm.refresh();
|
||||
},
|
||||
});
|
||||
|
||||
frappe.ui.form.on("BOM Operation", {
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
"is_default",
|
||||
"allow_alternative_item",
|
||||
"set_rate_of_sub_assembly_item_based_on_bom",
|
||||
"is_phantom_bom",
|
||||
"project",
|
||||
"image",
|
||||
"currency_detail",
|
||||
@@ -201,6 +202,7 @@
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"depends_on": "eval:!doc.is_phantom_bom",
|
||||
"fieldname": "currency_detail",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Cost Configuration"
|
||||
@@ -293,6 +295,7 @@
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"depends_on": "eval:!doc.is_phantom_bom",
|
||||
"fieldname": "scrap_section",
|
||||
"fieldtype": "Tab Break",
|
||||
"label": "Scrap & Process Loss"
|
||||
@@ -310,6 +313,7 @@
|
||||
"oldfieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:!doc.is_phantom_bom",
|
||||
"fieldname": "operating_cost",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Operating Cost",
|
||||
@@ -324,6 +328,7 @@
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:!doc.is_phantom_bom",
|
||||
"fieldname": "scrap_material_cost",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Scrap Material Cost",
|
||||
@@ -336,6 +341,7 @@
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:!doc.is_phantom_bom",
|
||||
"fieldname": "base_operating_cost",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Operating Cost (Company Currency)",
|
||||
@@ -352,6 +358,7 @@
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:!doc.is_phantom_bom",
|
||||
"fieldname": "base_scrap_material_cost",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Scrap Material Cost(Company Currency)",
|
||||
@@ -380,6 +387,7 @@
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:!doc.is_phantom_bom",
|
||||
"fieldname": "project",
|
||||
"fieldtype": "Link",
|
||||
"label": "Project",
|
||||
@@ -427,6 +435,7 @@
|
||||
},
|
||||
{
|
||||
"collapsible": 1,
|
||||
"depends_on": "eval:!doc.is_phantom_bom",
|
||||
"fieldname": "website_section",
|
||||
"fieldtype": "Tab Break",
|
||||
"label": "Website"
|
||||
@@ -536,6 +545,7 @@
|
||||
{
|
||||
"collapsible": 1,
|
||||
"collapsible_depends_on": "eval:doc.with_operations",
|
||||
"depends_on": "eval:!doc.is_phantom_bom",
|
||||
"fieldname": "operations_section_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Operations"
|
||||
@@ -570,6 +580,7 @@
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:!doc.is_phantom_bom",
|
||||
"fieldname": "quality_inspection_section_break",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Quality Inspection"
|
||||
@@ -659,6 +670,12 @@
|
||||
"fieldtype": "Link",
|
||||
"label": "Default Target Warehouse",
|
||||
"options": "Warehouse"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "is_phantom_bom",
|
||||
"fieldtype": "Check",
|
||||
"label": "Is Phantom BOM"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-sitemap",
|
||||
@@ -666,7 +683,7 @@
|
||||
"image_field": "image",
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2025-10-29 17:43:12.966753",
|
||||
"modified": "2025-11-06 15:27:54.806116",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "BOM",
|
||||
|
||||
@@ -135,6 +135,7 @@ class BOM(WebsiteGenerator):
|
||||
inspection_required: DF.Check
|
||||
is_active: DF.Check
|
||||
is_default: DF.Check
|
||||
is_phantom_bom: DF.Check
|
||||
item: DF.Link
|
||||
item_name: DF.Data | None
|
||||
items: DF.Table[BOMItem]
|
||||
@@ -447,6 +448,9 @@ class BOM(WebsiteGenerator):
|
||||
"uom": args["uom"] if args.get("uom") else item and args["stock_uom"] or "",
|
||||
"conversion_factor": args["conversion_factor"] if args.get("conversion_factor") else 1,
|
||||
"bom_no": args["bom_no"],
|
||||
"is_phantom_item": frappe.get_value("BOM", args["bom_no"], "is_phantom_bom")
|
||||
if args["bom_no"]
|
||||
else 0,
|
||||
"rate": rate,
|
||||
"qty": args.get("qty") or args.get("stock_qty") or 1,
|
||||
"stock_qty": args.get("stock_qty") or args.get("qty") or 1,
|
||||
@@ -455,6 +459,9 @@ class BOM(WebsiteGenerator):
|
||||
"sourced_by_supplier": args.get("sourced_by_supplier", 0),
|
||||
}
|
||||
|
||||
if ret_item["is_phantom_item"]:
|
||||
ret_item["do_not_explode"] = 0
|
||||
|
||||
if args.get("do_not_explode"):
|
||||
ret_item["bom_no"] = ""
|
||||
|
||||
@@ -481,7 +488,9 @@ class BOM(WebsiteGenerator):
|
||||
if not frappe.db.get_value("Item", arg["item_code"], "is_customer_provided_item") and not arg.get(
|
||||
"sourced_by_supplier"
|
||||
):
|
||||
if arg.get("bom_no") and self.set_rate_of_sub_assembly_item_based_on_bom:
|
||||
if arg.get("bom_no") and (
|
||||
self.set_rate_of_sub_assembly_item_based_on_bom or arg.get("is_phantom_item")
|
||||
):
|
||||
rate = flt(self.get_bom_unitcost(arg["bom_no"])) * (arg.get("conversion_factor") or 1)
|
||||
else:
|
||||
rate = get_bom_item_rate(arg, self)
|
||||
@@ -888,7 +897,7 @@ class BOM(WebsiteGenerator):
|
||||
|
||||
for d in self.get("items"):
|
||||
old_rate = d.rate
|
||||
if not self.bom_creator and d.is_stock_item:
|
||||
if not self.bom_creator and (d.is_stock_item or d.is_phantom_item):
|
||||
d.rate = self.get_rm_rate(
|
||||
{
|
||||
"company": self.company,
|
||||
@@ -899,6 +908,7 @@ class BOM(WebsiteGenerator):
|
||||
"stock_uom": d.stock_uom,
|
||||
"conversion_factor": d.conversion_factor,
|
||||
"sourced_by_supplier": d.sourced_by_supplier,
|
||||
"is_phantom_item": d.is_phantom_item,
|
||||
}
|
||||
)
|
||||
|
||||
@@ -1277,16 +1287,16 @@ def get_bom_items_as_dict(
|
||||
where
|
||||
bom_item.docstatus < 2
|
||||
and bom.name = %(bom)s
|
||||
and item.is_stock_item in (1, {is_stock_item})
|
||||
and (item.is_stock_item in (1, {is_stock_item})
|
||||
{where_conditions}
|
||||
{group_by_cond}
|
||||
order by idx"""
|
||||
|
||||
is_stock_item = 0 if include_non_stock_items else 1
|
||||
is_stock_item = cint(not include_non_stock_items)
|
||||
if cint(fetch_exploded):
|
||||
query = query.format(
|
||||
table="BOM Explosion Item",
|
||||
where_conditions="",
|
||||
where_conditions=")",
|
||||
is_stock_item=is_stock_item,
|
||||
qty_field="stock_qty",
|
||||
group_by_cond=group_by_cond,
|
||||
@@ -1301,7 +1311,7 @@ def get_bom_items_as_dict(
|
||||
elif fetch_scrap_items:
|
||||
query = query.format(
|
||||
table="BOM Scrap Item",
|
||||
where_conditions="",
|
||||
where_conditions=")",
|
||||
select_columns=", item.description",
|
||||
is_stock_item=is_stock_item,
|
||||
qty_field="stock_qty",
|
||||
@@ -1312,12 +1322,12 @@ def get_bom_items_as_dict(
|
||||
else:
|
||||
query = query.format(
|
||||
table="BOM Item",
|
||||
where_conditions="",
|
||||
where_conditions="or bom_item.is_phantom_item)",
|
||||
is_stock_item=is_stock_item,
|
||||
qty_field="stock_qty" if fetch_qty_in_stock_uom else "qty",
|
||||
select_columns=""", bom_item.uom, bom_item.conversion_factor, bom_item.source_warehouse,
|
||||
bom_item.operation, bom_item.include_item_in_manufacturing, bom_item.sourced_by_supplier,
|
||||
bom_item.description, bom_item.base_rate as rate, bom_item.operation_row_id """,
|
||||
bom_item.description, bom_item.base_rate as rate, bom_item.operation_row_id, bom_item.is_phantom_item , bom_item.bom_no """,
|
||||
group_by_cond=group_by_cond,
|
||||
)
|
||||
items = frappe.db.sql(query, {"qty": qty, "bom": bom, "company": company}, as_dict=True)
|
||||
@@ -1327,7 +1337,24 @@ def get_bom_items_as_dict(
|
||||
if item.operation_row_id:
|
||||
key = (item.item_code, item.operation_row_id)
|
||||
|
||||
if key in item_dict:
|
||||
if item.get("is_phantom_item"):
|
||||
data = get_bom_items_as_dict(
|
||||
item.get("bom_no"),
|
||||
company,
|
||||
qty=item.get("qty"),
|
||||
fetch_exploded=fetch_exploded,
|
||||
fetch_scrap_items=fetch_scrap_items,
|
||||
include_non_stock_items=include_non_stock_items,
|
||||
fetch_qty_in_stock_uom=fetch_qty_in_stock_uom,
|
||||
)
|
||||
|
||||
for k, v in data.items():
|
||||
if item_dict.get(k):
|
||||
item_dict[k]["qty"] += flt(v.qty)
|
||||
else:
|
||||
item_dict[k] = v
|
||||
|
||||
elif key in item_dict:
|
||||
item_dict[key]["qty"] += flt(item.qty)
|
||||
else:
|
||||
item_dict[key] = item
|
||||
@@ -1379,7 +1406,7 @@ def validate_bom_no(item, bom_no):
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_children(parent=None, is_root=False, **filters):
|
||||
def get_children(parent=None, return_all=True, fetch_phantom_items=False, is_root=False, **filters):
|
||||
if not parent or parent == "BOM":
|
||||
frappe.msgprint(_("Please select a BOM"))
|
||||
return
|
||||
@@ -1391,10 +1418,13 @@ def get_children(parent=None, is_root=False, **filters):
|
||||
bom_doc = frappe.get_cached_doc("BOM", frappe.form_dict.parent)
|
||||
frappe.has_permission("BOM", doc=bom_doc, throw=True)
|
||||
|
||||
filters = [["parent", "=", frappe.form_dict.parent]]
|
||||
if not return_all:
|
||||
filters.append(["is_phantom_item", "=", cint(fetch_phantom_items)])
|
||||
bom_items = frappe.get_all(
|
||||
"BOM Item",
|
||||
fields=["item_code", "bom_no as value", "stock_qty", "qty"],
|
||||
filters=[["parent", "=", frappe.form_dict.parent]],
|
||||
fields=["item_code", "bom_no as value", "stock_qty", "qty", "is_phantom_item", "bom_no"],
|
||||
filters=filters,
|
||||
order_by="idx",
|
||||
)
|
||||
|
||||
|
||||
@@ -12,7 +12,10 @@
|
||||
{{ __("Description") }}
|
||||
</h4>
|
||||
<div style="padding-top: 10px;">
|
||||
{{ data.description }}
|
||||
{% if data.is_phantom_item %}
|
||||
<p><b>{{ __("Phantom Item") }}</b></p>
|
||||
{% endif %}
|
||||
<p>{{ data.description }}</p>
|
||||
</div>
|
||||
<hr style="margin: 15px -15px;">
|
||||
<p>
|
||||
|
||||
@@ -794,7 +794,7 @@ def level_order_traversal(node):
|
||||
return traversal
|
||||
|
||||
|
||||
def create_nested_bom(tree, prefix="_Test bom ", submit=True):
|
||||
def create_nested_bom(tree, prefix="_Test bom ", submit=True, phantom_items=None):
|
||||
"""Helper function to create a simple nested bom from tree describing item names. (along with required items)"""
|
||||
|
||||
def create_items(bom_tree):
|
||||
@@ -806,6 +806,9 @@ def create_nested_bom(tree, prefix="_Test bom ", submit=True):
|
||||
).insert()
|
||||
create_items(subtree)
|
||||
|
||||
if not phantom_items:
|
||||
phantom_items = []
|
||||
|
||||
create_items(tree)
|
||||
|
||||
def dfs(tree, node):
|
||||
@@ -824,7 +827,7 @@ def create_nested_bom(tree, prefix="_Test bom ", submit=True):
|
||||
child_items = dfs(tree, item)
|
||||
if child_items:
|
||||
bom_item_code = prefix + item
|
||||
bom = frappe.get_doc(doctype="BOM", item=bom_item_code)
|
||||
bom = frappe.get_doc(doctype="BOM", item=bom_item_code, is_phantom_bom=item in phantom_items)
|
||||
for child_item in child_items.keys():
|
||||
bom.append("items", {"item_code": prefix + child_item})
|
||||
bom.company = "_Test Company"
|
||||
@@ -906,3 +909,15 @@ def create_process_loss_bom_item(item_tuple):
|
||||
return make_item(item_code, {"stock_uom": stock_uom, "valuation_rate": 100})
|
||||
else:
|
||||
return frappe.get_doc("Item", item_code)
|
||||
|
||||
|
||||
def create_tree_for_phantom_bom_tests(): # returns expected explosion result
|
||||
bom_tree_1 = {
|
||||
"Top Level Parent": {
|
||||
"Sub Assembly Level 1-1": {"Phantom Item Level 1-2": {"Item Level 1-3": {}}},
|
||||
"Phantom Item Level 2-1": {"Phantom Item Level 2-2": {"Item Level 2-3": {}}},
|
||||
}
|
||||
}
|
||||
phantom_list = ["Phantom Item Level 1-2", "Phantom Item Level 2-1", "Phantom Item Level 2-2"]
|
||||
create_nested_bom(bom_tree_1, prefix="", phantom_items=phantom_list)
|
||||
return ["Sub Assembly Level 1-1", "Item Level 2-3"]
|
||||
|
||||
@@ -6,7 +6,7 @@ from collections import OrderedDict
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe.model.document import Document
|
||||
from frappe.utils import cint, flt
|
||||
from frappe.utils import cint, flt, sbool
|
||||
|
||||
from erpnext.manufacturing.doctype.bom.bom import get_bom_item_rate
|
||||
|
||||
@@ -29,6 +29,7 @@ BOM_ITEM_FIELDS = [
|
||||
"conversion_factor",
|
||||
"do_not_explode",
|
||||
"operation",
|
||||
"is_phantom_item",
|
||||
]
|
||||
|
||||
|
||||
@@ -305,6 +306,7 @@ class BOMCreator(Document):
|
||||
"allow_alternative_item": 1,
|
||||
"bom_creator": self.name,
|
||||
"bom_creator_item": bom_creator_item,
|
||||
"is_phantom_bom": row.get("is_phantom_item"),
|
||||
}
|
||||
)
|
||||
|
||||
@@ -332,7 +334,7 @@ class BOMCreator(Document):
|
||||
{
|
||||
"bom_no": bom_no,
|
||||
"allow_alternative_item": 1,
|
||||
"allow_scrap_items": 1,
|
||||
"allow_scrap_items": not item.get("is_phantom_item"),
|
||||
"include_item_in_manufacturing": 1,
|
||||
}
|
||||
)
|
||||
@@ -456,12 +458,16 @@ def add_sub_assembly(**kwargs):
|
||||
"is_expandable": 1,
|
||||
"stock_uom": item_info.stock_uom,
|
||||
"operation": bom_item.operation,
|
||||
"is_phantom_item": sbool(kwargs.phantom),
|
||||
},
|
||||
)
|
||||
|
||||
parent_row_no = item_row.idx
|
||||
name = ""
|
||||
else:
|
||||
if sbool(kwargs.phantom):
|
||||
parent_row = next(item for item in doc.items if item.name == kwargs.fg_reference_id)
|
||||
parent_row.db_set("is_phantom_item", 1)
|
||||
parent_row_no = get_parent_row_no(doc, kwargs.fg_reference_id)
|
||||
|
||||
for row in bom_item.get("items"):
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
"sourced_by_supplier",
|
||||
"bom_created",
|
||||
"is_subcontracted",
|
||||
"is_phantom_item",
|
||||
"operation_section",
|
||||
"operation",
|
||||
"column_break_cbnk",
|
||||
@@ -159,8 +160,8 @@
|
||||
"fieldname": "amount",
|
||||
"fieldtype": "Currency",
|
||||
"label": "Amount",
|
||||
"read_only": 1,
|
||||
"options": "currency"
|
||||
"options": "currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_yuca",
|
||||
@@ -229,6 +230,7 @@
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "eval:!doc.is_phantom_item",
|
||||
"fieldname": "operation_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Operation"
|
||||
@@ -245,22 +247,31 @@
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval:!doc.is_phantom_item",
|
||||
"fieldname": "is_subcontracted",
|
||||
"fieldtype": "Check",
|
||||
"label": "Is Subcontracted",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "is_phantom_item",
|
||||
"fieldtype": "Check",
|
||||
"label": "Is Phantom Item",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-11-25 18:13:34.542391",
|
||||
"modified": "2025-11-05 21:15:55.187671",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "BOM Creator Item",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"row_format": "Dynamic",
|
||||
"sort_field": "creation",
|
||||
"sort_order": "DESC",
|
||||
"states": []
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@ class BOMCreatorItem(Document):
|
||||
fg_reference_id: DF.Data | None
|
||||
instruction: DF.SmallText | None
|
||||
is_expandable: DF.Check
|
||||
is_phantom_item: DF.Check
|
||||
is_subcontracted: DF.Check
|
||||
item_code: DF.Link
|
||||
item_group: DF.Link | None
|
||||
|
||||
@@ -42,7 +42,8 @@
|
||||
"original_item",
|
||||
"column_break_33",
|
||||
"sourced_by_supplier",
|
||||
"is_sub_assembly_item"
|
||||
"is_sub_assembly_item",
|
||||
"is_phantom_item"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
@@ -81,6 +82,7 @@
|
||||
"fieldtype": "Link",
|
||||
"in_filter": 1,
|
||||
"label": "BOM No",
|
||||
"mandatory_depends_on": "eval:doc.is_phantom_item",
|
||||
"oldfieldname": "bom_no",
|
||||
"oldfieldtype": "Link",
|
||||
"options": "BOM",
|
||||
@@ -278,6 +280,7 @@
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval:!doc.is_phantom_item",
|
||||
"fieldname": "sourced_by_supplier",
|
||||
"fieldtype": "Check",
|
||||
"label": "Sourced by Supplier"
|
||||
@@ -286,7 +289,8 @@
|
||||
"default": "0",
|
||||
"fieldname": "do_not_explode",
|
||||
"fieldtype": "Check",
|
||||
"label": "Do Not Explode"
|
||||
"label": "Do Not Explode",
|
||||
"read_only_depends_on": "eval:doc.is_phantom_item"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
@@ -304,18 +308,26 @@
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval:!doc.is_phantom_item",
|
||||
"fieldname": "is_sub_assembly_item",
|
||||
"fieldtype": "Check",
|
||||
"label": "Is Sub Assembly Item",
|
||||
"no_copy": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "is_phantom_item",
|
||||
"fieldtype": "Check",
|
||||
"label": "Is Phantom Item",
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2025-08-12 20:01:59.532613",
|
||||
"modified": "2025-11-05 19:00:38.646539",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "BOM Item",
|
||||
|
||||
@@ -25,6 +25,7 @@ class BOMItem(Document):
|
||||
has_variants: DF.Check
|
||||
image: DF.Attach | None
|
||||
include_item_in_manufacturing: DF.Check
|
||||
is_phantom_item: DF.Check
|
||||
is_stock_item: DF.Check
|
||||
is_sub_assembly_item: DF.Check
|
||||
item_code: DF.Link
|
||||
|
||||
@@ -1085,7 +1085,7 @@ class JobCard(Document):
|
||||
|
||||
def set_wip_warehouse(self):
|
||||
if not self.wip_warehouse:
|
||||
self.wip_warehouse = frappe.db.get_single_value("Manufacturing Settings", "default_wip_warehouse")
|
||||
self.wip_warehouse = frappe.get_cached_value("Company", self.company, "default_wip_warehouse")
|
||||
|
||||
def validate_operation_id(self):
|
||||
if (
|
||||
|
||||
@@ -18,18 +18,6 @@ frappe.tour["Manufacturing Settings"] = [
|
||||
"The Stock Entry of type 'Manufacture' is known as backflush. Raw materials being consumed to manufacture finished goods is known as backflushing. <br><br> When creating Manufacture Entry, raw-material items are backflushed based on BOM of production item. If you want raw-material items to be backflushed based on Material Transfer entry made against that Work Order instead, then you can set it under this field."
|
||||
),
|
||||
},
|
||||
{
|
||||
fieldname: "default_wip_warehouse",
|
||||
title: __("Work In Progress Warehouse"),
|
||||
description: __(
|
||||
"This Warehouse will be auto-updated in the Work In Progress Warehouse field of Work Orders."
|
||||
),
|
||||
},
|
||||
{
|
||||
fieldname: "default_fg_warehouse",
|
||||
title: __("Finished Goods Warehouse"),
|
||||
description: __("This Warehouse will be auto-updated in the Target Warehouse field of Work Order."),
|
||||
},
|
||||
{
|
||||
fieldname: "update_bom_costs_automatically",
|
||||
title: __("Update BOM Cost Automatically"),
|
||||
|
||||
@@ -16,11 +16,6 @@
|
||||
"update_bom_costs_automatically",
|
||||
"column_break_lhyt",
|
||||
"allow_editing_of_items_and_quantities_in_work_order",
|
||||
"section_break_6",
|
||||
"default_wip_warehouse",
|
||||
"default_fg_warehouse",
|
||||
"column_break_11",
|
||||
"default_scrap_warehouse",
|
||||
"over_production_for_sales_and_work_order_section",
|
||||
"overproduction_percentage_for_sales_order",
|
||||
"column_break_16",
|
||||
@@ -86,11 +81,6 @@
|
||||
"fieldtype": "Int",
|
||||
"label": "Time Between Operations (Mins)"
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_6",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Default Warehouses for Production"
|
||||
},
|
||||
{
|
||||
"fieldname": "overproduction_percentage_for_sales_order",
|
||||
"fieldtype": "Percent",
|
||||
@@ -122,34 +112,12 @@
|
||||
"fieldtype": "Check",
|
||||
"label": "Update BOM Cost Automatically"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_11",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "default_wip_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"label": "Default Work In Progress Warehouse",
|
||||
"options": "Warehouse"
|
||||
},
|
||||
{
|
||||
"fieldname": "default_fg_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"label": "Default Finished Goods Warehouse",
|
||||
"options": "Warehouse"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "disable_capacity_planning",
|
||||
"fieldtype": "Check",
|
||||
"label": "Disable Capacity Planning"
|
||||
},
|
||||
{
|
||||
"fieldname": "default_scrap_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"label": "Default Scrap Warehouse",
|
||||
"options": "Warehouse"
|
||||
},
|
||||
{
|
||||
"fieldname": "over_production_for_sales_and_work_order_section",
|
||||
"fieldtype": "Section Break",
|
||||
@@ -275,7 +243,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2025-11-07 14:52:56.241459",
|
||||
"modified": "2025-11-13 12:30:29.006822",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Manufacturing Settings",
|
||||
|
||||
@@ -23,9 +23,6 @@ class ManufacturingSettings(Document):
|
||||
allow_production_on_holidays: DF.Check
|
||||
backflush_raw_materials_based_on: DF.Literal["BOM", "Material Transferred for Manufacture"]
|
||||
capacity_planning_for_days: DF.Int
|
||||
default_fg_warehouse: DF.Link | None
|
||||
default_scrap_warehouse: DF.Link | None
|
||||
default_wip_warehouse: DF.Link | None
|
||||
disable_capacity_planning: DF.Check
|
||||
enforce_time_logs: DF.Check
|
||||
get_rm_cost_from_consumption_entry: DF.Check
|
||||
|
||||
@@ -6,6 +6,8 @@ frappe.ui.form.on("Master Production Schedule", {
|
||||
frm.trigger("set_query_filters");
|
||||
|
||||
frm.set_df_property("items", "cannot_add_rows", true);
|
||||
frm.set_df_property("material_requests", "cannot_add_rows", true);
|
||||
frm.set_df_property("sales_orders", "cannot_add_rows", true);
|
||||
frm.fields_dict.items.$wrapper.find("[data-action='duplicate_rows']").css("display", "none");
|
||||
|
||||
frm.trigger("set_custom_buttons");
|
||||
@@ -36,6 +38,14 @@ frappe.ui.form.on("Master Production Schedule", {
|
||||
},
|
||||
};
|
||||
});
|
||||
|
||||
frm.set_query("sales_forecast", (doc) => {
|
||||
return {
|
||||
filters: {
|
||||
company: doc.company,
|
||||
},
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
get_actual_demand(frm) {
|
||||
|
||||
@@ -26,8 +26,8 @@
|
||||
"material_requests",
|
||||
"section_break_xtby",
|
||||
"column_break_yhkr",
|
||||
"column_break_vvys",
|
||||
"get_actual_demand",
|
||||
"column_break_vvys",
|
||||
"section_break_cmgo",
|
||||
"items",
|
||||
"forecast_demand_section",
|
||||
@@ -60,7 +60,6 @@
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"allow_bulk_edit": 1,
|
||||
"fieldname": "items",
|
||||
"fieldtype": "Table",
|
||||
"label": "Items",
|
||||
@@ -189,7 +188,7 @@
|
||||
"grid_page_length": 50,
|
||||
"index_web_pages_for_search": 1,
|
||||
"links": [],
|
||||
"modified": "2025-09-02 19:33:28.244544",
|
||||
"modified": "2025-11-13 19:15:36.090622",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Master Production Schedule",
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
import math
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
from frappe import _, bold
|
||||
from frappe.model.document import Document
|
||||
from frappe.model.mapper import get_mapped_doc
|
||||
from frappe.query_builder.functions import Sum
|
||||
@@ -64,6 +64,22 @@ class MasterProductionSchedule(Document):
|
||||
|
||||
def validate(self):
|
||||
self.set_to_date()
|
||||
self.validate_company()
|
||||
|
||||
def validate_company(self):
|
||||
if self.sales_forecast:
|
||||
sales_forecast_company = frappe.db.get_value("Sales Forecast", self.sales_forecast, "company")
|
||||
if sales_forecast_company != self.company:
|
||||
frappe.throw(
|
||||
_(
|
||||
"The Company {0} of Sales Forecast {1} does not match with the Company {2} of Master Production Schedule {3}."
|
||||
).format(
|
||||
bold(sales_forecast_company),
|
||||
bold(self.sales_forecast),
|
||||
bold(self.company),
|
||||
bold(self.name),
|
||||
)
|
||||
)
|
||||
|
||||
def set_to_date(self):
|
||||
self.to_date = None
|
||||
|
||||
@@ -8,12 +8,16 @@
|
||||
"item_code",
|
||||
"from_warehouse",
|
||||
"warehouse",
|
||||
"item_name",
|
||||
"material_request_type",
|
||||
"column_break_4",
|
||||
"item_name",
|
||||
"uom",
|
||||
"conversion_factor",
|
||||
"section_break_azee",
|
||||
"from_bom",
|
||||
"column_break_scnz",
|
||||
"main_item_code",
|
||||
"section_break_qnpt",
|
||||
"required_bom_qty",
|
||||
"projected_qty",
|
||||
"column_break_wack",
|
||||
@@ -25,6 +29,7 @@
|
||||
"min_order_qty",
|
||||
"section_break_8",
|
||||
"sales_order",
|
||||
"sub_assembly_item_reference",
|
||||
"bin_qty_section",
|
||||
"actual_qty",
|
||||
"requested_qty",
|
||||
@@ -220,12 +225,48 @@
|
||||
"label": "Stock Reserved Qty",
|
||||
"no_copy": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"depends_on": "from_bom",
|
||||
"fieldname": "from_bom",
|
||||
"fieldtype": "Link",
|
||||
"label": "From BOM",
|
||||
"mandatory_depends_on": "eval:parent.reserve_stock",
|
||||
"no_copy": 1,
|
||||
"options": "BOM",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "sub_assembly_item_reference",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"label": "Sub Assembly Item Reference",
|
||||
"no_copy": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "section_break_qnpt",
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"depends_on": "main_item_code",
|
||||
"fieldname": "main_item_code",
|
||||
"fieldtype": "Link",
|
||||
"label": "Main Item Code",
|
||||
"mandatory_depends_on": "eval:parent.reserve_stock",
|
||||
"no_copy": 1,
|
||||
"options": "Item",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_scnz",
|
||||
"fieldtype": "Column Break"
|
||||
}
|
||||
],
|
||||
"grid_page_length": 50,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2025-05-01 14:50:55.805442",
|
||||
"modified": "2025-10-30 17:01:25.996352",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Material Request Plan Item",
|
||||
|
||||
@@ -17,9 +17,11 @@ class MaterialRequestPlanItem(Document):
|
||||
actual_qty: DF.Float
|
||||
conversion_factor: DF.Float
|
||||
description: DF.TextEditor | None
|
||||
from_bom: DF.Link | None
|
||||
from_warehouse: DF.Link | None
|
||||
item_code: DF.Link
|
||||
item_name: DF.Data | None
|
||||
main_item_code: DF.Link | None
|
||||
material_request_type: DF.Literal[
|
||||
"",
|
||||
"Purchase",
|
||||
@@ -43,6 +45,7 @@ class MaterialRequestPlanItem(Document):
|
||||
sales_order: DF.Link | None
|
||||
schedule_date: DF.Date | None
|
||||
stock_reserved_qty: DF.Float
|
||||
sub_assembly_item_reference: DF.Data | None
|
||||
uom: DF.Link | None
|
||||
warehouse: DF.Link
|
||||
# end: auto-generated types
|
||||
|
||||
@@ -568,6 +568,7 @@ class ProductionPlan(Document):
|
||||
def on_submit(self):
|
||||
self.update_bin_qty()
|
||||
self.update_sales_order()
|
||||
self.add_reference_to_raw_materials()
|
||||
self.update_stock_reservation()
|
||||
|
||||
def on_cancel(self):
|
||||
@@ -583,6 +584,24 @@ class ProductionPlan(Document):
|
||||
|
||||
make_stock_reservation_entries(self)
|
||||
|
||||
def add_reference_to_raw_materials(self):
|
||||
for item in self.mr_items:
|
||||
if reference := next(
|
||||
(
|
||||
sa_item.name
|
||||
for sa_item in self.sub_assembly_items
|
||||
if sa_item.production_item == item.main_item_code and sa_item.bom_no == item.from_bom
|
||||
),
|
||||
None,
|
||||
):
|
||||
item.db_set("sub_assembly_item_reference", reference)
|
||||
elif self.reserve_stock and item.main_item_code and item.from_bom:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Sub assembly item references are missing. Please fetch the sub assemblies and raw materials again."
|
||||
)
|
||||
)
|
||||
|
||||
def update_sales_order(self):
|
||||
sales_orders = [row.sales_order for row in self.po_items if row.sales_order]
|
||||
if sales_orders:
|
||||
@@ -737,7 +756,7 @@ class ProductionPlan(Document):
|
||||
|
||||
wo_list, po_list = [], []
|
||||
subcontracted_po = {}
|
||||
default_warehouses = get_default_warehouse()
|
||||
default_warehouses = get_default_warehouse(self.company)
|
||||
|
||||
self.make_work_order_for_finished_goods(wo_list, default_warehouses)
|
||||
self.make_work_order_for_subassembly_items(wo_list, subcontracted_po, default_warehouses)
|
||||
@@ -1335,14 +1354,19 @@ def get_subitems(
|
||||
item.purchase_uom,
|
||||
item_uom.conversion_factor,
|
||||
bom.item.as_("main_bom_item"),
|
||||
bom_item.is_phantom_item,
|
||||
)
|
||||
.where(
|
||||
(bom.name == bom_no)
|
||||
& (bom_item.is_sub_assembly_item == 0)
|
||||
& (bom_item.docstatus < 2)
|
||||
& (item.is_stock_item.isin([0, 1]) if include_non_stock_items else item.is_stock_item == 1)
|
||||
& (
|
||||
(item.is_stock_item.isin([0, 1]) if include_non_stock_items else item.is_stock_item == 1)
|
||||
| (bom_item.is_phantom_item == 1)
|
||||
)
|
||||
)
|
||||
.groupby(bom_item.item_code)
|
||||
.orderby(bom_item.idx)
|
||||
).run(as_dict=True)
|
||||
|
||||
for d in items:
|
||||
@@ -1355,10 +1379,12 @@ def get_subitems(
|
||||
|
||||
item_details[d.item_code] = d
|
||||
|
||||
if data.get("include_exploded_items") and d.default_bom:
|
||||
if d.is_phantom_item or (data.get("include_exploded_items") and d.default_bom):
|
||||
if (
|
||||
d.default_material_request_type in ["Manufacture", "Purchase"] and not d.is_sub_contracted
|
||||
) or (d.is_sub_contracted and include_subcontracted_items):
|
||||
(d.default_material_request_type in ["Manufacture", "Purchase"] and not d.is_sub_contracted)
|
||||
or (d.is_sub_contracted and include_subcontracted_items)
|
||||
or d.is_phantom_item
|
||||
):
|
||||
if d.qty > 0:
|
||||
get_subitems(
|
||||
doc,
|
||||
@@ -1370,7 +1396,7 @@ def get_subitems(
|
||||
include_subcontracted_items,
|
||||
d.qty,
|
||||
)
|
||||
return item_details
|
||||
return {key: value for key, value in item_details.items() if not value.get("is_phantom_item")}
|
||||
|
||||
|
||||
def get_material_request_items(
|
||||
@@ -1382,14 +1408,14 @@ def get_material_request_items(
|
||||
include_safety_stock,
|
||||
warehouse,
|
||||
bin_dict,
|
||||
total_qty,
|
||||
):
|
||||
total_qty = row["qty"]
|
||||
|
||||
required_qty = 0
|
||||
if not ignore_existing_ordered_qty or bin_dict.get("projected_qty", 0) < 0:
|
||||
required_qty = total_qty
|
||||
elif total_qty > bin_dict.get("projected_qty", 0):
|
||||
required_qty = total_qty - bin_dict.get("projected_qty", 0)
|
||||
required_qty = total_qty[row.get("item_code")]
|
||||
elif total_qty[row.get("item_code")] > bin_dict.get("projected_qty", 0):
|
||||
required_qty = total_qty[row.get("item_code")] - bin_dict.get("projected_qty", 0)
|
||||
total_qty[row.get("item_code")] -= required_qty
|
||||
|
||||
if doc.get("consider_minimum_order_qty") and required_qty > 0 and required_qty < row["min_order_qty"]:
|
||||
required_qty = row["min_order_qty"]
|
||||
@@ -1432,7 +1458,7 @@ def get_material_request_items(
|
||||
"item_name": row.item_name,
|
||||
"quantity": required_qty / conversion_factor,
|
||||
"conversion_factor": conversion_factor,
|
||||
"required_bom_qty": total_qty,
|
||||
"required_bom_qty": row.get("qty"),
|
||||
"stock_uom": row.get("stock_uom"),
|
||||
"warehouse": warehouse
|
||||
or row.get("source_warehouse")
|
||||
@@ -1448,7 +1474,8 @@ def get_material_request_items(
|
||||
"sales_order": sales_order,
|
||||
"description": row.get("description"),
|
||||
"uom": row.get("purchase_uom") or row.get("stock_uom"),
|
||||
"main_bom_item": row.get("main_bom_item"),
|
||||
"main_item_code": row.get("main_bom_item"),
|
||||
"from_bom": row.get("main_bom"),
|
||||
}
|
||||
|
||||
|
||||
@@ -1629,7 +1656,27 @@ def get_items_for_material_requests(doc, warehouses=None, get_parent_warehouse_d
|
||||
sub_assembly_items = defaultdict(int)
|
||||
if doc.get("skip_available_sub_assembly_item") and doc.get("sub_assembly_items"):
|
||||
for d in doc.get("sub_assembly_items"):
|
||||
sub_assembly_items[(d.get("production_item"), d.get("bom_no"))] += d.get("qty")
|
||||
sub_assembly_items[
|
||||
(d.get("production_item"), d.get("bom_no"), d.get("type_of_manufacturing"))
|
||||
] += d.get("qty")
|
||||
sub_assembly_items = {k[:2]: v for k, v in sub_assembly_items.items()}
|
||||
|
||||
data = []
|
||||
for row in doc.get("po_items"):
|
||||
get_sub_assembly_items(
|
||||
[],
|
||||
frappe._dict(),
|
||||
row.get("bom_no"),
|
||||
data,
|
||||
row.get("planned_qty"),
|
||||
doc.get("company"),
|
||||
warehouse=doc.get("sub_assembly_warehouse"),
|
||||
skip_available_sub_assembly_item=doc.get("skip_available_sub_assembly_item"),
|
||||
fetch_phantom_items=True,
|
||||
)
|
||||
|
||||
for d in data:
|
||||
sub_assembly_items[(d.get("production_item"), d.get("bom_no"))] += d.get("stock_qty")
|
||||
|
||||
for data in po_items:
|
||||
if not data.get("include_exploded_items") and doc.get("sub_assembly_items"):
|
||||
@@ -1668,7 +1715,6 @@ def get_items_for_material_requests(doc, warehouses=None, get_parent_warehouse_d
|
||||
sub_assembly_items,
|
||||
planned_qty=planned_qty,
|
||||
)
|
||||
|
||||
elif data.get("include_exploded_items") and include_subcontracted_items:
|
||||
# fetch exploded items from BOM
|
||||
item_details = get_exploded_items(
|
||||
@@ -1698,7 +1744,7 @@ def get_items_for_material_requests(doc, warehouses=None, get_parent_warehouse_d
|
||||
get_uom_conversion_factor(item_master.name, purchase_uom) if item_master.purchase_uom else 1.0
|
||||
)
|
||||
|
||||
item_details[item_master.name] = frappe._dict(
|
||||
item_details[item_master.item_code] = frappe._dict(
|
||||
{
|
||||
"item_name": item_master.item_name,
|
||||
"default_bom": doc.bom,
|
||||
@@ -1707,7 +1753,7 @@ def get_items_for_material_requests(doc, warehouses=None, get_parent_warehouse_d
|
||||
"min_order_qty": item_master.min_order_qty,
|
||||
"default_material_request_type": item_master.default_material_request_type,
|
||||
"qty": planned_qty or 1,
|
||||
"is_sub_contracted": item_master.is_subcontracted_item,
|
||||
"is_sub_contracted": item_master.is_sub_contracted_item,
|
||||
"item_code": item_master.name,
|
||||
"description": item_master.description,
|
||||
"stock_uom": item_master.stock_uom,
|
||||
@@ -1718,19 +1764,21 @@ def get_items_for_material_requests(doc, warehouses=None, get_parent_warehouse_d
|
||||
|
||||
sales_order = data.get("sales_order")
|
||||
|
||||
for item_code, details in item_details.items():
|
||||
for key, details in item_details.items():
|
||||
so_item_details.setdefault(sales_order, frappe._dict())
|
||||
if item_code in so_item_details.get(sales_order, {}):
|
||||
so_item_details[sales_order][item_code]["qty"] = so_item_details[sales_order][item_code].get(
|
||||
if key in so_item_details.get(sales_order, {}):
|
||||
so_item_details[sales_order][key]["qty"] = so_item_details[sales_order][key].get(
|
||||
"qty", 0
|
||||
) + flt(details.qty)
|
||||
else:
|
||||
so_item_details[sales_order][item_code] = details
|
||||
so_item_details[sales_order][key] = details
|
||||
|
||||
mr_items = []
|
||||
for sales_order in so_item_details:
|
||||
item_dict = so_item_details[sales_order]
|
||||
total_qty = defaultdict(float)
|
||||
for details in item_dict.values():
|
||||
total_qty[details.item_code] += flt(details.qty)
|
||||
bin_dict = get_bin_details(details, doc.company, warehouse)
|
||||
bin_dict = bin_dict[0] if bin_dict else {}
|
||||
|
||||
@@ -1744,6 +1792,7 @@ def get_items_for_material_requests(doc, warehouses=None, get_parent_warehouse_d
|
||||
include_safety_stock,
|
||||
warehouse,
|
||||
bin_dict,
|
||||
total_qty,
|
||||
)
|
||||
if items:
|
||||
mr_items.append(items)
|
||||
@@ -1847,8 +1896,9 @@ def get_sub_assembly_items(
|
||||
warehouse=None,
|
||||
indent=0,
|
||||
skip_available_sub_assembly_item=False,
|
||||
fetch_phantom_items=False,
|
||||
):
|
||||
data = get_bom_children(parent=bom_no)
|
||||
data = get_bom_children(parent=bom_no, return_all=False, fetch_phantom_items=fetch_phantom_items)
|
||||
for d in data:
|
||||
if d.expandable:
|
||||
parent_item_code = frappe.get_cached_value("BOM", bom_no, "item")
|
||||
@@ -1892,6 +1942,7 @@ def get_sub_assembly_items(
|
||||
"projected_qty": bin_details[d.item_code][0].get("projected_qty", 0)
|
||||
if bin_details.get(d.item_code)
|
||||
else 0,
|
||||
"main_bom": bom_no,
|
||||
}
|
||||
)
|
||||
)
|
||||
@@ -1907,6 +1958,7 @@ def get_sub_assembly_items(
|
||||
warehouse,
|
||||
indent=indent + 1,
|
||||
skip_available_sub_assembly_item=skip_available_sub_assembly_item,
|
||||
fetch_phantom_items=fetch_phantom_items,
|
||||
)
|
||||
|
||||
|
||||
@@ -1998,7 +2050,7 @@ def get_raw_materials_of_sub_assembly_items(
|
||||
item_default = frappe.qb.DocType("Item Default")
|
||||
item_uom = frappe.qb.DocType("UOM Conversion Detail")
|
||||
|
||||
items = (
|
||||
query = (
|
||||
frappe.qb.from_(bei)
|
||||
.join(bom)
|
||||
.on(bom.name == bei.parent)
|
||||
@@ -2014,6 +2066,7 @@ def get_raw_materials_of_sub_assembly_items(
|
||||
item.name.as_("item_code"),
|
||||
bei.description,
|
||||
bei.stock_uom,
|
||||
bei.is_phantom_item,
|
||||
bei.bom_no,
|
||||
item.min_order_qty,
|
||||
bei.source_warehouse,
|
||||
@@ -2024,19 +2077,28 @@ def get_raw_materials_of_sub_assembly_items(
|
||||
item_uom.conversion_factor,
|
||||
item.safety_stock,
|
||||
bom.item.as_("main_bom_item"),
|
||||
bom.name.as_("main_bom"),
|
||||
)
|
||||
.where(
|
||||
(bei.docstatus == 1)
|
||||
& (bei.is_sub_assembly_item == 0)
|
||||
& (bom.name == bom_no)
|
||||
& (item.is_stock_item.isin([0, 1]) if include_non_stock_items else item.is_stock_item == 1)
|
||||
& (
|
||||
(item.is_stock_item.isin([0, 1]) if include_non_stock_items else item.is_stock_item == 1)
|
||||
| (bei.is_phantom_item == 1)
|
||||
)
|
||||
)
|
||||
.groupby(bei.item_code, bei.stock_uom)
|
||||
).run(as_dict=True)
|
||||
)
|
||||
|
||||
for item in items:
|
||||
for item in query.run(as_dict=True):
|
||||
key = (item.item_code, item.bom_no)
|
||||
if (item.bom_no and key not in sub_assembly_items) or (item.item_code in existing_sub_assembly_items):
|
||||
if item.is_phantom_item:
|
||||
sub_assembly_items[key] += item.get("qty")
|
||||
|
||||
if (item.bom_no and key not in sub_assembly_items) or (
|
||||
(item.item_code, item.bom_no or item.main_bom) in existing_sub_assembly_items
|
||||
):
|
||||
continue
|
||||
|
||||
if item.bom_no:
|
||||
@@ -2050,15 +2112,15 @@ def get_raw_materials_of_sub_assembly_items(
|
||||
sub_assembly_items,
|
||||
planned_qty=planned_qty,
|
||||
)
|
||||
existing_sub_assembly_items.add(item.item_code)
|
||||
existing_sub_assembly_items.add((item.item_code, item.bom_no or item.main_bom))
|
||||
else:
|
||||
if not item.conversion_factor and item.purchase_uom:
|
||||
item.conversion_factor = get_uom_conversion_factor(item.item_code, item.purchase_uom)
|
||||
|
||||
if details := item_details.get(item.get("item_code")):
|
||||
if details := item_details.get((item.get("item_code"), item.get("main_bom"))):
|
||||
details.qty += item.get("qty")
|
||||
else:
|
||||
item_details.setdefault(item.get("item_code"), item)
|
||||
item_details.setdefault((item.get("item_code"), item.get("main_bom")), item)
|
||||
|
||||
return item_details
|
||||
|
||||
|
||||
@@ -1944,11 +1944,17 @@ class TestProductionPlan(IntegrationTestCase):
|
||||
|
||||
mr_items = get_items_for_material_requests(plan.as_dict())
|
||||
|
||||
from collections import defaultdict
|
||||
|
||||
mr_items_dict = defaultdict(float)
|
||||
for item in mr_items:
|
||||
mr_items_dict[item.get("item_code")] += item.get("quantity")
|
||||
|
||||
# RM Item 1 (FG1 (100 + 100) + FG2 (50) + FG3 (10) - 90 in stock - 80 sub assembly stock)
|
||||
self.assertEqual(mr_items[0].get("quantity"), 90)
|
||||
self.assertEqual(mr_items_dict["RM Item 1"], 90)
|
||||
|
||||
# RM Item 2 (FG1 (100) + FG2 (50) + FG4 (10) - 80 sub assembly stock)
|
||||
self.assertEqual(mr_items[1].get("quantity"), 80)
|
||||
self.assertEqual(mr_items_dict["RM Item 2"], 80)
|
||||
|
||||
def test_stock_reservation_against_production_plan(self):
|
||||
from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt
|
||||
@@ -2362,11 +2368,7 @@ class TestProductionPlan(IntegrationTestCase):
|
||||
frappe.db.set_single_value("Stock Settings", "enable_stock_reservation", 0)
|
||||
|
||||
def test_production_plan_for_partial_sub_assembly_items(self):
|
||||
from erpnext.controllers.status_updater import OverAllowanceError
|
||||
from erpnext.manufacturing.doctype.bom.test_bom import create_nested_bom
|
||||
from erpnext.subcontracting.doctype.subcontracting_bom.test_subcontracting_bom import (
|
||||
create_subcontracting_bom,
|
||||
)
|
||||
|
||||
frappe.flags.test_print = False
|
||||
|
||||
@@ -2418,6 +2420,30 @@ class TestProductionPlan(IntegrationTestCase):
|
||||
for row in plan.sub_assembly_items:
|
||||
self.assertEqual(row.ordered_qty, 10.0)
|
||||
|
||||
def test_phantom_bom_explosion(self):
|
||||
from erpnext.manufacturing.doctype.bom.test_bom import create_tree_for_phantom_bom_tests
|
||||
|
||||
create_tree_for_phantom_bom_tests()
|
||||
|
||||
plan = create_production_plan(
|
||||
item_code="Top Level Parent",
|
||||
planned_qty=10,
|
||||
use_multi_level_bom=0,
|
||||
do_not_submit=True,
|
||||
company="_Test Company",
|
||||
skip_getting_mr_items=True,
|
||||
)
|
||||
plan.get_sub_assembly_items()
|
||||
plan.submit()
|
||||
|
||||
plan.set("mr_items", [])
|
||||
mr_items = get_items_for_material_requests(plan.as_dict())
|
||||
for d in mr_items:
|
||||
plan.append("mr_items", d)
|
||||
|
||||
self.assertEqual(plan.sub_assembly_items[0].production_item, "Sub Assembly Level 1-1")
|
||||
self.assertEqual([item.item_code for item in plan.mr_items], ["Item Level 1-3", "Item Level 2-3"])
|
||||
|
||||
|
||||
def create_production_plan(**args):
|
||||
"""
|
||||
@@ -2440,6 +2466,7 @@ def create_production_plan(**args):
|
||||
"skip_available_sub_assembly_item": args.skip_available_sub_assembly_item or 0,
|
||||
"sub_assembly_warehouse": args.sub_assembly_warehouse,
|
||||
"reserve_stock": args.reserve_stock or 0,
|
||||
"for_warehouse": args.for_warehouse or None,
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
@@ -79,13 +79,14 @@
|
||||
"fieldname": "received_qty",
|
||||
"fieldtype": "Float",
|
||||
"label": "Received Qty",
|
||||
"no_copy": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "bom_no",
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Bom No",
|
||||
"label": "BOM No",
|
||||
"options": "BOM"
|
||||
},
|
||||
{
|
||||
@@ -245,7 +246,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2025-06-10 13:36:24.759101",
|
||||
"modified": "2025-11-03 14:33:50.677717",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Production Plan Sub Assembly Item",
|
||||
|
||||
@@ -2,6 +2,16 @@
|
||||
// For license information, please see license.txt
|
||||
|
||||
frappe.ui.form.on("Routing", {
|
||||
setup: function (frm) {
|
||||
frm.set_query("bom_no", "operations", function () {
|
||||
return {
|
||||
filters: {
|
||||
is_phantom_bom: 0,
|
||||
},
|
||||
};
|
||||
});
|
||||
},
|
||||
|
||||
refresh: function (frm) {
|
||||
frm.trigger("display_sequence_id_column");
|
||||
},
|
||||
|
||||
@@ -3270,6 +3270,14 @@ class TestWorkOrder(IntegrationTestCase):
|
||||
)
|
||||
frappe.db.set_single_value("Stock Settings", "auto_reserve_serial_and_batch", original_auto_reserve)
|
||||
|
||||
def test_phantom_bom_explosion(self):
|
||||
from erpnext.manufacturing.doctype.bom.test_bom import create_tree_for_phantom_bom_tests
|
||||
|
||||
expected = create_tree_for_phantom_bom_tests()
|
||||
|
||||
wo = make_wo_order_test_record(item="Top Level Parent")
|
||||
self.assertEqual([item.item_code for item in wo.required_items], expected)
|
||||
|
||||
|
||||
def get_reserved_entries(voucher_no, warehouse=None):
|
||||
doctype = frappe.qb.DocType("Stock Reservation Entry")
|
||||
|
||||
@@ -932,6 +932,9 @@ erpnext.work_order = {
|
||||
if (!(frm.doc.wip_warehouse || frm.doc.fg_warehouse)) {
|
||||
frappe.call({
|
||||
method: "erpnext.manufacturing.doctype.work_order.work_order.get_default_warehouse",
|
||||
args: {
|
||||
company: frm.doc.company,
|
||||
},
|
||||
callback: function (r) {
|
||||
if (!r.exe) {
|
||||
frm.set_value("wip_warehouse", r.message.wip_warehouse);
|
||||
|
||||
@@ -453,9 +453,9 @@ class WorkOrder(Document):
|
||||
|
||||
def set_default_warehouse(self):
|
||||
if not self.wip_warehouse and not self.skip_transfer:
|
||||
self.wip_warehouse = frappe.db.get_single_value("Manufacturing Settings", "default_wip_warehouse")
|
||||
self.wip_warehouse = frappe.get_cached_value("Company", self.company, "default_wip_warehouse")
|
||||
if not self.fg_warehouse:
|
||||
self.fg_warehouse = frappe.db.get_single_value("Manufacturing Settings", "default_fg_warehouse")
|
||||
self.fg_warehouse = frappe.get_cached_value("Company", self.company, "default_fg_warehouse")
|
||||
|
||||
def check_wip_warehouse_skip(self):
|
||||
if self.skip_transfer and not self.from_wip_warehouse:
|
||||
@@ -2318,13 +2318,14 @@ def make_stock_entry(
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def get_default_warehouse():
|
||||
doc = frappe.get_cached_doc("Manufacturing Settings")
|
||||
|
||||
def get_default_warehouse(company):
|
||||
wip, fg, scrap = frappe.get_cached_value(
|
||||
"Company", company, ["default_wip_warehouse", "default_fg_warehouse", "default_scrap_warehouse"]
|
||||
)
|
||||
return {
|
||||
"wip_warehouse": doc.default_wip_warehouse,
|
||||
"fg_warehouse": doc.default_fg_warehouse,
|
||||
"scrap_warehouse": doc.default_scrap_warehouse,
|
||||
"wip_warehouse": wip,
|
||||
"fg_warehouse": fg,
|
||||
"scrap_warehouse": scrap,
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -21,7 +21,17 @@ def get_exploded_items(bom, data, indent=0, qty=1):
|
||||
exploded_items = frappe.get_all(
|
||||
"BOM Item",
|
||||
filters={"parent": bom},
|
||||
fields=["qty", "bom_no", "qty", "item_code", "item_name", "description", "uom", "idx"],
|
||||
fields=[
|
||||
"qty",
|
||||
"bom_no",
|
||||
"qty",
|
||||
"item_code",
|
||||
"item_name",
|
||||
"description",
|
||||
"uom",
|
||||
"idx",
|
||||
"is_phantom_item",
|
||||
],
|
||||
order_by="idx ASC",
|
||||
)
|
||||
|
||||
@@ -37,6 +47,7 @@ def get_exploded_items(bom, data, indent=0, qty=1):
|
||||
"qty": item.qty * qty,
|
||||
"uom": item.uom,
|
||||
"description": item.description,
|
||||
"is_phantom_item": item.is_phantom_item,
|
||||
}
|
||||
)
|
||||
if item.bom_no:
|
||||
@@ -54,6 +65,7 @@ def get_columns():
|
||||
},
|
||||
{"label": _("Item Name"), "fieldtype": "data", "fieldname": "item_name", "width": 100},
|
||||
{"label": _("BOM"), "fieldtype": "Link", "fieldname": "bom", "width": 150, "options": "BOM"},
|
||||
{"label": _("Is Phantom Item"), "fieldtype": "Check", "fieldname": "is_phantom_item"},
|
||||
{"label": _("Qty"), "fieldtype": "data", "fieldname": "qty", "width": 100},
|
||||
{"label": _("UOM"), "fieldtype": "data", "fieldname": "uom", "width": 100},
|
||||
{"label": _("BOM Level"), "fieldtype": "Int", "fieldname": "bom_level", "width": 100},
|
||||
|
||||
@@ -32,6 +32,7 @@ def get_report_data(last_purchase_rate, required_qty, row, manufacture_details):
|
||||
return [
|
||||
row.item_code,
|
||||
row.description,
|
||||
row.from_bom_no,
|
||||
comma_and(manufacture_details.get(row.item_code, {}).get("manufacturer", []), add_quotes=False),
|
||||
comma_and(manufacture_details.get(row.item_code, {}).get("manufacturer_part", []), add_quotes=False),
|
||||
qty_per_unit,
|
||||
@@ -57,6 +58,13 @@ def get_columns():
|
||||
"fieldtype": "Data",
|
||||
"width": 150,
|
||||
},
|
||||
{
|
||||
"fieldname": "from_bom_no",
|
||||
"label": _("From BOM No"),
|
||||
"fieldtype": "Link",
|
||||
"options": "BOM",
|
||||
"width": 150,
|
||||
},
|
||||
{
|
||||
"fieldname": "manufacturer",
|
||||
"label": _("Manufacturer"),
|
||||
@@ -103,10 +111,7 @@ def get_columns():
|
||||
|
||||
|
||||
def get_bom_data(filters):
|
||||
if filters.get("show_exploded_view"):
|
||||
bom_item_table = "BOM Explosion Item"
|
||||
else:
|
||||
bom_item_table = "BOM Item"
|
||||
bom_item_table = "BOM Explosion Item" if filters.get("show_exploded_view") else "BOM Item"
|
||||
|
||||
bom_item = frappe.qb.DocType(bom_item_table)
|
||||
bin = frappe.qb.DocType("Bin")
|
||||
@@ -118,11 +123,13 @@ def get_bom_data(filters):
|
||||
.select(
|
||||
bom_item.item_code,
|
||||
bom_item.description,
|
||||
bom_item.parent.as_("from_bom_no"),
|
||||
bom_item.qty_consumed_per_unit.as_("qty_per_unit"),
|
||||
IfNull(Sum(bin.actual_qty), 0).as_("actual_qty"),
|
||||
)
|
||||
.where((bom_item.parent == filters.get("bom")) & (bom_item.parenttype == "BOM"))
|
||||
.groupby(bom_item.item_code)
|
||||
.orderby(bom_item.idx)
|
||||
)
|
||||
|
||||
if filters.get("warehouse"):
|
||||
@@ -146,7 +153,36 @@ def get_bom_data(filters):
|
||||
else:
|
||||
query = query.where(bin.warehouse == filters.get("warehouse"))
|
||||
|
||||
return query.run(as_dict=True)
|
||||
if bom_item_table == "BOM Item":
|
||||
query = query.select(bom_item.bom_no, bom_item.is_phantom_item)
|
||||
|
||||
data = query.run(as_dict=True)
|
||||
return explode_phantom_boms(data, filters) if bom_item_table == "BOM Item" else data
|
||||
|
||||
|
||||
def explode_phantom_boms(data, filters):
|
||||
original_bom = filters.get("bom")
|
||||
replacements = []
|
||||
|
||||
for idx, item in enumerate(data):
|
||||
if not item.is_phantom_item:
|
||||
continue
|
||||
|
||||
filters["bom"] = item.bom_no
|
||||
children = get_bom_data(filters)
|
||||
filters["bom"] = original_bom
|
||||
|
||||
for child in children:
|
||||
child.qty_per_unit = (child.qty_per_unit or 0) * (item.qty_per_unit or 0)
|
||||
|
||||
replacements.append((idx, children))
|
||||
|
||||
for idx, children in reversed(replacements):
|
||||
data.pop(idx)
|
||||
data[idx:idx] = children
|
||||
|
||||
filters["bom"] = original_bom
|
||||
return data
|
||||
|
||||
|
||||
def get_manufacturer_records():
|
||||
|
||||
@@ -102,6 +102,7 @@ def get_expected_data(bom, qty_to_make):
|
||||
[
|
||||
bom.items[idx].item_code,
|
||||
bom.items[idx].item_code,
|
||||
bom.name,
|
||||
"",
|
||||
"",
|
||||
float(bom.items[idx].stock_qty / bom.quantity),
|
||||
|
||||
@@ -22,8 +22,9 @@ def get_columns():
|
||||
_("Item") + ":Link/Item:150",
|
||||
_("Item Name") + "::240",
|
||||
_("Description") + "::300",
|
||||
_("From BOM No") + "::200",
|
||||
_("BOM Qty") + ":Float:160",
|
||||
_("BOM UoM") + "::160",
|
||||
_("BOM UOM") + "::160",
|
||||
_("Required Qty") + ":Float:120",
|
||||
_("In Stock Qty") + ":Float:120",
|
||||
_("Enough Parts to Build") + ":Float:200",
|
||||
@@ -72,6 +73,7 @@ def get_bom_stock(filters):
|
||||
BOM_ITEM.item_code,
|
||||
BOM_ITEM.item_name,
|
||||
BOM_ITEM.description,
|
||||
BOM.name,
|
||||
Sum(BOM_ITEM.stock_qty),
|
||||
BOM_ITEM.stock_uom,
|
||||
(Sum(BOM_ITEM.stock_qty) * qty_to_produce) / BOM.quantity,
|
||||
@@ -80,6 +82,25 @@ def get_bom_stock(filters):
|
||||
)
|
||||
.where((BOM_ITEM.parent == filters.get("bom")) & (BOM_ITEM.parenttype == "BOM"))
|
||||
.groupby(BOM_ITEM.item_code)
|
||||
.orderby(BOM_ITEM.idx)
|
||||
)
|
||||
|
||||
return QUERY.run()
|
||||
if bom_item_table == "BOM Item":
|
||||
QUERY = QUERY.select(BOM_ITEM.bom_no, BOM_ITEM.is_phantom_item)
|
||||
|
||||
data = QUERY.run(as_list=True)
|
||||
return explode_phantom_boms(data, filters) if bom_item_table == "BOM Item" else data
|
||||
|
||||
|
||||
def explode_phantom_boms(data, filters):
|
||||
expanded = []
|
||||
for row in data:
|
||||
if row[-1]: # last element is `is_phantom_item`
|
||||
phantom_filters = filters.copy()
|
||||
phantom_filters["qty_to_produce"] = row[-5]
|
||||
phantom_filters["bom"] = row[-2]
|
||||
expanded.extend(get_bom_stock(phantom_filters))
|
||||
else:
|
||||
expanded.append(row)
|
||||
|
||||
return expanded
|
||||
|
||||
@@ -96,6 +96,7 @@ def get_expected_data(bom, warehouse, qty_to_produce, show_exploded_view=False):
|
||||
item.item_code,
|
||||
item.item_name,
|
||||
item.description,
|
||||
bom.name,
|
||||
item.stock_qty,
|
||||
item.stock_uom,
|
||||
item.stock_qty * qty_to_produce / bom.quantity,
|
||||
@@ -103,6 +104,8 @@ def get_expected_data(bom, warehouse, qty_to_produce, show_exploded_view=False):
|
||||
floor(in_stock_qty / (item.stock_qty * qty_to_produce / bom.quantity))
|
||||
if in_stock_qty
|
||||
else None,
|
||||
item.bom_no,
|
||||
item.is_phantom_item,
|
||||
]
|
||||
)
|
||||
|
||||
|
||||
@@ -446,3 +446,5 @@ erpnext.patches.v16_0.add_new_stock_entry_types
|
||||
erpnext.patches.v15_0.set_asset_status_if_not_already_set
|
||||
erpnext.patches.v15_0.toggle_legacy_controller_for_period_closing
|
||||
erpnext.patches.v16_0.update_serial_batch_entries
|
||||
erpnext.patches.v16_0.set_company_wise_warehouses
|
||||
erpnext.patches.v16_0.set_valuation_method_on_companies
|
||||
|
||||
14
erpnext/patches/v16_0/set_company_wise_warehouses.py
Normal file
14
erpnext/patches/v16_0/set_company_wise_warehouses.py
Normal file
@@ -0,0 +1,14 @@
|
||||
import frappe
|
||||
|
||||
|
||||
def execute():
|
||||
warehouses = frappe.get_single_value(
|
||||
"Manufacturing Settings",
|
||||
["default_wip_warehouse", "default_fg_warehouse", "default_scrap_warehouse"],
|
||||
as_dict=True,
|
||||
)
|
||||
|
||||
for name, warehouse in warehouses.items():
|
||||
if warehouse:
|
||||
company = frappe.get_value("Warehouse", warehouse, "company")
|
||||
frappe.db.set_value("Company", company, name, warehouse)
|
||||
@@ -0,0 +1,7 @@
|
||||
import frappe
|
||||
|
||||
|
||||
def execute():
|
||||
valuation_method = frappe.get_single_value("Stock Settings", "valuation_method")
|
||||
for company in frappe.get_all("Company", pluck="name"):
|
||||
frappe.db.set_value("Company", company, "valuation_method", valuation_method)
|
||||
@@ -140,6 +140,17 @@ class BOMConfigurator {
|
||||
},
|
||||
btnClass: "hidden-xs",
|
||||
},
|
||||
{
|
||||
label: __(frappe.utils.icon("add", "sm") + " Phantom Item"),
|
||||
click: function (node) {
|
||||
let view = frappe.views.trees["BOM Configurator"];
|
||||
view.events.add_sub_assembly(node, view, true);
|
||||
},
|
||||
condition: function (node) {
|
||||
return node.expandable;
|
||||
},
|
||||
btnClass: "hidden-xs",
|
||||
},
|
||||
{
|
||||
label: __("Collapse All"),
|
||||
click: function (node) {
|
||||
@@ -170,6 +181,17 @@ class BOMConfigurator {
|
||||
},
|
||||
btnClass: "hidden-xs",
|
||||
},
|
||||
{
|
||||
label: __(frappe.utils.icon("move", "sm") + " Phantom Item"),
|
||||
click: function (node) {
|
||||
let view = frappe.views.trees["BOM Configurator"];
|
||||
view.events.convert_to_sub_assembly(node, view, true);
|
||||
},
|
||||
condition: function (node) {
|
||||
return !node.expandable;
|
||||
},
|
||||
btnClass: "hidden-xs",
|
||||
},
|
||||
{
|
||||
label: __(frappe.utils.icon("delete", "sm") + " Item"),
|
||||
click: function (node) {
|
||||
@@ -253,10 +275,10 @@ class BOMConfigurator {
|
||||
}
|
||||
}
|
||||
|
||||
add_sub_assembly(node, view) {
|
||||
add_sub_assembly(node, view, phantom = false) {
|
||||
let dialog = new frappe.ui.Dialog({
|
||||
fields: view.events.get_sub_assembly_modal_fields(view, node.is_root),
|
||||
title: __("Add Sub Assembly"),
|
||||
fields: view.events.get_sub_assembly_modal_fields(view, node.is_root, false, phantom),
|
||||
title: phantom ? __("Add Phantom Item") : __("Add Sub Assembly"),
|
||||
});
|
||||
view.events.set_query_for_workstation(dialog);
|
||||
|
||||
@@ -282,6 +304,7 @@ class BOMConfigurator {
|
||||
operation: node.data.operation,
|
||||
workstation_type: node.data.workstation_type,
|
||||
operation_time: node.data.operation_time,
|
||||
phantom: phantom,
|
||||
},
|
||||
callback: (r) => {
|
||||
view.events.load_tree(r, node);
|
||||
@@ -292,15 +315,18 @@ class BOMConfigurator {
|
||||
});
|
||||
}
|
||||
|
||||
get_sub_assembly_modal_fields(view, is_root = false, read_only = false) {
|
||||
get_sub_assembly_modal_fields(view, is_root = false, read_only = false, phantom = false) {
|
||||
let fields = [
|
||||
{
|
||||
label: __("Sub Assembly Item"),
|
||||
label: phantom ? __("Phantom Item") : __("Sub Assembly Item"),
|
||||
fieldname: "item_code",
|
||||
fieldtype: "Link",
|
||||
options: "Item",
|
||||
reqd: 1,
|
||||
read_only: read_only,
|
||||
filters: {
|
||||
is_stock_item: !phantom,
|
||||
},
|
||||
},
|
||||
{ fieldtype: "Column Break" },
|
||||
{
|
||||
@@ -320,7 +346,7 @@ class BOMConfigurator {
|
||||
},
|
||||
];
|
||||
|
||||
if (is_root) {
|
||||
if (is_root && !phantom) {
|
||||
fields.push(
|
||||
...[
|
||||
{ fieldtype: "Section Break" },
|
||||
@@ -384,10 +410,10 @@ class BOMConfigurator {
|
||||
return fields;
|
||||
}
|
||||
|
||||
convert_to_sub_assembly(node, view) {
|
||||
convert_to_sub_assembly(node, view, phantom = false) {
|
||||
let dialog = new frappe.ui.Dialog({
|
||||
fields: view.events.get_sub_assembly_modal_fields(view, node.is_root, true),
|
||||
title: __("Add Sub Assembly"),
|
||||
fields: view.events.get_sub_assembly_modal_fields(view, node.is_root, true, phantom),
|
||||
title: phantom ? __("Add Phantom Item") : __("Add Sub Assembly"),
|
||||
});
|
||||
|
||||
dialog.set_values({
|
||||
@@ -400,7 +426,9 @@ class BOMConfigurator {
|
||||
let bom_item = dialog.get_values();
|
||||
|
||||
if (!bom_item.item_code) {
|
||||
frappe.throw(__("Sub Assembly Item is mandatory"));
|
||||
frappe.throw(
|
||||
phantom ? __("Phantom Item is mandatory") : __("Sub Assembly Item is mandatory")
|
||||
);
|
||||
}
|
||||
|
||||
bom_item.items.forEach((d) => {
|
||||
@@ -425,6 +453,7 @@ class BOMConfigurator {
|
||||
workstation_type: node.data.workstation_type,
|
||||
operation_time: node.data.operation_time,
|
||||
workstation: node.data.workstation,
|
||||
phantom: phantom,
|
||||
},
|
||||
callback: (r) => {
|
||||
node.expandable = true;
|
||||
|
||||
@@ -1,6 +1,16 @@
|
||||
let beforePrintHandled = false;
|
||||
|
||||
frappe.realtime.on("sales_invoice_before_print", (data) => {
|
||||
let print_format = $('input[data-fieldname="print_format"]').val();
|
||||
let letterhead = $('input[data-fieldname="letterhead"]').val();
|
||||
|
||||
let allowed_print_formats = ["Sales Invoice Standard", "Sales Invoice with Item Image"];
|
||||
let allowed_letterheads = ["Company Letterhead", "Company Letterhead - Grey"];
|
||||
|
||||
if (!allowed_print_formats.includes(print_format) && !allowed_letterheads.includes(letterhead)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const route = frappe.get_route();
|
||||
|
||||
if (!beforePrintHandled && route[0] === "print" && route[1] === "Sales Invoice") {
|
||||
|
||||
@@ -1796,7 +1796,7 @@ class TestSalesOrder(AccountsTestMixin, IntegrationTestCase):
|
||||
mr.submit()
|
||||
|
||||
# WO from MR
|
||||
wo_name = raise_work_orders(mr.name)[0]
|
||||
wo_name = raise_work_orders(mr.name, mr.company)[0]
|
||||
wo = frappe.get_doc("Work Order", wo_name)
|
||||
wo.wip_warehouse = "Work In Progress - _TC"
|
||||
wo.skip_transfer = True
|
||||
|
||||
@@ -117,6 +117,7 @@
|
||||
"enable_item_wise_inventory_account",
|
||||
"enable_provisional_accounting_for_non_stock_items",
|
||||
"default_inventory_account",
|
||||
"valuation_method",
|
||||
"column_break_32",
|
||||
"stock_adjustment_account",
|
||||
"stock_received_but_not_billed",
|
||||
@@ -124,6 +125,10 @@
|
||||
"default_in_transit_warehouse",
|
||||
"manufacturing_section",
|
||||
"default_operating_cost_account",
|
||||
"column_break_9prc",
|
||||
"default_wip_warehouse",
|
||||
"default_fg_warehouse",
|
||||
"default_scrap_warehouse",
|
||||
"dashboard_tab"
|
||||
],
|
||||
"fields": [
|
||||
@@ -885,6 +890,39 @@
|
||||
"fieldname": "enable_item_wise_inventory_account",
|
||||
"fieldtype": "Check",
|
||||
"label": "Enable Item-wise Inventory Account"
|
||||
},
|
||||
{
|
||||
"default": "FIFO",
|
||||
"fieldname": "valuation_method",
|
||||
"fieldtype": "Select",
|
||||
"label": "Default Stock Valuation Method",
|
||||
"options": "FIFO\nMoving Average\nLIFO",
|
||||
"reqd": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "default_wip_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"label": " Default Work In Progress Warehouse ",
|
||||
"link_filters": "[[\"Warehouse\",\"disabled\",\"=\",0]]",
|
||||
"options": "Warehouse"
|
||||
},
|
||||
{
|
||||
"fieldname": "default_fg_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"label": "Default Finished Goods Warehouse",
|
||||
"link_filters": "[[\"Warehouse\",\"disabled\",\"=\",0]]",
|
||||
"options": "Warehouse"
|
||||
},
|
||||
{
|
||||
"fieldname": "default_scrap_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"label": "Default Scrap Warehouse",
|
||||
"link_filters": "[[\"Warehouse\",\"disabled\",\"=\",0]]",
|
||||
"options": "Warehouse"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_9prc",
|
||||
"fieldtype": "Column Break"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-building",
|
||||
@@ -892,7 +930,7 @@
|
||||
"image_field": "company_logo",
|
||||
"is_tree": 1,
|
||||
"links": [],
|
||||
"modified": "2025-10-23 13:15:52.411984",
|
||||
"modified": "2025-11-16 16:50:27.624096",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Setup",
|
||||
"name": "Company",
|
||||
|
||||
@@ -59,6 +59,7 @@ class Company(NestedSet):
|
||||
default_deferred_revenue_account: DF.Link | None
|
||||
default_discount_account: DF.Link | None
|
||||
default_expense_account: DF.Link | None
|
||||
default_fg_warehouse: DF.Link | None
|
||||
default_finance_book: DF.Link | None
|
||||
default_holiday_list: DF.Link | None
|
||||
default_in_transit_warehouse: DF.Link | None
|
||||
@@ -69,8 +70,10 @@ class Company(NestedSet):
|
||||
default_payable_account: DF.Link | None
|
||||
default_provisional_account: DF.Link | None
|
||||
default_receivable_account: DF.Link | None
|
||||
default_scrap_warehouse: DF.Link | None
|
||||
default_selling_terms: DF.Link | None
|
||||
default_warehouse_for_sales_return: DF.Link | None
|
||||
default_wip_warehouse: DF.Link | None
|
||||
depreciation_cost_center: DF.Link | None
|
||||
depreciation_expense_account: DF.Link | None
|
||||
disposal_account: DF.Link | None
|
||||
@@ -113,6 +116,7 @@ class Company(NestedSet):
|
||||
transactions_annual_history: DF.Code | None
|
||||
unrealized_exchange_gain_loss_account: DF.Link | None
|
||||
unrealized_profit_loss_account: DF.Link | None
|
||||
valuation_method: DF.Literal["FIFO", "Moving Average", "LIFO"]
|
||||
website: DF.Data | None
|
||||
write_off_account: DF.Link | None
|
||||
# end: auto-generated types
|
||||
@@ -163,6 +167,32 @@ class Company(NestedSet):
|
||||
self.validate_parent_company()
|
||||
self.set_reporting_currency()
|
||||
self.validate_inventory_account_settings()
|
||||
self.cant_change_valuation_method()
|
||||
|
||||
def cant_change_valuation_method(self):
|
||||
doc_before_save = self.get_doc_before_save()
|
||||
if not doc_before_save:
|
||||
return
|
||||
|
||||
previous_valuation_method = doc_before_save.get("valuation_method")
|
||||
|
||||
if previous_valuation_method and previous_valuation_method != self.valuation_method:
|
||||
# check if there are any stock ledger entries against items
|
||||
# which does not have it's own valuation method
|
||||
sle = frappe.db.sql(
|
||||
"""select name from `tabStock Ledger Entry` sle
|
||||
where exists(select name from tabItem
|
||||
where name=sle.item_code and (valuation_method is null or valuation_method='')) and sle.company=%s limit 1
|
||||
""",
|
||||
self.name,
|
||||
)
|
||||
|
||||
if sle:
|
||||
frappe.throw(
|
||||
_(
|
||||
"Can't change the valuation method, as there are transactions against some items which do not have its own valuation method"
|
||||
)
|
||||
)
|
||||
|
||||
def validate_inventory_account_settings(self):
|
||||
doc_before_save = self.get_doc_before_save()
|
||||
|
||||
@@ -5,12 +5,11 @@
|
||||
import os
|
||||
|
||||
import frappe
|
||||
from frappe import _
|
||||
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
|
||||
|
||||
from erpnext.setup.doctype.incoterm.incoterm import create_incoterms
|
||||
from erpnext.setup.utils import identity as _
|
||||
|
||||
from .default_success_action import get_default_success_action
|
||||
|
||||
@@ -184,33 +183,27 @@ def add_company_to_session_defaults():
|
||||
|
||||
def add_standard_navbar_items():
|
||||
navbar_settings = frappe.get_single("Navbar Settings")
|
||||
|
||||
# Translatable strings for below navbar items
|
||||
__ = _("Documentation")
|
||||
__ = _("User Forum")
|
||||
__ = _("Report an Issue")
|
||||
|
||||
erpnext_navbar_items = [
|
||||
{
|
||||
"item_label": "Documentation",
|
||||
"item_label": _("Documentation"),
|
||||
"item_type": "Route",
|
||||
"route": "https://docs.erpnext.com/",
|
||||
"is_standard": 1,
|
||||
},
|
||||
{
|
||||
"item_label": "User Forum",
|
||||
"item_label": _("User Forum"),
|
||||
"item_type": "Route",
|
||||
"route": "https://discuss.frappe.io",
|
||||
"is_standard": 1,
|
||||
},
|
||||
{
|
||||
"item_label": "Frappe School",
|
||||
"item_label": _("Frappe School"),
|
||||
"item_type": "Route",
|
||||
"route": "https://frappe.io/school?utm_source=in_app",
|
||||
"is_standard": 1,
|
||||
},
|
||||
{
|
||||
"item_label": "Report an Issue",
|
||||
"item_label": _("Report an Issue"),
|
||||
"item_type": "Route",
|
||||
"route": "https://github.com/frappe/erpnext/issues",
|
||||
"is_standard": 1,
|
||||
@@ -319,26 +312,26 @@ def create_letter_head():
|
||||
|
||||
|
||||
DEFAULT_ROLE_PROFILES = {
|
||||
"Inventory": [
|
||||
_("Inventory"): [
|
||||
"Stock User",
|
||||
"Stock Manager",
|
||||
"Item Manager",
|
||||
],
|
||||
"Manufacturing": [
|
||||
_("Manufacturing"): [
|
||||
"Stock User",
|
||||
"Manufacturing User",
|
||||
"Manufacturing Manager",
|
||||
],
|
||||
"Accounts": [
|
||||
_("Accounts"): [
|
||||
"Accounts User",
|
||||
"Accounts Manager",
|
||||
],
|
||||
"Sales": [
|
||||
_("Sales"): [
|
||||
"Sales User",
|
||||
"Stock User",
|
||||
"Sales Manager",
|
||||
],
|
||||
"Purchase": [
|
||||
_("Purchase"): [
|
||||
"Item Manager",
|
||||
"Stock User",
|
||||
"Purchase User",
|
||||
|
||||
@@ -15,14 +15,7 @@ from frappe.utils import cstr, getdate
|
||||
|
||||
from erpnext.accounts.doctype.account.account import RootNotEditable
|
||||
from erpnext.regional.address_template.setup import set_up_address_templates
|
||||
|
||||
|
||||
def _(x, *args, **kwargs):
|
||||
"""Redefine the translation function to return the string as is.
|
||||
|
||||
We want to create english records but still mark the strings as translatable.
|
||||
The respective DocTypes have 'Translate Link Fields' enabled."""
|
||||
return x
|
||||
from erpnext.setup.utils import identity as _
|
||||
|
||||
|
||||
def read_lines(filename: str) -> list[str]:
|
||||
@@ -579,7 +572,7 @@ def create_bank_account(args, demo=False):
|
||||
return doc
|
||||
|
||||
except RootNotEditable:
|
||||
frappe.throw(_("Bank account cannot be named as {0}").format(args.get("bank_account")))
|
||||
frappe.throw(frappe._("Bank account cannot be named as {0}").format(args.get("bank_account")))
|
||||
except frappe.DuplicateEntryError:
|
||||
# bank account same as a CoA entry
|
||||
pass
|
||||
|
||||
@@ -232,3 +232,15 @@ def welcome_email():
|
||||
site_name = get_default_company() or "ERPNext"
|
||||
title = _("Welcome to {0}").format(site_name)
|
||||
return title
|
||||
|
||||
|
||||
def identity(x, *args, **kwargs):
|
||||
"""Used for redefining the translation function to return the string as is.
|
||||
|
||||
We want to create english records but still mark the strings as translatable.
|
||||
E.g. when the respective DocTypes have 'Translate Link Fields' enabled or
|
||||
we're creating custom fields.
|
||||
|
||||
Use like this: `from erpnext.setup.utils import identity as _`
|
||||
"""
|
||||
return x
|
||||
|
||||
@@ -495,6 +495,7 @@ frappe.ui.form.on("Material Request", {
|
||||
method: "erpnext.stock.doctype.material_request.material_request.raise_work_orders",
|
||||
args: {
|
||||
material_request: frm.doc.name,
|
||||
company: frm.doc.company,
|
||||
},
|
||||
freeze: true,
|
||||
callback: function (r) {
|
||||
|
||||
@@ -833,11 +833,11 @@ def make_stock_entry(source_name, target_doc=None):
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def raise_work_orders(material_request):
|
||||
def raise_work_orders(material_request, company):
|
||||
mr = frappe.get_doc("Material Request", material_request)
|
||||
errors = []
|
||||
work_orders = []
|
||||
default_wip_warehouse = frappe.db.get_single_value("Manufacturing Settings", "default_wip_warehouse")
|
||||
default_wip_warehouse = frappe.get_cached_value("Company", company, "default_wip_warehouse")
|
||||
|
||||
for d in mr.items:
|
||||
if (d.stock_qty - d.ordered_qty) > 0:
|
||||
|
||||
@@ -747,7 +747,7 @@ class TestMaterialRequest(IntegrationTestCase):
|
||||
(mr.items[0].item_code, mr.items[0].warehouse),
|
||||
)[0][0]
|
||||
|
||||
prod_order = raise_work_orders(mr.name)
|
||||
prod_order = raise_work_orders(mr.name, mr.company)
|
||||
po = frappe.get_doc("Work Order", prod_order[0])
|
||||
po.wip_warehouse = "_Test Warehouse 1 - _TC"
|
||||
po.submit()
|
||||
@@ -789,7 +789,7 @@ class TestMaterialRequest(IntegrationTestCase):
|
||||
|
||||
self.assertEqual(requested_qty, existing_requested_qty + 120)
|
||||
|
||||
work_order = raise_work_orders(mr.name)
|
||||
work_order = raise_work_orders(mr.name, mr.company)
|
||||
wo = frappe.get_doc("Work Order", work_order[0])
|
||||
wo.qty = 50
|
||||
wo.wip_warehouse = "_Test Warehouse 1 - _TC"
|
||||
@@ -924,7 +924,7 @@ class TestMaterialRequest(IntegrationTestCase):
|
||||
item_code="_Test FG Item", material_request_type="Manufacture", do_not_submit=False
|
||||
)
|
||||
|
||||
work_order = raise_work_orders(mr.name)
|
||||
work_order = raise_work_orders(mr.name, mr.company)
|
||||
wo = frappe.get_doc("Work Order", work_order[0])
|
||||
wo.wip_warehouse = "_Test Warehouse 1 - _TC"
|
||||
wo.submit()
|
||||
|
||||
@@ -514,7 +514,7 @@ class SerialandBatchBundle(Document):
|
||||
if hasattr(sn_obj, "stock_queue") and sn_obj.stock_queue:
|
||||
stock_queue = parse_json(sn_obj.stock_queue)
|
||||
|
||||
val_method = get_valuation_method(self.item_code)
|
||||
val_method = get_valuation_method(self.item_code, self.company)
|
||||
|
||||
for d in self.entries:
|
||||
available_qty = 0
|
||||
@@ -642,7 +642,7 @@ class SerialandBatchBundle(Document):
|
||||
def set_incoming_rate_for_inward_transaction(self, row=None, save=False, prev_sle=None):
|
||||
from erpnext.stock.utils import get_valuation_method
|
||||
|
||||
valuation_method = get_valuation_method(self.item_code)
|
||||
valuation_method = get_valuation_method(self.item_code, self.company)
|
||||
|
||||
valuation_field = "valuation_rate"
|
||||
if self.voucher_type in ["Sales Invoice", "Delivery Note", "Quotation"]:
|
||||
@@ -2502,18 +2502,11 @@ def get_auto_batch_nos(kwargs):
|
||||
|
||||
|
||||
def get_batch_nos_from_sre(kwargs):
|
||||
from frappe.query_builder.functions import Max, Min, Sum
|
||||
from frappe.query_builder.functions import Sum
|
||||
|
||||
table = frappe.qb.DocType("Stock Reservation Entry")
|
||||
child_table = frappe.qb.DocType("Serial and Batch Entry")
|
||||
|
||||
if kwargs.based_on == "LIFO":
|
||||
creation_field = Max(child_table.creation).as_("sort_creation")
|
||||
order = frappe.query_builder.Order.desc
|
||||
else:
|
||||
creation_field = Min(child_table.creation).as_("sort_creation")
|
||||
order = frappe.query_builder.Order.asc
|
||||
|
||||
query = (
|
||||
frappe.qb.from_(table)
|
||||
.join(child_table)
|
||||
@@ -2522,7 +2515,6 @@ def get_batch_nos_from_sre(kwargs):
|
||||
child_table.batch_no,
|
||||
child_table.warehouse,
|
||||
Sum(child_table.qty - child_table.delivered_qty).as_("qty"),
|
||||
creation_field,
|
||||
)
|
||||
.where(
|
||||
(table.docstatus == 1)
|
||||
@@ -2530,7 +2522,6 @@ def get_batch_nos_from_sre(kwargs):
|
||||
& (child_table.qty != child_table.delivered_qty)
|
||||
)
|
||||
.groupby(child_table.batch_no, child_table.warehouse)
|
||||
.orderby("sort_creation", order=order)
|
||||
.orderby(child_table.batch_no, order=frappe.query_builder.Order.asc)
|
||||
)
|
||||
|
||||
|
||||
@@ -1382,8 +1382,8 @@ erpnext.stock.StockEntry = class StockEntry extends erpnext.stock.StockControlle
|
||||
this.frm.script_manager.copy_from_first_row("items", row, ["expense_account", "cost_center"]);
|
||||
}
|
||||
|
||||
if (!row.s_warehouse) row.s_warehouse = this.frm.doc.from_warehouse;
|
||||
if (!row.t_warehouse) row.t_warehouse = this.frm.doc.to_warehouse;
|
||||
if (this.frm.doc.from_warehouse) row.s_warehouse = this.frm.doc.from_warehouse;
|
||||
if (this.frm.doc.to_warehouse) row.t_warehouse = this.frm.doc.to_warehouse;
|
||||
|
||||
if (cint(frappe.user_defaults?.use_serial_batch_fields)) {
|
||||
frappe.model.set_value(row.doctype, row.name, "use_serial_batch_fields", 1);
|
||||
|
||||
@@ -202,6 +202,12 @@ class StockEntry(StockController, SubcontractingInwardController):
|
||||
for item in self.get("items"):
|
||||
item.update(get_bin_details(item.item_code, item.s_warehouse))
|
||||
|
||||
def before_insert(self):
|
||||
if self.subcontracting_order and frappe.get_cached_value(
|
||||
"Subcontracting Order", self.subcontracting_order, "reserve_stock"
|
||||
):
|
||||
self.set_serial_batch_from_reserved_entry()
|
||||
|
||||
def before_validate(self):
|
||||
from erpnext.stock.doctype.putaway_rule.putaway_rule import apply_putaway_rule
|
||||
|
||||
@@ -274,9 +280,10 @@ class StockEntry(StockController, SubcontractingInwardController):
|
||||
self.update_work_order()
|
||||
self.update_disassembled_order()
|
||||
self.adjust_stock_reservation_entries_for_return()
|
||||
self.update_sre_for_subcontracting_delivery()
|
||||
self.update_stock_reservation_entries()
|
||||
self.update_stock_ledger()
|
||||
self.make_stock_reserve_for_wip_and_fg()
|
||||
self.reserve_stock_for_subcontracting()
|
||||
|
||||
self.update_subcontract_order_supplied_items()
|
||||
self.update_subcontracting_order_status()
|
||||
@@ -324,7 +331,7 @@ class StockEntry(StockController, SubcontractingInwardController):
|
||||
self.update_transferred_qty()
|
||||
self.update_quality_inspection()
|
||||
self.adjust_stock_reservation_entries_for_return()
|
||||
self.update_sre_for_subcontracting_delivery()
|
||||
self.update_stock_reservation_entries()
|
||||
self.delete_auto_created_batches()
|
||||
self.delete_linked_stock_entry()
|
||||
|
||||
@@ -1889,6 +1896,30 @@ class StockEntry(StockController, SubcontractingInwardController):
|
||||
|
||||
pro_doc.set_reserved_qty_for_wip_and_fg(self)
|
||||
|
||||
def reserve_stock_for_subcontracting(self):
|
||||
if self.purpose == "Send to Subcontractor" and frappe.get_value(
|
||||
"Subcontracting Order", self.subcontracting_order, "reserve_stock"
|
||||
):
|
||||
items = {}
|
||||
for item in self.items:
|
||||
if item.sco_rm_detail in items:
|
||||
items[item.sco_rm_detail].qty_to_reserve += item.transfer_qty
|
||||
items[item.sco_rm_detail].serial_and_batch_bundles.append(item.serial_and_batch_bundle)
|
||||
else:
|
||||
items[item.sco_rm_detail] = frappe._dict(
|
||||
{
|
||||
"name": item.sco_rm_detail,
|
||||
"qty_to_reserve": item.transfer_qty,
|
||||
"warehouse": item.t_warehouse,
|
||||
"reference_voucher_detail_no": item.name,
|
||||
"serial_and_batch_bundles": [item.serial_and_batch_bundle],
|
||||
}
|
||||
)
|
||||
|
||||
frappe.get_doc("Subcontracting Order", self.subcontracting_order).reserve_raw_materials(
|
||||
items=items.values(), stock_entry=self.name
|
||||
)
|
||||
|
||||
def cancel_stock_reserve_for_wip_and_fg(self):
|
||||
if self.is_stock_reserve_for_work_order():
|
||||
pro_doc = frappe.get_doc("Work Order", self.work_order)
|
||||
@@ -2230,21 +2261,16 @@ class StockEntry(StockController, SubcontractingInwardController):
|
||||
self.calculate_rate_and_amount(raise_error_if_no_rate=False)
|
||||
|
||||
def set_serial_batch_from_reserved_entry(self):
|
||||
if not self.work_order:
|
||||
return
|
||||
if self.work_order and frappe.get_cached_value("Work Order", self.work_order, "reserve_stock"):
|
||||
skip_transfer = frappe.get_cached_value("Work Order", self.work_order, "skip_transfer")
|
||||
|
||||
if not frappe.get_cached_value("Work Order", self.work_order, "reserve_stock"):
|
||||
return
|
||||
|
||||
skip_transfer = frappe.get_cached_value("Work Order", self.work_order, "skip_transfer")
|
||||
|
||||
if (
|
||||
self.purpose not in ["Material Transfer for Manufacture"]
|
||||
and frappe.db.get_single_value("Manufacturing Settings", "backflush_raw_materials_based_on")
|
||||
!= "BOM"
|
||||
and not skip_transfer
|
||||
):
|
||||
return
|
||||
if (
|
||||
self.purpose not in ["Material Transfer for Manufacture"]
|
||||
and frappe.db.get_single_value("Manufacturing Settings", "backflush_raw_materials_based_on")
|
||||
!= "BOM"
|
||||
and not skip_transfer
|
||||
):
|
||||
return
|
||||
|
||||
reservation_entries = self.get_available_reserved_materials()
|
||||
if not reservation_entries:
|
||||
@@ -2252,6 +2278,9 @@ class StockEntry(StockController, SubcontractingInwardController):
|
||||
|
||||
new_items_to_add = []
|
||||
for d in self.items:
|
||||
if d.serial_and_batch_bundle or d.serial_no or d.batch_no:
|
||||
continue
|
||||
|
||||
key = (d.item_code, d.s_warehouse)
|
||||
if details := reservation_entries.get(key):
|
||||
original_qty = d.qty
|
||||
@@ -2363,7 +2392,7 @@ class StockEntry(StockController, SubcontractingInwardController):
|
||||
)
|
||||
.where(
|
||||
(doctype.docstatus == 1)
|
||||
& (doctype.voucher_no == self.work_order)
|
||||
& (doctype.voucher_no == (self.work_order or self.subcontracting_order))
|
||||
& (serial_batch_doc.delivered_qty < serial_batch_doc.qty)
|
||||
)
|
||||
.orderby(serial_batch_doc.idx)
|
||||
|
||||
@@ -84,7 +84,7 @@
|
||||
"no_copy": 1,
|
||||
"oldfieldname": "voucher_type",
|
||||
"oldfieldtype": "Data",
|
||||
"options": "\nSales Order\nWork Order\nSubcontracting Inward Order\nProduction Plan",
|
||||
"options": "\nSales Order\nWork Order\nSubcontracting Inward Order\nProduction Plan\nSubcontracting Order",
|
||||
"print_width": "150px",
|
||||
"read_only": 1,
|
||||
"width": "150px"
|
||||
@@ -315,8 +315,7 @@
|
||||
},
|
||||
{
|
||||
"fieldname": "production_section",
|
||||
"fieldtype": "Section Break",
|
||||
"label": "Production"
|
||||
"fieldtype": "Section Break"
|
||||
},
|
||||
{
|
||||
"fieldname": "column_break_qdwj",
|
||||
@@ -335,7 +334,7 @@
|
||||
{
|
||||
"fieldname": "transferred_qty",
|
||||
"fieldtype": "Float",
|
||||
"label": "Qty in WIP Warehouse"
|
||||
"label": "Transferred Qty"
|
||||
}
|
||||
],
|
||||
"grid_page_length": 50,
|
||||
@@ -344,7 +343,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2025-10-12 19:48:33.170835",
|
||||
"modified": "2025-11-10 16:09:10.380024",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Stock Reservation Entry",
|
||||
|
||||
@@ -64,7 +64,12 @@ class StockReservationEntry(Document):
|
||||
voucher_no: DF.DynamicLink | None
|
||||
voucher_qty: DF.Float
|
||||
voucher_type: DF.Literal[
|
||||
"", "Sales Order", "Work Order", "Subcontracting Inward Order", "Production Plan"
|
||||
"",
|
||||
"Sales Order",
|
||||
"Work Order",
|
||||
"Subcontracting Inward Order",
|
||||
"Production Plan",
|
||||
"Subcontracting Order",
|
||||
]
|
||||
warehouse: DF.Link | None
|
||||
# end: auto-generated types
|
||||
@@ -338,7 +343,7 @@ class StockReservationEntry(Document):
|
||||
|
||||
def validate_reservation_based_on_serial_and_batch(self) -> None:
|
||||
"""Validates `Reserved Qty`, `Serial and Batch Nos` when `Reservation Based On` is `Serial and Batch`."""
|
||||
if self.voucher_type == "Work Order":
|
||||
if self.voucher_type in ["Work Order", "Subcontracting Order"]:
|
||||
return
|
||||
|
||||
if self.reservation_based_on == "Serial and Batch":
|
||||
@@ -460,13 +465,14 @@ class StockReservationEntry(Document):
|
||||
"Sales Order": "Sales Order Item",
|
||||
"Work Order": "Work Order Item",
|
||||
"Production Plan": "Production Plan Sub Assembly Item",
|
||||
"Subcontracting Order": "Subcontracting Order Supplied Item",
|
||||
}.get(self.voucher_type, None)
|
||||
|
||||
if item_doctype:
|
||||
sre = frappe.qb.DocType("Stock Reservation Entry")
|
||||
reserved_qty = (
|
||||
frappe.qb.from_(sre)
|
||||
.select(Sum(sre.reserved_qty))
|
||||
.select(Sum(sre.reserved_qty - sre.delivered_qty - sre.transferred_qty - sre.consumed_qty))
|
||||
.where(
|
||||
(sre.docstatus == 1)
|
||||
& (sre.voucher_type == self.voucher_type)
|
||||
@@ -574,7 +580,7 @@ class StockReservationEntry(Document):
|
||||
)
|
||||
|
||||
from_voucher_detail_no = None
|
||||
if self.from_voucher_type and self.from_voucher_type == "Stock Entry":
|
||||
if self.from_voucher_type and self.from_voucher_type in ["Stock Entry", "Production Plan"]:
|
||||
from_voucher_detail_no = self.from_voucher_detail_no
|
||||
|
||||
total_reserved_qty = get_sre_reserved_qty_for_voucher_detail_no(
|
||||
@@ -1276,7 +1282,7 @@ class StockReservation:
|
||||
if not reservation_entries:
|
||||
return
|
||||
|
||||
entries_to_reserve = frappe._dict({})
|
||||
entries_to_reserve = frappe._dict()
|
||||
for row in reservation_entries:
|
||||
reserved_qty_field = "reserved_qty" if row.reservation_based_on == "Qty" else "sabb_qty"
|
||||
delivered_qty_field = (
|
||||
@@ -1293,7 +1299,7 @@ class StockReservation:
|
||||
if available_qty <= 0:
|
||||
continue
|
||||
|
||||
key = (row.item_code, row.warehouse)
|
||||
key = (row.item_code, row.warehouse, entry.voucher_detail_no)
|
||||
|
||||
if key not in entries_to_reserve:
|
||||
entries_to_reserve.setdefault(
|
||||
@@ -1303,7 +1309,7 @@ class StockReservation:
|
||||
"qty_to_reserve": 0.0,
|
||||
"item_code": row.item_code,
|
||||
"warehouse": row.warehouse,
|
||||
"voucher_type": entry.voucher_type,
|
||||
"voucher_type": entry.voucher_type or to_doctype,
|
||||
"voucher_no": entry.voucher_no,
|
||||
"voucher_detail_no": entry.voucher_detail_no,
|
||||
"serial_nos": [],
|
||||
@@ -1475,6 +1481,9 @@ class StockReservation:
|
||||
.orderby(sabb_entry.idx)
|
||||
)
|
||||
|
||||
if self.items and (data := [item.from_voucher_detail_no for item in self.items]):
|
||||
query = query.where(sre.voucher_detail_no.isin(data))
|
||||
|
||||
if against_fg_item:
|
||||
query = query.where(
|
||||
sre.voucher_detail_no.isin(
|
||||
@@ -1490,9 +1499,14 @@ class StockReservation:
|
||||
|
||||
def get_items_to_reserve(self, docnames, from_doctype, to_doctype):
|
||||
field = frappe.scrub(from_doctype)
|
||||
item_code_fieldname, child_table_suffix = (
|
||||
("rm_item_code", " Supplied Item")
|
||||
if to_doctype == "Subcontracting Order"
|
||||
else ("item_code", " Item")
|
||||
)
|
||||
|
||||
doctype = frappe.qb.DocType(to_doctype)
|
||||
child_doctype = frappe.qb.DocType(to_doctype + " Item")
|
||||
child_doctype = frappe.qb.DocType(to_doctype + child_table_suffix)
|
||||
|
||||
query = (
|
||||
frappe.qb.from_(doctype)
|
||||
@@ -1501,11 +1515,12 @@ class StockReservation:
|
||||
.select(
|
||||
doctype.name.as_("voucher_no"),
|
||||
child_doctype.name.as_("voucher_detail_no"),
|
||||
child_doctype.item_code,
|
||||
child_doctype[item_code_fieldname].as_("item_code"),
|
||||
doctype.company,
|
||||
child_doctype.stock_uom,
|
||||
)
|
||||
.where((doctype.docstatus == 1) & (doctype[field].isin(docnames)))
|
||||
.groupby(child_doctype.name)
|
||||
)
|
||||
|
||||
if to_doctype == "Work Order":
|
||||
@@ -1523,6 +1538,15 @@ class StockReservation:
|
||||
(doctype.qty > doctype.material_transferred_for_manufacturing)
|
||||
& (doctype.status != "Completed")
|
||||
)
|
||||
elif to_doctype == "Subcontracting Order":
|
||||
query = query.select(
|
||||
child_doctype.stock_reserved_qty,
|
||||
child_doctype.required_qty.as_("qty"),
|
||||
child_doctype.reserve_warehouse.as_("source_warehouse"),
|
||||
)
|
||||
|
||||
if self.items and (data := [item.voucher_detail_no for item in self.items]):
|
||||
query = query.where(child_doctype.name.isin(data))
|
||||
|
||||
data = query.run(as_dict=True)
|
||||
items = []
|
||||
|
||||
@@ -52,7 +52,7 @@ def format_report_data(filters: Filters, item_details: dict, to_date: str) -> li
|
||||
range_values = get_range_age(filters, fifo_queue, to_date, item_dict)
|
||||
|
||||
check_and_replace_valuations_if_moving_average(
|
||||
range_values, details.valuation_method, details.valuation_rate
|
||||
range_values, details.valuation_method, details.valuation_rate, filters.get("company")
|
||||
)
|
||||
|
||||
row = [details.name, details.item_name, details.description, details.item_group, details.brand]
|
||||
@@ -76,10 +76,12 @@ def format_report_data(filters: Filters, item_details: dict, to_date: str) -> li
|
||||
return data
|
||||
|
||||
|
||||
def check_and_replace_valuations_if_moving_average(range_values, item_valuation_method, valuation_rate):
|
||||
def check_and_replace_valuations_if_moving_average(
|
||||
range_values, item_valuation_method, valuation_rate, company
|
||||
):
|
||||
if item_valuation_method == "Moving Average" or (
|
||||
not item_valuation_method
|
||||
and frappe.db.get_single_value("Stock Settings", "valuation_method") == "Moving Average"
|
||||
and frappe.get_cached_value("Company", company, "valuation_method") == "Moving Average"
|
||||
):
|
||||
for i in range(0, len(range_values), 2):
|
||||
range_values[i + 1] = range_values[i] * valuation_rate
|
||||
|
||||
@@ -201,7 +201,7 @@ def get_columns():
|
||||
def get_data(filters=None):
|
||||
filters = frappe._dict(filters or {})
|
||||
item_warehouse_map = get_item_warehouse_combinations(filters)
|
||||
valuation_method = frappe.db.get_single_value("Stock Settings", "valuation_method")
|
||||
valuation_method = frappe.get_cached_value("Company", filters.get("company"), "valuation_method")
|
||||
|
||||
data = []
|
||||
if item_warehouse_map:
|
||||
|
||||
@@ -861,9 +861,9 @@ class BatchNoValuation(DeprecatedBatchNoValuation):
|
||||
self.batchwise_valuation_batches = []
|
||||
self.non_batchwise_valuation_batches = []
|
||||
|
||||
if get_valuation_method(self.sle.item_code) == "Moving Average" and frappe.get_single_value(
|
||||
"Stock Settings", "do_not_use_batchwise_valuation"
|
||||
):
|
||||
if get_valuation_method(
|
||||
self.sle.item_code, self.sle.company
|
||||
) == "Moving Average" and frappe.get_single_value("Stock Settings", "do_not_use_batchwise_valuation"):
|
||||
self.non_batchwise_valuation_batches = self.batches
|
||||
return
|
||||
|
||||
|
||||
@@ -563,7 +563,7 @@ class update_entries_after:
|
||||
|
||||
self.company = frappe.get_cached_value("Warehouse", self.args.warehouse, "company")
|
||||
self.set_precision()
|
||||
self.valuation_method = get_valuation_method(self.item_code)
|
||||
self.valuation_method = get_valuation_method(self.item_code, self.company)
|
||||
|
||||
self.new_items_found = False
|
||||
self.distinct_item_warehouses = args.get("distinct_item_warehouses", frappe._dict())
|
||||
@@ -1087,7 +1087,7 @@ class update_entries_after:
|
||||
avg_rate = 0.0
|
||||
|
||||
for d in sabb_data:
|
||||
incoming_rate = get_incoming_rate_for_serial_and_batch(self.item_code, d, sn_obj)
|
||||
incoming_rate = get_incoming_rate_for_serial_and_batch(self.item_code, d, sn_obj, self.company)
|
||||
amount = incoming_rate * flt(d.qty)
|
||||
tot_amt += flt(amount)
|
||||
total_qty += flt(d.qty)
|
||||
@@ -2398,7 +2398,7 @@ def get_serial_from_sabb(serial_and_batch_bundle):
|
||||
)
|
||||
|
||||
|
||||
def get_incoming_rate_for_serial_and_batch(item_code, row, sn_obj):
|
||||
def get_incoming_rate_for_serial_and_batch(item_code, row, sn_obj, company):
|
||||
if row.serial_no:
|
||||
return abs(sn_obj.serial_no_incoming_rate.get(row.serial_no, 0.0))
|
||||
else:
|
||||
@@ -2406,7 +2406,7 @@ def get_incoming_rate_for_serial_and_batch(item_code, row, sn_obj):
|
||||
if hasattr(sn_obj, "stock_queue") and sn_obj.stock_queue:
|
||||
stock_queue = parse_json(sn_obj.stock_queue)
|
||||
|
||||
val_method = get_valuation_method(item_code)
|
||||
val_method = get_valuation_method(item_code, company)
|
||||
|
||||
actual_qty = row.qty
|
||||
if stock_queue and val_method == "FIFO" and row.batch_no in sn_obj.non_batchwise_valuation_batches:
|
||||
|
||||
@@ -302,7 +302,7 @@ def get_incoming_rate(args, raise_error_if_no_rate=True):
|
||||
|
||||
return batch_obj.get_incoming_rate()
|
||||
else:
|
||||
valuation_method = get_valuation_method(args.get("item_code"))
|
||||
valuation_method = get_valuation_method(args.get("item_code"), args.get("company"))
|
||||
previous_sle = get_previous_sle(args)
|
||||
if valuation_method in ("FIFO", "LIFO"):
|
||||
if previous_sle:
|
||||
@@ -374,11 +374,15 @@ def get_avg_purchase_rate(serial_nos):
|
||||
|
||||
|
||||
@frappe.request_cache
|
||||
def get_valuation_method(item_code):
|
||||
def get_valuation_method(item_code, company=None):
|
||||
"""get valuation method from item or default"""
|
||||
val_method = frappe.get_cached_value("Item", item_code, "valuation_method")
|
||||
if not val_method:
|
||||
val_method = frappe.get_cached_doc("Stock Settings").valuation_method or "FIFO"
|
||||
val_method = (
|
||||
frappe.get_cached_value("Company", company, "valuation_method")
|
||||
if company
|
||||
else frappe.get_single_value("Stock Settings", "valuation_method") or "FIFO"
|
||||
)
|
||||
return val_method
|
||||
|
||||
|
||||
|
||||
@@ -172,11 +172,279 @@ frappe.ui.form.on("Subcontracting Order", {
|
||||
__("Status")
|
||||
);
|
||||
}
|
||||
|
||||
if (frm.doc.reserve_stock) {
|
||||
if (frm.doc.status !== "Closed") {
|
||||
if (frm.doc.__onload && frm.doc.__onload.has_unreserved_stock) {
|
||||
frm.add_custom_button(
|
||||
__("Reserve"),
|
||||
() => frm.events.create_stock_reservation_entries(frm),
|
||||
__("Stock Reservation")
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if (
|
||||
frm.doc.__onload &&
|
||||
frm.doc.__onload.has_reserved_stock &&
|
||||
frappe.model.can_cancel("Stock Reservation Entry")
|
||||
) {
|
||||
frm.add_custom_button(
|
||||
__("Unreserve"),
|
||||
() => frm.events.cancel_stock_reservation_entries(frm),
|
||||
__("Stock Reservation")
|
||||
);
|
||||
}
|
||||
|
||||
frm.doc.supplied_items.forEach((item) => {
|
||||
if (
|
||||
flt(item.stock_reserved_qty) > 0 &&
|
||||
frappe.model.can_read("Stock Reservation Entry")
|
||||
) {
|
||||
frm.add_custom_button(
|
||||
__("Reserved Stock"),
|
||||
() => frm.events.show_reserved_stock(frm),
|
||||
__("Stock Reservation")
|
||||
);
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
frm.trigger("get_materials_from_supplier");
|
||||
},
|
||||
|
||||
create_stock_reservation_entries(frm) {
|
||||
const dialog = new frappe.ui.Dialog({
|
||||
title: __("Stock Reservation"),
|
||||
size: "extra-large",
|
||||
fields: [
|
||||
{
|
||||
fieldname: "items",
|
||||
fieldtype: "Table",
|
||||
label: __("Items to Reserve"),
|
||||
allow_bulk_edit: false,
|
||||
cannot_add_rows: true,
|
||||
cannot_delete_rows: true,
|
||||
data: [],
|
||||
fields: [
|
||||
{
|
||||
fieldname: "subcontracting_order_supplied_item",
|
||||
fieldtype: "Link",
|
||||
label: __("Subcontracting Order Supplied Item"),
|
||||
options: "Subcontracting Order Supplied Item",
|
||||
reqd: 1,
|
||||
in_list_view: 1,
|
||||
read_only: 1,
|
||||
get_query: () => {
|
||||
return {
|
||||
query: "erpnext.controllers.queries.get_filtered_child_rows",
|
||||
filters: {
|
||||
parenttype: frm.doc.doctype,
|
||||
parent: frm.doc.name,
|
||||
},
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldname: "rm_item_code",
|
||||
fieldtype: "Link",
|
||||
label: __("Item Code"),
|
||||
options: "Item",
|
||||
reqd: 1,
|
||||
read_only: 1,
|
||||
in_list_view: 1,
|
||||
},
|
||||
{
|
||||
fieldname: "warehouse",
|
||||
fieldtype: "Link",
|
||||
label: __("Warehouse"),
|
||||
options: "Warehouse",
|
||||
reqd: 1,
|
||||
in_list_view: 1,
|
||||
read_only: 1,
|
||||
},
|
||||
{
|
||||
fieldname: "qty_to_reserve",
|
||||
fieldtype: "Float",
|
||||
label: __("Qty"),
|
||||
reqd: 1,
|
||||
in_list_view: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
primary_action_label: __("Reserve Stock"),
|
||||
primary_action: () => {
|
||||
var data = { items: dialog.fields_dict.items.grid.get_selected_children() };
|
||||
|
||||
if (data.items && data.items.length > 0) {
|
||||
frappe.call({
|
||||
doc: frm.doc,
|
||||
method: "reserve_raw_materials",
|
||||
args: {
|
||||
items: data.items.map((item) => ({
|
||||
name: item.subcontracting_order_supplied_item,
|
||||
qty_to_reserve: item.qty_to_reserve,
|
||||
})),
|
||||
},
|
||||
freeze: true,
|
||||
freeze_message: __("Reserving Stock..."),
|
||||
callback: (_) => {
|
||||
frm.reload_doc();
|
||||
},
|
||||
});
|
||||
|
||||
dialog.hide();
|
||||
} else {
|
||||
frappe.msgprint(__("Please select items to reserve."));
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
frm.doc.supplied_items.forEach((item) => {
|
||||
let unreserved_qty =
|
||||
flt(item.required_qty) - flt(item.supplied_qty) - flt(item.stock_reserved_qty);
|
||||
|
||||
if (unreserved_qty > 0) {
|
||||
dialog.fields_dict.items.df.data.push({
|
||||
__checked: 1,
|
||||
subcontracting_order_supplied_item: item.name,
|
||||
rm_item_code: item.rm_item_code,
|
||||
warehouse: item.reserve_warehouse,
|
||||
qty_to_reserve: unreserved_qty,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
dialog.fields_dict.items.grid.refresh();
|
||||
dialog.show();
|
||||
},
|
||||
|
||||
cancel_stock_reservation_entries(frm) {
|
||||
const dialog = new frappe.ui.Dialog({
|
||||
title: __("Stock Unreservation"),
|
||||
size: "extra-large",
|
||||
fields: [
|
||||
{
|
||||
fieldname: "sr_entries",
|
||||
fieldtype: "Table",
|
||||
label: __("Reserved Stock"),
|
||||
allow_bulk_edit: false,
|
||||
cannot_add_rows: true,
|
||||
cannot_delete_rows: true,
|
||||
in_place_edit: true,
|
||||
data: [],
|
||||
fields: [
|
||||
{
|
||||
fieldname: "sre",
|
||||
fieldtype: "Link",
|
||||
label: __("Stock Reservation Entry"),
|
||||
options: "Stock Reservation Entry",
|
||||
reqd: 1,
|
||||
read_only: 1,
|
||||
in_list_view: 1,
|
||||
},
|
||||
{
|
||||
fieldname: "item_code",
|
||||
fieldtype: "Link",
|
||||
label: __("Item Code"),
|
||||
options: "Item",
|
||||
reqd: 1,
|
||||
read_only: 1,
|
||||
in_list_view: 1,
|
||||
},
|
||||
{
|
||||
fieldname: "warehouse",
|
||||
fieldtype: "Link",
|
||||
label: __("Warehouse"),
|
||||
options: "Warehouse",
|
||||
reqd: 1,
|
||||
read_only: 1,
|
||||
in_list_view: 1,
|
||||
},
|
||||
{
|
||||
fieldname: "qty",
|
||||
fieldtype: "Float",
|
||||
label: __("Qty"),
|
||||
reqd: 1,
|
||||
read_only: 1,
|
||||
in_list_view: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
primary_action_label: __("Unreserve Stock"),
|
||||
primary_action: () => {
|
||||
var data = { sr_entries: dialog.fields_dict.sr_entries.grid.get_selected_children() };
|
||||
|
||||
if (data.sr_entries && data.sr_entries.length > 0) {
|
||||
frappe.call({
|
||||
doc: frm.doc,
|
||||
method: "cancel_stock_reservation_entries",
|
||||
args: {
|
||||
sre_list: data.sr_entries.map((item) => item.sre),
|
||||
},
|
||||
freeze: true,
|
||||
freeze_message: __("Unreserving Stock..."),
|
||||
callback: (_) => {
|
||||
frm.doc.__onload.has_reserved_stock = false;
|
||||
frm.reload_doc();
|
||||
},
|
||||
});
|
||||
|
||||
dialog.hide();
|
||||
} else {
|
||||
frappe.msgprint(__("Please select items to unreserve."));
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
frappe
|
||||
.call({
|
||||
method: "erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry.get_stock_reservation_entries_for_voucher",
|
||||
args: {
|
||||
voucher_type: frm.doctype,
|
||||
voucher_no: frm.doc.name,
|
||||
},
|
||||
callback: (r) => {
|
||||
if (!r.exc && r.message) {
|
||||
r.message.forEach((sre) => {
|
||||
if (flt(sre.reserved_qty) > flt(sre.delivered_qty)) {
|
||||
dialog.fields_dict.sr_entries.df.data.push({
|
||||
sre: sre.name,
|
||||
item_code: sre.item_code,
|
||||
warehouse: sre.warehouse,
|
||||
qty: flt(sre.reserved_qty) - flt(sre.delivered_qty),
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
})
|
||||
.then((r) => {
|
||||
dialog.fields_dict.sr_entries.grid.refresh();
|
||||
dialog.show();
|
||||
});
|
||||
},
|
||||
|
||||
show_reserved_stock(frm) {
|
||||
// Get the latest modified date from the items table.
|
||||
var to_date = moment(new Date(Math.max(...frm.doc.items.map((e) => new Date(e.modified))))).format(
|
||||
"YYYY-MM-DD"
|
||||
);
|
||||
|
||||
frappe.route_options = {
|
||||
company: frm.doc.company,
|
||||
from_date: frm.doc.transaction_date,
|
||||
to_date: to_date,
|
||||
voucher_type: frm.doc.doctype,
|
||||
voucher_no: frm.doc.name,
|
||||
};
|
||||
frappe.set_route("query-report", "Reserved Stock");
|
||||
},
|
||||
|
||||
update_subcontracting_order_status(frm, status) {
|
||||
frappe.call({
|
||||
method: "erpnext.subcontracting.doctype.subcontracting_order.subcontracting_order.update_subcontracting_order_status",
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
"service_items",
|
||||
"raw_materials_supplied_section",
|
||||
"set_reserve_warehouse",
|
||||
"reserve_stock",
|
||||
"supplied_items",
|
||||
"tab_address_and_contact",
|
||||
"supplier_address",
|
||||
@@ -62,7 +63,8 @@
|
||||
"select_print_heading",
|
||||
"column_break_43",
|
||||
"letter_head",
|
||||
"tab_connections"
|
||||
"tab_connections",
|
||||
"production_plan"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
@@ -471,6 +473,22 @@
|
||||
"no_copy": 1,
|
||||
"options": "Currency",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "reserve_stock",
|
||||
"fieldtype": "Check",
|
||||
"label": "Reserve Stock",
|
||||
"no_copy": 1,
|
||||
"show_on_timeline": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "production_plan",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"label": "Production Plan",
|
||||
"no_copy": 1,
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-file-text",
|
||||
|
||||
@@ -8,6 +8,10 @@ from frappe.utils import flt
|
||||
|
||||
from erpnext.buying.utils import check_on_hold_or_closed_status
|
||||
from erpnext.controllers.subcontracting_controller import SubcontractingController
|
||||
from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import (
|
||||
StockReservation,
|
||||
has_reserved_stock,
|
||||
)
|
||||
from erpnext.stock.stock_balance import update_bin_qty
|
||||
from erpnext.stock.utils import get_bin
|
||||
|
||||
@@ -50,8 +54,10 @@ class SubcontractingOrder(SubcontractingController):
|
||||
letter_head: DF.Link | None
|
||||
naming_series: DF.Literal["SC-ORD-.YYYY.-"]
|
||||
per_received: DF.Percent
|
||||
production_plan: DF.Data | None
|
||||
project: DF.Link | None
|
||||
purchase_order: DF.Link
|
||||
reserve_stock: DF.Check
|
||||
schedule_date: DF.Date | None
|
||||
select_print_heading: DF.Link | None
|
||||
service_items: DF.Table[SubcontractingOrderServiceItem]
|
||||
@@ -105,6 +111,13 @@ class SubcontractingOrder(SubcontractingController):
|
||||
frappe.db.get_single_value("Buying Settings", "over_transfer_allowance"),
|
||||
)
|
||||
|
||||
if self.reserve_stock:
|
||||
if self.has_unreserved_stock():
|
||||
self.set_onload("has_unreserved_stock", True)
|
||||
|
||||
if has_reserved_stock(self.doctype, self.name):
|
||||
self.set_onload("has_reserved_stock", True)
|
||||
|
||||
def before_validate(self):
|
||||
super().before_validate()
|
||||
|
||||
@@ -121,6 +134,7 @@ class SubcontractingOrder(SubcontractingController):
|
||||
self.update_prevdoc_status()
|
||||
self.update_status()
|
||||
self.update_subcontracted_quantity_in_po()
|
||||
self.reserve_raw_materials()
|
||||
|
||||
def on_cancel(self):
|
||||
self.update_prevdoc_status()
|
||||
@@ -253,10 +267,10 @@ class SubcontractingOrder(SubcontractingController):
|
||||
if si.fg_item:
|
||||
item = frappe.get_doc("Item", si.fg_item)
|
||||
|
||||
qty, subcontracted_qty, fg_item_qty = frappe.db.get_value(
|
||||
qty, subcontracted_qty, fg_item_qty, production_plan_sub_assembly_item = frappe.db.get_value(
|
||||
"Purchase Order Item",
|
||||
si.purchase_order_item,
|
||||
["qty", "subcontracted_qty", "fg_item_qty"],
|
||||
["qty", "subcontracted_qty", "fg_item_qty", "production_plan_sub_assembly_item"],
|
||||
)
|
||||
available_qty = flt(qty) - flt(subcontracted_qty)
|
||||
|
||||
@@ -292,6 +306,7 @@ class SubcontractingOrder(SubcontractingController):
|
||||
"purchase_order_item": si.purchase_order_item,
|
||||
"material_request": si.material_request,
|
||||
"material_request_item": si.material_request_item,
|
||||
"production_plan_sub_assembly_item": production_plan_sub_assembly_item,
|
||||
}
|
||||
)
|
||||
else:
|
||||
@@ -362,6 +377,90 @@ class SubcontractingOrder(SubcontractingController):
|
||||
subcontracted_qty,
|
||||
)
|
||||
|
||||
@frappe.whitelist()
|
||||
def reserve_raw_materials(self, items=None, stock_entry=None):
|
||||
if self.reserve_stock:
|
||||
item_dict = {}
|
||||
|
||||
if items:
|
||||
item_dict = {d["name"]: d for d in items}
|
||||
items = [item for item in self.supplied_items if item.name in item_dict]
|
||||
|
||||
reservation_items = []
|
||||
is_transfer = False
|
||||
for item in items or self.supplied_items:
|
||||
data = frappe._dict(
|
||||
{
|
||||
"voucher_no": self.name,
|
||||
"voucher_type": self.doctype,
|
||||
"voucher_detail_no": item.name,
|
||||
"item_code": item.rm_item_code,
|
||||
"warehouse": item_dict.get(item.name, {}).get("warehouse", item.reserve_warehouse),
|
||||
"stock_qty": item_dict.get(item.name, {}).get("qty_to_reserve", item.required_qty),
|
||||
}
|
||||
)
|
||||
|
||||
if stock_entry:
|
||||
data.update(
|
||||
{
|
||||
"from_voucher_no": stock_entry,
|
||||
"from_voucher_type": "Stock Entry",
|
||||
"from_voucher_detail_no": item_dict[item.name]["reference_voucher_detail_no"],
|
||||
"serial_and_batch_bundles": item_dict[item.name]["serial_and_batch_bundles"],
|
||||
}
|
||||
)
|
||||
elif self.production_plan:
|
||||
fg_item = next(i for i in self.items if i.name == item.reference_name)
|
||||
if production_plan_sub_assembly_item := fg_item.production_plan_sub_assembly_item:
|
||||
from_voucher_detail_no, reserved_qty = frappe.get_value(
|
||||
"Material Request Plan Item",
|
||||
{
|
||||
"parent": self.production_plan,
|
||||
"item_code": item.rm_item_code,
|
||||
"warehouse": item.reserve_warehouse,
|
||||
"sub_assembly_item_reference": production_plan_sub_assembly_item,
|
||||
"docstatus": 1,
|
||||
},
|
||||
["name", "stock_reserved_qty"],
|
||||
)
|
||||
if flt(item.stock_reserved_qty) < reserved_qty:
|
||||
is_transfer = True
|
||||
data.update(
|
||||
{
|
||||
"from_voucher_no": self.production_plan,
|
||||
"from_voucher_type": "Production Plan",
|
||||
"from_voucher_detail_no": from_voucher_detail_no,
|
||||
}
|
||||
)
|
||||
|
||||
reservation_items.append(data)
|
||||
|
||||
sre = StockReservation(self, items=reservation_items, notify=True)
|
||||
if is_transfer:
|
||||
sre.transfer_reservation_entries_to(
|
||||
self.production_plan, from_doctype="Production Plan", to_doctype="Subcontracting Order"
|
||||
)
|
||||
else:
|
||||
if sre.make_stock_reservation_entries():
|
||||
frappe.msgprint(_("Stock Reservation Entries created"), alert=True, indicator="blue")
|
||||
|
||||
def has_unreserved_stock(self) -> bool:
|
||||
for item in self.get("supplied_items"):
|
||||
if item.required_qty - flt(item.supplied_qty) - flt(item.stock_reserved_qty) > 0:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
@frappe.whitelist()
|
||||
def cancel_stock_reservation_entries(self, sre_list=None, notify=True) -> None:
|
||||
from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import (
|
||||
cancel_stock_reservation_entries,
|
||||
)
|
||||
|
||||
cancel_stock_reservation_entries(
|
||||
voucher_type=self.doctype, voucher_no=self.name, sre_list=sre_list, notify=notify
|
||||
)
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_subcontracting_receipt(source_name, target_doc=None):
|
||||
|
||||
@@ -4,5 +4,15 @@ from frappe import _
|
||||
def get_data():
|
||||
return {
|
||||
"fieldname": "subcontracting_order",
|
||||
"transactions": [{"label": _("Reference"), "items": ["Subcontracting Receipt", "Stock Entry"]}],
|
||||
"non_standard_fieldnames": {"Stock Reservation Entry": "voucher_no"},
|
||||
"transactions": [
|
||||
{
|
||||
"label": _("Reference"),
|
||||
"items": ["Subcontracting Receipt", "Stock Entry"],
|
||||
},
|
||||
{
|
||||
"label": _("Stock Reservation"),
|
||||
"items": ["Stock Reservation Entry"],
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -700,6 +700,126 @@ class TestSubcontractingOrder(IntegrationTestCase):
|
||||
|
||||
self.assertEqual(sco.supplied_items[0].required_qty, 210.149)
|
||||
|
||||
def test_stock_reservation(self):
|
||||
from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import (
|
||||
get_sre_details_for_voucher,
|
||||
)
|
||||
|
||||
service_items = [
|
||||
{
|
||||
"warehouse": "_Test Warehouse - _TC",
|
||||
"item_code": "Subcontracted Service Item 4",
|
||||
"qty": 10,
|
||||
"rate": 100,
|
||||
"fg_item": "Subcontracted Item SA4",
|
||||
"fg_item_qty": 10,
|
||||
}
|
||||
]
|
||||
|
||||
sco = get_subcontracting_order(service_items=service_items, do_not_submit=1)
|
||||
sco.reserve_stock = 1
|
||||
|
||||
rm_items = get_rm_items(sco.supplied_items)
|
||||
make_stock_in_entry(rm_items=rm_items)
|
||||
sco.submit()
|
||||
|
||||
sre_list = get_sre_details_for_voucher("Subcontracting Order", sco.name)
|
||||
self.assertTrue(len(sre_list) > 0)
|
||||
|
||||
se_dict = make_rm_stock_entry(sco.name)
|
||||
se = frappe.get_doc(se_dict)
|
||||
se.items[-1].use_serial_batch_fields = 1
|
||||
se.save()
|
||||
se.submit()
|
||||
sco.reload()
|
||||
|
||||
for sre in sre_list:
|
||||
self.assertEqual(frappe.get_value("Stock Reservation Entry", sre.name, "status"), "Closed")
|
||||
|
||||
make_subcontracting_receipt(sco.name).submit()
|
||||
for status in frappe.get_all(
|
||||
"Stock Reservation Entry", filters={"voucher_no": sco.name, "docstatus": 1}, pluck="status"
|
||||
)[:3]:
|
||||
self.assertEqual(status, "Delivered")
|
||||
|
||||
def test_stock_reservation_transfer(self):
|
||||
from erpnext.manufacturing.doctype.production_plan.production_plan import (
|
||||
get_items_for_material_requests,
|
||||
)
|
||||
from erpnext.manufacturing.doctype.production_plan.test_production_plan import create_production_plan
|
||||
from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import (
|
||||
get_serial_batch_entries_for_voucher,
|
||||
get_sre_details_for_voucher,
|
||||
)
|
||||
|
||||
parent_fg = make_item()
|
||||
make_bom(
|
||||
item=parent_fg.name, raw_materials=["Subcontracted Item SA10"], rate=100, rm_qty=1, currency="INR"
|
||||
)
|
||||
|
||||
plan = create_production_plan(
|
||||
item_code=parent_fg.name,
|
||||
planned_qty=10,
|
||||
do_not_submit=True,
|
||||
reserve_stock=True,
|
||||
skip_available_sub_assembly_item=True,
|
||||
for_warehouse="_Test Warehouse - _TC",
|
||||
sub_assembly_warehouse="_Test Warehouse - _TC",
|
||||
skip_getting_mr_items=True,
|
||||
)
|
||||
plan.get_sub_assembly_items()
|
||||
plan.sub_assembly_items[0].supplier = "_Test Supplier"
|
||||
mr_items = get_items_for_material_requests(plan.as_dict())
|
||||
for d in mr_items:
|
||||
plan.append("mr_items", d)
|
||||
|
||||
make_stock_entry(
|
||||
target="_Test Warehouse - _TC", item_code="Subcontracted SRM Item 1", qty=10, basic_rate=100
|
||||
)
|
||||
make_stock_entry(
|
||||
target="_Test Warehouse - _TC", item_code="Subcontracted SRM Item 2", qty=10, basic_rate=100
|
||||
)
|
||||
make_stock_entry(
|
||||
target="_Test Warehouse - _TC", item_code="Subcontracted SRM Item 3", qty=10, basic_rate=100
|
||||
)
|
||||
plan.submit()
|
||||
|
||||
sre_against_plan = get_sre_details_for_voucher("Production Plan", plan.name)
|
||||
sbe_pp_list = []
|
||||
for sre in sre_against_plan:
|
||||
sbe_pp_list.append(
|
||||
sorted(
|
||||
get_serial_batch_entries_for_voucher(sre.name),
|
||||
key=lambda x: x.get("serial_no") or x.get("batch_no") or "",
|
||||
)
|
||||
)
|
||||
|
||||
plan.make_work_order()
|
||||
po = frappe.get_doc(
|
||||
"Purchase Order",
|
||||
frappe.get_value("Purchase Order Item", {"production_plan": plan.name}, "parent"),
|
||||
)
|
||||
po.items[0].item_code = "Subcontracted Service Item 4"
|
||||
po.items[0].qty = 10
|
||||
po.submit()
|
||||
so = create_subcontracting_order(po_name=po.name, do_not_save=1)
|
||||
so.supplier_warehouse = "_Test Warehouse 1 - _TC"
|
||||
so.reserve_stock = True
|
||||
so.submit()
|
||||
so.reload()
|
||||
|
||||
sre_against_so = get_sre_details_for_voucher("Subcontracting Order", so.name)
|
||||
sbe_so_list = []
|
||||
for sre in sre_against_so:
|
||||
sbe_so_list.append(
|
||||
sorted(
|
||||
get_serial_batch_entries_for_voucher(sre.name),
|
||||
key=lambda x: x.get("serial_no") or x.get("batch_no") or "",
|
||||
)
|
||||
)
|
||||
|
||||
self.assertEqual(sbe_pp_list, sbe_so_list)
|
||||
|
||||
|
||||
def create_subcontracting_order(**args):
|
||||
args = frappe._dict(args)
|
||||
|
||||
@@ -55,7 +55,8 @@
|
||||
"section_break_34",
|
||||
"purchase_order_item",
|
||||
"page_break",
|
||||
"subcontracting_conversion_factor"
|
||||
"subcontracting_conversion_factor",
|
||||
"production_plan_sub_assembly_item"
|
||||
],
|
||||
"fields": [
|
||||
{
|
||||
@@ -407,6 +408,16 @@
|
||||
"hidden": 1,
|
||||
"label": "Subcontracting Conversion Factor",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "production_plan_sub_assembly_item",
|
||||
"fieldtype": "Data",
|
||||
"hidden": 1,
|
||||
"label": "Production Plan Sub Assembly Item",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1,
|
||||
"report_hide": 1
|
||||
}
|
||||
],
|
||||
"grid_page_length": 50,
|
||||
@@ -414,7 +425,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2025-08-10 22:37:39.863628",
|
||||
"modified": "2025-11-03 12:29:45.156101",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Subcontracting",
|
||||
"name": "Subcontracting Order Item",
|
||||
|
||||
@@ -35,6 +35,7 @@ class SubcontractingOrderItem(Document):
|
||||
parent: DF.Data
|
||||
parentfield: DF.Data
|
||||
parenttype: DF.Data
|
||||
production_plan_sub_assembly_item: DF.Data | None
|
||||
project: DF.Link | None
|
||||
purchase_order_item: DF.Data | None
|
||||
qty: DF.Float
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
"section_break_13",
|
||||
"required_qty",
|
||||
"supplied_qty",
|
||||
"stock_reserved_qty",
|
||||
"column_break_16",
|
||||
"consumed_qty",
|
||||
"returned_qty",
|
||||
@@ -52,7 +53,7 @@
|
||||
{
|
||||
"fieldname": "stock_uom",
|
||||
"fieldtype": "Link",
|
||||
"label": "Stock Uom",
|
||||
"label": "Stock UOM",
|
||||
"options": "UOM",
|
||||
"read_only": 1
|
||||
},
|
||||
@@ -160,18 +161,29 @@
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval:parent.reserve_stock",
|
||||
"fieldname": "stock_reserved_qty",
|
||||
"fieldtype": "Float",
|
||||
"label": "Reserved Qty",
|
||||
"no_copy": 1,
|
||||
"non_negative": 1,
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"hide_toolbar": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-03-27 13:10:46.680164",
|
||||
"modified": "2025-10-30 16:00:43.379828",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Subcontracting",
|
||||
"name": "Subcontracting Order Supplied Item",
|
||||
"owner": "Administrator",
|
||||
"permissions": [],
|
||||
"row_format": "Dynamic",
|
||||
"sort_field": "creation",
|
||||
"sort_order": "DESC",
|
||||
"states": []
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ class SubcontractingOrderSuppliedItem(Document):
|
||||
reserve_warehouse: DF.Link | None
|
||||
returned_qty: DF.Float
|
||||
rm_item_code: DF.Link | None
|
||||
stock_reserved_qty: DF.Float
|
||||
stock_uom: DF.Link | None
|
||||
supplied_qty: DF.Float
|
||||
total_supplied_qty: DF.Float
|
||||
|
||||
@@ -164,6 +164,8 @@ class SubcontractingReceipt(SubcontractingController):
|
||||
|
||||
for table_name in ["items", "supplied_items"]:
|
||||
self.make_bundle_using_old_serial_batch_fields(table_name)
|
||||
|
||||
self.update_stock_reservation_entries()
|
||||
self.update_stock_ledger()
|
||||
self.make_gl_entries()
|
||||
self.repost_future_sle_and_gle()
|
||||
@@ -189,6 +191,7 @@ class SubcontractingReceipt(SubcontractingController):
|
||||
self.set_consumed_qty_in_subcontract_order()
|
||||
self.set_subcontracting_order_status(update_bin=False)
|
||||
self.update_stock_ledger()
|
||||
self.update_stock_reservation_entries()
|
||||
self.make_gl_entries_on_cancel()
|
||||
self.repost_future_sle_and_gle()
|
||||
self.update_status()
|
||||
@@ -199,7 +202,7 @@ class SubcontractingReceipt(SubcontractingController):
|
||||
def reset_raw_materials(self):
|
||||
self.supplied_items = []
|
||||
self.flags.reset_raw_materials = True
|
||||
self.create_raw_materials_supplied()
|
||||
self.create_raw_materials_supplied_or_received()
|
||||
|
||||
def validate_closed_subcontracting_order(self):
|
||||
for item in self.items:
|
||||
@@ -853,6 +856,17 @@ class SubcontractingReceipt(SubcontractingController):
|
||||
if frappe.db.get_single_value("Buying Settings", "auto_create_purchase_receipt"):
|
||||
make_purchase_receipt(self, save=True, notify=True)
|
||||
|
||||
def has_reserved_stock(self):
|
||||
from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import (
|
||||
get_sre_details_for_voucher,
|
||||
)
|
||||
|
||||
for item in self.supplied_items:
|
||||
if get_sre_details_for_voucher("Subcontracting Order", item.subcontracting_order):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
|
||||
@frappe.whitelist()
|
||||
def make_subcontract_return_against_rejected_warehouse(source_name):
|
||||
|
||||
Reference in New Issue
Block a user