Merge branch 'develop' into holiday-list-assignment

This commit is contained in:
Asmita Hase
2025-11-17 11:53:56 +05:30
committed by GitHub
82 changed files with 1700 additions and 600 deletions

View File

@@ -523,6 +523,9 @@ class PurchaseOrder(BuyingController):
if self.is_against_so(): if self.is_against_so():
self.update_status_updater() self.update_status_updater()
if self.is_against_pp():
self.update_status_updater_if_from_pp()
if self.has_drop_ship_item(): if self.has_drop_ship_item():
self.update_delivered_qty_in_sales_order() 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" "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): if target_doc and isinstance(target_doc, str):
target_doc = json.loads(target_doc) target_doc = json.loads(target_doc)
for key in ["service_items", "items", "supplied_items"]: for key in ["service_items", "items", "supplied_items"]:

View File

@@ -830,6 +830,7 @@
"fieldname": "production_plan", "fieldname": "production_plan",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Production Plan", "label": "Production Plan",
"no_copy": 1,
"options": "Production Plan", "options": "Production Plan",
"print_hide": 1, "print_hide": 1,
"read_only": 1 "read_only": 1
@@ -948,7 +949,7 @@
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2025-10-12 10:57:31.552812", "modified": "2025-10-30 16:51:56.761673",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Buying", "module": "Buying",
"name": "Purchase Order Item", "name": "Purchase Order Item",

View File

@@ -12,7 +12,6 @@ from frappe.utils import cint, flt, format_datetime, get_datetime
import erpnext import erpnext
from erpnext.stock.serial_batch_bundle import get_batches_from_bundle 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 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 ref.rate
and flt(d.rate) > ref.rate and flt(d.rate) > ref.rate
and doc.doctype in ("Delivery Note", "Sales Invoice") 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( frappe.throw(
_("Row # {0}: Rate cannot be greater than the rate used in {1} {2}").format( _("Row # {0}: Rate cannot be greater than the rate used in {1} {2}").format(

View File

@@ -524,7 +524,7 @@ class SellingController(StockController):
) )
if not self.get("return_against") or ( 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 self.get("is_return")
and not item_details.has_serial_no and not item_details.has_serial_no
and not item_details.has_batch_no and not item_details.has_batch_no
@@ -535,7 +535,10 @@ class SellingController(StockController):
if ( if (
not d.incoming_rate not d.incoming_rate
or self.is_internal_transfer() 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( d.incoming_rate = get_incoming_rate(
{ {
@@ -560,7 +563,7 @@ class SellingController(StockController):
not d.incoming_rate not d.incoming_rate
and self.get("return_against") and self.get("return_against")
and self.get("is_return") 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( d.incoming_rate = get_rate_for_return(
self.doctype, self.name, d.item_code, self.return_against, item_row=d self.doctype, self.name, d.item_code, self.return_against, item_row=d

View File

@@ -1645,6 +1645,128 @@ class StockController(AccountsController):
gl_entries.append(self.get_gl_dict(gl_entry, item=item)) 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() @frappe.whitelist()
def show_accounting_ledger_preview(company, doctype, docname): def show_accounting_ledger_preview(company, doctype, docname):

View File

@@ -497,11 +497,10 @@ class SubcontractingController(StockController):
if row.serial_no: if row.serial_no:
details.serial_no.extend(get_serial_nos(row.serial_no)) details.serial_no.extend(get_serial_nos(row.serial_no))
if row.batch_no:
elif row.batch_no:
details.batch_no[row.batch_no] += row.qty 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_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()) 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) 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): 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" doctype = "BOM Item" if not exploded_item else "BOM Explosion Item"
fields = [f"`tab{doctype}`.`stock_qty` / `tabBOM`.`quantity` as qty_consumed_per_unit"] fields = [f"`tab{doctype}`.`stock_qty` / `tabBOM`.`quantity` as qty_consumed_per_unit"]
@@ -559,7 +560,7 @@ class SubcontractingController(StockController):
"name": "bom_detail_no", "name": "bom_detail_no",
"source_warehouse": "reserve_warehouse", "source_warehouse": "reserve_warehouse",
} }
for field in [ fields_list = [
"item_code", "item_code",
"name", "name",
"rate", "rate",
@@ -568,7 +569,12 @@ class SubcontractingController(StockController):
"description", "description",
"item_name", "item_name",
"stock_uom", "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)}") fields.append(f"`tab{doctype}`.`{field}` As {alias_dict.get(field, field)}")
filters = [ filters = [
@@ -578,7 +584,19 @@ class SubcontractingController(StockController):
[doctype, "sourced_by_supplier", "=", 0], [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): def __update_reserve_warehouse(self, row, item):
if ( if (

View File

@@ -556,131 +556,6 @@ class SubcontractingInwardController:
item.basic_rate + (item.additional_cost / item.transfer_qty), item.precision("basic_rate") 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): def validate_receive_from_customer_cancel(self):
if self.purpose == "Receive from Customer": if self.purpose == "Receive from Customer":
for item in self.items: for item in self.items:

View File

@@ -1141,6 +1141,28 @@ class TestSubcontractingController(IntegrationTestCase):
itemwise_details.get(doc.items[0].item_code)["serial_no"][5:6], 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): def add_second_row_in_scr(scr):
item_dict = {} item_dict = {}
@@ -1308,7 +1330,12 @@ def make_subcontracted_items():
"Subcontracted Item SA7": {}, "Subcontracted Item SA7": {},
"Subcontracted Item SA8": {}, "Subcontracted Item SA8": {},
"Subcontracted Item SA9": {"stock_uom": "Litre"}, "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(): for item, properties in sub_contracted_items.items():
@@ -1360,6 +1387,7 @@ def make_service_items():
"Subcontracted Service Item 8": {}, "Subcontracted Service Item 8": {},
"Subcontracted Service Item 9": {}, "Subcontracted Service Item 9": {},
"Subcontracted Service Item 10": {}, "Subcontracted Service Item 10": {},
"Subcontracted Service Item 11": {},
} }
for item, properties in service_items.items(): 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 SA7": ["Subcontracted SRM Item 1"],
"Subcontracted Item SA8": ["Subcontracted SRM Item 8"], "Subcontracted Item SA8": ["Subcontracted SRM Item 8"],
"Subcontracted Item SA10": ["Subcontracted SRM Item 10"], "Subcontracted Item SA10": ["Subcontracted SRM Item 10"],
"Subcontracted Service Item 11": ["Top Level Parent"],
} }
for item_code, raw_materials in boms.items(): for item_code, raw_materials in boms.items():

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: frappe\n" "Project-Id-Version: frappe\n"
"Report-Msgid-Bugs-To: hello@frappe.io\n" "Report-Msgid-Bugs-To: hello@frappe.io\n"
"POT-Creation-Date: 2025-11-10 12:11+0000\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" "Last-Translator: hello@frappe.io\n"
"Language-Team: Arabic\n" "Language-Team: Arabic\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@@ -1050,7 +1050,7 @@ msgstr ""
#. Label of the abbr (Data) field in DocType 'Company' #. Label of the abbr (Data) field in DocType 'Company'
#: erpnext/setup/doctype/company/company.json #: erpnext/setup/doctype/company/company.json
msgid "Abbr" msgid "Abbr"
msgstr "اسم مختصر" msgstr ""
#. Label of the abbr (Data) field in DocType 'Item Attribute Value' #. Label of the abbr (Data) field in DocType 'Item Attribute Value'
#: erpnext/stock/doctype/item_attribute_value/item_attribute_value.json #: 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/purchase_taxes_and_charges/purchase_taxes_and_charges.json
#: erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json #: erpnext/accounts/doctype/sales_taxes_and_charges/sales_taxes_and_charges.json
msgid "Account Head" msgid "Account Head"
msgstr "رئيس حساب" msgstr ""
#. Label of the account_manager (Link) field in DocType 'Customer' #. Label of the account_manager (Link) field in DocType 'Customer'
#: erpnext/selling/doctype/customer/customer.json #: 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_item/subcontracting_receipt_item.json
#: erpnext/subcontracting/doctype/subcontracting_receipt_supplied_item/subcontracting_receipt_supplied_item.json #: erpnext/subcontracting/doctype/subcontracting_receipt_supplied_item/subcontracting_receipt_supplied_item.json
msgid "Accounting Details" msgid "Accounting Details"
msgstr "تفاصيل المحاسبة" msgstr ""
#. Name of a DocType #. Name of a DocType
#. Label of the accounting_dimension (Select) field in DocType 'Accounting #. Label of the accounting_dimension (Select) field in DocType 'Accounting
@@ -2150,7 +2150,7 @@ msgstr ""
#: erpnext/crm/doctype/opportunity/opportunity.json #: erpnext/crm/doctype/opportunity/opportunity.json
#: erpnext/crm/doctype/prospect/prospect.json #: erpnext/crm/doctype/prospect/prospect.json
msgid "Activities" msgid "Activities"
msgstr "أنشطة" msgstr ""
#. Name of a DocType #. Name of a DocType
#. Label of a Link in the Projects Workspace #. Label of a Link in the Projects Workspace
@@ -3009,7 +3009,7 @@ msgstr ""
#. Label of the advance_account (Link) field in DocType 'Party Account' #. Label of the advance_account (Link) field in DocType 'Party Account'
#: erpnext/accounts/doctype/party_account/party_account.json #: erpnext/accounts/doctype/party_account/party_account.json
msgid "Advance Account" msgid "Advance Account"
msgstr "حساب مقدم" msgstr ""
#: erpnext/utilities/transaction_base.py:215 #: erpnext/utilities/transaction_base.py:215
msgid "Advance Account: {0} must be in either customer billing currency: {1} or Company default currency: {2}" 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/buying/doctype/purchase_order/purchase_order.json
#: erpnext/selling/doctype/sales_order/sales_order.json #: erpnext/selling/doctype/sales_order/sales_order.json
msgid "Advance Paid" msgid "Advance Paid"
msgstr "مسبقا المدفوعة" msgstr ""
#: erpnext/buying/doctype/purchase_order/purchase_order_list.js:75 #: erpnext/buying/doctype/purchase_order/purchase_order_list.js:75
#: erpnext/selling/doctype/sales_order/sales_order_list.js:122 #: 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/purchase_invoice/purchase_invoice.json
#: erpnext/accounts/doctype/sales_invoice/sales_invoice.json #: erpnext/accounts/doctype/sales_invoice/sales_invoice.json
msgid "Advances" msgid "Advances"
msgstr "الدفعات المقدمة" msgstr ""
#: erpnext/setup/setup_wizard/data/marketing_source.txt:3 #: erpnext/setup/setup_wizard/data/marketing_source.txt:3
msgid "Advertisement" msgid "Advertisement"
@@ -6818,7 +6818,7 @@ msgstr "معلومات الحساب البنكي"
#: erpnext/accounts/doctype/payment_entry/payment_entry.json #: erpnext/accounts/doctype/payment_entry/payment_entry.json
#: erpnext/accounts/doctype/payment_request/payment_request.json #: erpnext/accounts/doctype/payment_request/payment_request.json
msgid "Bank Account No" msgid "Bank Account No"
msgstr "رقم الحساب البنكي" msgstr ""
#. Name of a DocType #. Name of a DocType
#: erpnext/accounts/doctype/bank_account_subtype/bank_account_subtype.json #: 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/accounts/doctype/cheque_print_template/cheque_print_template.json
#: erpnext/setup/doctype/employee/employee.json #: erpnext/setup/doctype/employee/employee.json
msgid "Bank Name" 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.py:98
#: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py:142 #: 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' #. Label of the start (Int) field in DocType 'Task'
#: erpnext/projects/doctype/task/task.json #: erpnext/projects/doctype/task/task.json
msgid "Begin On (Days)" msgid "Begin On (Days)"
msgstr "ابدأ (بالأيام)" msgstr ""
#. Option for the 'Generate Invoice At' (Select) field in DocType #. Option for the 'Generate Invoice At' (Select) field in DocType
#. 'Subscription' #. 'Subscription'
@@ -7795,7 +7795,7 @@ msgstr "فصيلة الدم"
#. Accounts' #. Accounts'
#: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json #: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json
msgid "Body" 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'
#. Label of the body_text (Text Editor) field in DocType 'Dunning Letter Text' #. Label of the body_text (Text Editor) field in DocType 'Dunning Letter Text'
@@ -10418,7 +10418,7 @@ msgstr "وصف الشركة"
#. 'Employee' #. 'Employee'
#: erpnext/setup/doctype/employee/employee.json #: erpnext/setup/doctype/employee/employee.json
msgid "Company Details" msgid "Company Details"
msgstr "تفاصيل الشركة" msgstr ""
#. Option for the 'Preferred Contact Email' (Select) field in DocType #. Option for the 'Preferred Contact Email' (Select) field in DocType
#. 'Employee' #. 'Employee'
@@ -11010,7 +11010,7 @@ msgstr "اسم جهة الاتصال"
#. Label of the contact_no (Data) field in DocType 'Sales Team' #. Label of the contact_no (Data) field in DocType 'Sales Team'
#: erpnext/selling/doctype/sales_team/sales_team.json #: erpnext/selling/doctype/sales_team/sales_team.json
msgid "Contact No." msgid "Contact No."
msgstr "الاتصال رقم" msgstr ""
#. Label of the contact_person (Link) field in DocType 'Dunning' #. Label of the contact_person (Link) field in DocType 'Dunning'
#. Label of the contact_person (Link) field in DocType 'POS Invoice' #. 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' #. Label of the cost (Currency) field in DocType 'Subscription Plan'
#: erpnext/accounts/doctype/subscription_plan/subscription_plan.json #: erpnext/accounts/doctype/subscription_plan/subscription_plan.json
msgid "Cost" 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 'Account Closing Balance'
#. Label of the cost_center (Link) field in DocType 'Advance Taxes and Charges' #. 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/manufacturing/doctype/bom_operation/bom_operation.json
#: erpnext/projects/doctype/task/task.json #: erpnext/projects/doctype/task/task.json
msgid "Costing" msgid "Costing"
msgstr "تكلف" msgstr ""
#. Label of the costing_amount (Currency) field in DocType 'Timesheet Detail' #. Label of the costing_amount (Currency) field in DocType 'Timesheet Detail'
#. Label of the base_costing_amount (Currency) field in DocType 'Timesheet #. 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' #. Label of the criteria (Table) field in DocType 'Supplier Scorecard Period'
#: erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.json #: erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.json
msgid "Criteria" msgid "Criteria"
msgstr "المعايير" msgstr ""
#. Label of the formula (Small Text) field in DocType 'Supplier Scorecard #. Label of the formula (Small Text) field in DocType 'Supplier Scorecard
#. Criteria' #. Criteria'
@@ -13499,7 +13499,7 @@ msgstr ""
#. Label of the date_of_birth (Date) field in DocType 'Employee' #. Label of the date_of_birth (Date) field in DocType 'Employee'
#: erpnext/setup/doctype/employee/employee.json #: erpnext/setup/doctype/employee/employee.json
msgid "Date of Birth" msgid "Date of Birth"
msgstr "تاريخ الميلاد" msgstr ""
#: erpnext/setup/doctype/employee/employee.py:147 #: erpnext/setup/doctype/employee/employee.py:147
msgid "Date of Birth cannot be greater than today." 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' #. Label of the date_of_joining (Date) field in DocType 'Employee'
#: erpnext/setup/doctype/employee/employee.json #: erpnext/setup/doctype/employee/employee.json
msgid "Date of Joining" msgid "Date of Joining"
msgstr "تاريخ الالتحاق بالعمل" msgstr ""
#: erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py:273 #: erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py:273
msgid "Date of Transaction" msgid "Date of Transaction"
@@ -13851,7 +13851,7 @@ msgstr "الخصومات أو الخسارة"
#: erpnext/accounts/doctype/mode_of_payment_account/mode_of_payment_account.json #: erpnext/accounts/doctype/mode_of_payment_account/mode_of_payment_account.json
#: erpnext/accounts/doctype/party_account/party_account.json #: erpnext/accounts/doctype/party_account/party_account.json
msgid "Default Account" msgid "Default Account"
msgstr "الافتراضي حساب" msgstr ""
#. Label of the default_accounts_section (Section Break) field in DocType #. Label of the default_accounts_section (Section Break) field in DocType
#. 'Supplier' #. 'Supplier'
@@ -14359,7 +14359,7 @@ msgstr "المصروفات المؤجلة"
#: erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json #: erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
#: erpnext/stock/doctype/item_default/item_default.json #: erpnext/stock/doctype/item_default/item_default.json
msgid "Deferred Expense Account" msgid "Deferred Expense Account"
msgstr "حساب المصروفات المؤجلة" msgstr ""
#. Option for the 'Entry Type' (Select) field in DocType 'Journal Entry' #. Option for the 'Entry Type' (Select) field in DocType 'Journal Entry'
#. Label of the deferred_revenue (Section Break) field in DocType 'POS Invoice #. 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/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.json
#: erpnext/stock/doctype/putaway_rule/putaway_rule.json #: erpnext/stock/doctype/putaway_rule/putaway_rule.json
msgid "Disable" msgid "Disable"
msgstr "تعطيل" msgstr ""
#. Label of the disable_capacity_planning (Check) field in DocType #. Label of the disable_capacity_planning (Check) field in DocType
#. 'Manufacturing Settings' #. 'Manufacturing Settings'
@@ -15292,7 +15292,7 @@ msgstr ""
#: erpnext/stock/doctype/delivery_note/delivery_note.json #: erpnext/stock/doctype/delivery_note/delivery_note.json
#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json #: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
msgid "Disable Rounded Total" msgid "Disable Rounded Total"
msgstr "تعطيل الاجمالي المقرب" msgstr ""
#. Label of the disable_serial_no_and_batch_selector (Check) field in DocType #. Label of the disable_serial_no_and_batch_selector (Check) field in DocType
#. 'Stock Settings' #. 'Stock Settings'
@@ -16207,7 +16207,7 @@ msgstr "مكرر {0} موجود في الجدول"
#. Label of the duration (Int) field in DocType 'Task' #. Label of the duration (Int) field in DocType 'Task'
#: erpnext/projects/doctype/task/task.json #: erpnext/projects/doctype/task/task.json
msgid "Duration (Days)" msgid "Duration (Days)"
msgstr "المدة (أيام)" msgstr ""
#: erpnext/crm/report/lead_conversion_time/lead_conversion_time.py:66 #: erpnext/crm/report/lead_conversion_time/lead_conversion_time.py:66
msgid "Duration in Days" msgid "Duration in Days"
@@ -16583,7 +16583,7 @@ msgstr "موظف"
#. Account' #. Account'
#: erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json #: erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
msgid "Employee Advance" 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.py:16
#: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py:23 #: 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' #. Label of the employee_number (Data) field in DocType 'Employee'
#: erpnext/setup/doctype/employee/employee.json #: erpnext/setup/doctype/employee/employee.json
msgid "Employee Number" msgid "Employee Number"
msgstr "رقم الموظف" msgstr ""
#. Label of the employee_user_id (Link) field in DocType 'Call Log' #. Label of the employee_user_id (Link) field in DocType 'Call Log'
#: erpnext/telephony/doctype/call_log/call_log.json #: erpnext/telephony/doctype/call_log/call_log.json
@@ -16672,7 +16672,7 @@ msgstr ""
#: erpnext/manufacturing/doctype/workstation/workstation.js:351 #: erpnext/manufacturing/doctype/workstation/workstation.js:351
msgid "Employees" msgid "Employees"
msgstr "الموظفين" msgstr ""
#: erpnext/stock/doctype/batch/batch_list.js:16 #: erpnext/stock/doctype/batch/batch_list.js:16
msgid "Empty" msgid "Empty"
@@ -16863,7 +16863,7 @@ msgstr ""
#. Label of the encashment_date (Date) field in DocType 'Employee' #. Label of the encashment_date (Date) field in DocType 'Employee'
#: erpnext/setup/doctype/employee/employee.json #: erpnext/setup/doctype/employee/employee.json
msgid "Encashment Date" msgid "Encashment Date"
msgstr "تاريخ التحصيل" msgstr ""
#: erpnext/crm/doctype/contract/contract.py:70 #: erpnext/crm/doctype/contract/contract.py:70
msgid "End Date cannot be before Start Date." msgid "End Date cannot be before Start Date."
@@ -16880,7 +16880,7 @@ msgstr "لا يمكن أن يكون تاريخ الانتهاء قبل تاري
#: erpnext/support/doctype/service_day/service_day.json #: erpnext/support/doctype/service_day/service_day.json
#: erpnext/telephony/doctype/call_log/call_log.json #: erpnext/telephony/doctype/call_log/call_log.json
msgid "End Time" msgid "End Time"
msgstr "وقت الانتهاء" msgstr ""
#: erpnext/stock/doctype/stock_entry/stock_entry.js:287 #: erpnext/stock/doctype/stock_entry/stock_entry.js:287
msgid "End Transit" 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/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.json
#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json #: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
msgid "Exchange Rate" msgid "Exchange Rate"
msgstr "سعر الصرف" msgstr ""
#. Name of a DocType #. Name of a DocType
#. Option for the 'Entry Type' (Select) field in DocType 'Journal Entry' #. Option for the 'Entry Type' (Select) field in DocType 'Journal Entry'
@@ -17582,7 +17582,7 @@ msgstr "حساب المصاريف مفقود"
#. Account' #. Account'
#: erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json #: erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
msgid "Expense Claim" msgid "Expense Claim"
msgstr "طلب النفقات" msgstr ""
#. Label of the expense_account (Link) field in DocType 'Purchase Invoice Item' #. Label of the expense_account (Link) field in DocType 'Purchase Invoice Item'
#: erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json #: 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' #. Label of the from_employee (Link) field in DocType 'Asset Movement Item'
#: erpnext/assets/doctype/asset_movement_item/asset_movement_item.json #: erpnext/assets/doctype/asset_movement_item/asset_movement_item.json
msgid "From Employee" msgid "From Employee"
msgstr "من الموظف" msgstr ""
#: erpnext/assets/doctype/asset_movement/asset_movement.py:85 #: erpnext/assets/doctype/asset_movement/asset_movement.py:85
msgid "From Employee is required while issuing Asset {0}" msgid "From Employee is required while issuing Asset {0}"
@@ -20526,7 +20526,7 @@ msgstr "اسم قائمة العطلات"
#. Label of the holidays (Table) field in DocType 'Holiday List' #. Label of the holidays (Table) field in DocType 'Holiday List'
#: erpnext/setup/doctype/holiday_list/holiday_list.json #: erpnext/setup/doctype/holiday_list/holiday_list.json
msgid "Holidays" msgid "Holidays"
msgstr "العطلات" msgstr ""
#. Option for the 'Forecasting Method' (Select) field in DocType 'Sales #. Option for the 'Forecasting Method' (Select) field in DocType 'Sales
#. Forecast' #. Forecast'
@@ -20556,7 +20556,7 @@ msgstr ""
#: erpnext/manufacturing/doctype/job_card/job_card.json #: erpnext/manufacturing/doctype/job_card/job_card.json
#: erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json #: erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json
msgid "Hour Rate" msgid "Hour Rate"
msgstr "سعرالساعة" msgstr ""
#. Label of the hours (Float) field in DocType 'Workstation Working Hour' #. Label of the hours (Float) field in DocType 'Workstation Working Hour'
#: erpnext/manufacturing/doctype/workstation_working_hour/workstation_working_hour.json #: erpnext/manufacturing/doctype/workstation_working_hour/workstation_working_hour.json
@@ -21293,7 +21293,7 @@ msgstr "في المئة"
#: erpnext/manufacturing/doctype/work_order/work_order.json #: erpnext/manufacturing/doctype/work_order/work_order.json
#: erpnext/stock/doctype/quality_inspection/quality_inspection.json #: erpnext/stock/doctype/quality_inspection/quality_inspection.json
msgid "In Process" msgid "In Process"
msgstr "في عملية" msgstr ""
#: erpnext/stock/report/item_variant_details/item_variant_details.py:107 #: erpnext/stock/report/item_variant_details/item_variant_details.py:107
msgid "In Production" msgid "In Production"
@@ -22156,7 +22156,7 @@ msgstr "إعدادات نقل المستودعات الداخلية"
#. Label of the interest (Currency) field in DocType 'Overdue Payment' #. Label of the interest (Currency) field in DocType 'Overdue Payment'
#: erpnext/accounts/doctype/overdue_payment/overdue_payment.json #: erpnext/accounts/doctype/overdue_payment/overdue_payment.json
msgid "Interest" msgid "Interest"
msgstr "فائدة" msgstr ""
#: erpnext/accounts/doctype/payment_entry/payment_entry.py:3052 #: erpnext/accounts/doctype/payment_entry/payment_entry.py:3052
msgid "Interest and/or dunning fee" msgid "Interest and/or dunning fee"
@@ -23001,7 +23001,7 @@ msgstr "هو المورد الداخلي"
#. Label of the is_mandatory (Check) field in DocType 'Applicable On Account' #. Label of the is_mandatory (Check) field in DocType 'Applicable On Account'
#: erpnext/accounts/doctype/applicable_on_account/applicable_on_account.json #: erpnext/accounts/doctype/applicable_on_account/applicable_on_account.json
msgid "Is Mandatory" msgid "Is Mandatory"
msgstr "إلزامي" msgstr ""
#. Label of the is_milestone (Check) field in DocType 'Task' #. Label of the is_milestone (Check) field in DocType 'Task'
#: erpnext/projects/doctype/task/task.json #: erpnext/projects/doctype/task/task.json
@@ -23055,7 +23055,7 @@ msgstr ""
#. Label of the is_paid (Check) field in DocType 'Purchase Invoice' #. Label of the is_paid (Check) field in DocType 'Purchase Invoice'
#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json #: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
msgid "Is Paid" msgid "Is Paid"
msgstr "مدفوع" msgstr ""
#. Label of the is_paused (Check) field in DocType 'Job Card' #. Label of the is_paused (Check) field in DocType 'Job Card'
#: erpnext/manufacturing/doctype/job_card/job_card.json #: erpnext/manufacturing/doctype/job_card/job_card.json
@@ -24992,7 +24992,7 @@ msgstr "بدأ العمل"
#: erpnext/crm/doctype/lead/lead.json #: erpnext/crm/doctype/lead/lead.json
#: erpnext/crm/doctype/opportunity/opportunity.json #: erpnext/crm/doctype/opportunity/opportunity.json
msgid "Job Title" msgid "Job Title"
msgstr "المسمى الوظيفي" msgstr ""
#. Label of the supplier (Link) field in DocType 'Subcontracting Order' #. Label of the supplier (Link) field in DocType 'Subcontracting Order'
#. Label of the supplier (Link) field in DocType 'Subcontracting Receipt' #. 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' #. Label of the license_plate (Data) field in DocType 'Vehicle'
#: erpnext/setup/doctype/vehicle/vehicle.json #: erpnext/setup/doctype/vehicle/vehicle.json
msgid "License Plate" msgid "License Plate"
msgstr "لوحة الترخيص" msgstr ""
#: erpnext/controllers/status_updater.py:459 #: erpnext/controllers/status_updater.py:459
msgid "Limit Crossed" msgid "Limit Crossed"
@@ -25911,7 +25911,7 @@ msgstr ""
#. Account' #. Account'
#: erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json #: erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
msgid "Loan" msgid "Loan"
msgstr "قرض" msgstr ""
#. Label of the loan_end_date (Date) field in DocType 'Invoice Discounting' #. Label of the loan_end_date (Date) field in DocType 'Invoice Discounting'
#: erpnext/accounts/doctype/invoice_discounting/invoice_discounting.json #: erpnext/accounts/doctype/invoice_discounting/invoice_discounting.json
@@ -28002,7 +28002,7 @@ msgstr "طريقة الدفع"
#. Label of the model (Data) field in DocType 'Vehicle' #. Label of the model (Data) field in DocType 'Vehicle'
#: erpnext/setup/doctype/vehicle/vehicle.json #: erpnext/setup/doctype/vehicle/vehicle.json
msgid "Model" msgid "Model"
msgstr "الموديل" msgstr ""
#. Label of the section_break_11 (Section Break) field in DocType 'POS Closing #. Label of the section_break_11 (Section Break) field in DocType 'POS Closing
#. Entry' #. Entry'
@@ -29597,7 +29597,7 @@ msgstr "قراءة عداد المسافات (الأخيرة)"
#. Label of the scheduled_confirmation_date (Date) field in DocType 'Employee' #. Label of the scheduled_confirmation_date (Date) field in DocType 'Employee'
#: erpnext/setup/doctype/employee/employee.json #: erpnext/setup/doctype/employee/employee.json
msgid "Offer Date" 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.py:29
#: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py:42 #: 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/setup/doctype/supplier_group/supplier_group.json
#: erpnext/stock/doctype/warehouse/warehouse.json #: erpnext/stock/doctype/warehouse/warehouse.json
msgid "Old Parent" msgid "Old Parent"
msgstr "الحساب الأب السابق" msgstr ""
#. Option for the 'Reconciliation Takes Effect On' (Select) field in DocType #. Option for the 'Reconciliation Takes Effect On' (Select) field in DocType
#. 'Company' #. 'Company'
@@ -30606,7 +30606,7 @@ msgstr "البند الأصلي"
#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json #: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
#: erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json #: erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json
msgid "Other Details" 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 'Asset'
#. Label of the other_info_tab (Tab Break) field in DocType 'Stock Entry' #. Label of the other_info_tab (Tab Break) field in DocType 'Stock Entry'
@@ -30639,7 +30639,7 @@ msgstr "تقارير أخرى"
#. 'Manufacturing Settings' #. 'Manufacturing Settings'
#: erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json #: erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json
msgid "Other Settings" msgid "Other Settings"
msgstr "اعدادات اخرى" msgstr ""
#. Name of a UOM #. Name of a UOM
#: erpnext/setup/setup_wizard/data/uom_data.json #: erpnext/setup/setup_wizard/data/uom_data.json
@@ -30928,7 +30928,7 @@ msgstr "زيادة الإنتاج للمبيعات وطلب العمل"
#. Option for the 'Current Address Is' (Select) field in DocType 'Employee' #. Option for the 'Current Address Is' (Select) field in DocType 'Employee'
#: erpnext/setup/doctype/employee/employee.json #: erpnext/setup/doctype/employee/employee.json
msgid "Owned" msgid "Owned"
msgstr "مملوك" msgstr ""
#: erpnext/accounts/report/sales_payment_summary/sales_payment_summary.js:29 #: erpnext/accounts/report/sales_payment_summary/sales_payment_summary.js:29
#: erpnext/accounts/report/sales_payment_summary/sales_payment_summary.py:23 #: 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' #. Label of the passport_number (Data) field in DocType 'Employee'
#: erpnext/setup/doctype/employee/employee.json #: erpnext/setup/doctype/employee/employee.json
msgid "Passport Number" msgid "Passport Number"
msgstr "رقم جواز السفر" msgstr ""
#. Option for the 'Status' (Select) field in DocType 'Subscription' #. Option for the 'Status' (Select) field in DocType 'Subscription'
#: erpnext/accounts/doctype/subscription/subscription.json #: 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_gateway_account/payment_gateway_account.json
#: erpnext/accounts/doctype/payment_request/payment_request.json #: erpnext/accounts/doctype/payment_request/payment_request.json
msgid "Payment Account" 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 'Overdue Payment'
#. Label of the payment_amount (Currency) field in DocType 'Payment Schedule' #. Label of the payment_amount (Currency) field in DocType 'Payment Schedule'
@@ -32768,7 +32768,7 @@ msgstr ""
#. Account' #. Account'
#: erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json #: erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
msgid "Payroll Entry" 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.py:88
#: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py:119 #: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py:119
@@ -35502,7 +35502,7 @@ msgstr ""
#. Request' #. Request'
#: erpnext/stock/doctype/material_request/material_request.json #: erpnext/stock/doctype/material_request/material_request.json
msgid "Printing Details" msgid "Printing Details"
msgstr "تفاصيل الطباعة" msgstr ""
#. Label of the printing_settings_section (Section Break) field in DocType #. Label of the printing_settings_section (Section Break) field in DocType
#. 'Dunning' #. 'Dunning'
@@ -37971,7 +37971,7 @@ msgstr "رفع طلب المواد عندما يصل المخزون إلى مس
#. Label of the complaint_raised_by (Data) field in DocType 'Warranty Claim' #. Label of the complaint_raised_by (Data) field in DocType 'Warranty Claim'
#: erpnext/support/doctype/warranty_claim/warranty_claim.json #: erpnext/support/doctype/warranty_claim/warranty_claim.json
msgid "Raised By" msgid "Raised By"
msgstr "التي أثارها" msgstr ""
#. Label of the raised_by (Data) field in DocType 'Issue' #. Label of the raised_by (Data) field in DocType 'Issue'
#: erpnext/support/doctype/issue/issue.json #: erpnext/support/doctype/issue/issue.json
@@ -39282,7 +39282,7 @@ msgstr "يجب أن يكون تاريخ الإصدار في المستقبل"
#. Label of the relieving_date (Date) field in DocType 'Employee' #. Label of the relieving_date (Date) field in DocType 'Employee'
#: erpnext/setup/doctype/employee/employee.json #: erpnext/setup/doctype/employee/employee.json
msgid "Relieving Date" msgid "Relieving Date"
msgstr "تاريخ المغادرة" msgstr ""
#: erpnext/public/js/bank_reconciliation_tool/dialog_manager.js:125 #: erpnext/public/js/bank_reconciliation_tool/dialog_manager.js:125
msgid "Remaining" msgid "Remaining"
@@ -40150,7 +40150,7 @@ msgstr "إعادة ضبط اتفاقية مستوى الخدمة."
#. Label of the resignation_letter_date (Date) field in DocType 'Employee' #. Label of the resignation_letter_date (Date) field in DocType 'Employee'
#: erpnext/setup/doctype/employee/employee.json #: erpnext/setup/doctype/employee/employee.json
msgid "Resignation Letter Date" msgid "Resignation Letter Date"
msgstr "تاريخ رسالة الإستقالة" msgstr ""
#. Label of the sb_00 (Section Break) field in DocType 'Quality Action' #. Label of the sb_00 (Section Break) field in DocType 'Quality Action'
#. Label of the resolution (Text Editor) 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/issue/issue.json
#: erpnext/support/doctype/warranty_claim/warranty_claim.json #: erpnext/support/doctype/warranty_claim/warranty_claim.json
msgid "Resolution Date" msgid "Resolution Date"
msgstr "تاريخ القرار" msgstr ""
#. Label of the section_break_19 (Section Break) field in DocType 'Issue' #. Label of the section_break_19 (Section Break) field in DocType 'Issue'
#. Label of the resolution_details (Text Editor) 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/issue/issue.json
#: erpnext/support/doctype/warranty_claim/warranty_claim.json #: erpnext/support/doctype/warranty_claim/warranty_claim.json
msgid "Resolution Details" msgid "Resolution Details"
msgstr "قرار تفاصيل" msgstr ""
#. Option for the 'Service Level Agreement Status' (Select) field in DocType #. Option for the 'Service Level Agreement Status' (Select) field in DocType
#. 'Issue' #. 'Issue'
@@ -40222,7 +40222,7 @@ msgstr "تم الحل"
#. Label of the resolved_by (Link) field in DocType 'Warranty Claim' #. Label of the resolved_by (Link) field in DocType 'Warranty Claim'
#: erpnext/support/doctype/warranty_claim/warranty_claim.json #: erpnext/support/doctype/warranty_claim/warranty_claim.json
msgid "Resolved By" msgid "Resolved By"
msgstr "حلها عن طريق" msgstr ""
#. Label of the response_by (Datetime) field in DocType 'Issue' #. Label of the response_by (Datetime) field in DocType 'Issue'
#: erpnext/support/doctype/issue/issue.json #: erpnext/support/doctype/issue/issue.json
@@ -40494,7 +40494,7 @@ msgstr ""
#: erpnext/selling/page/point_of_sale/pos_past_order_summary.js:138 #: erpnext/selling/page/point_of_sale/pos_past_order_summary.js:138
#: erpnext/stock/doctype/shipment/shipment.json #: erpnext/stock/doctype/shipment/shipment.json
msgid "Returned" msgid "Returned"
msgstr "تم إرجاعه" msgstr ""
#. Label of the returned_against (Data) field in DocType 'Serial and Batch #. Label of the returned_against (Data) field in DocType 'Serial and Batch
#. Bundle' #. Bundle'
@@ -40816,7 +40816,7 @@ msgstr "تقريب إجمالي"
#: erpnext/stock/doctype/delivery_note/delivery_note.json #: erpnext/stock/doctype/delivery_note/delivery_note.json
#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json #: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
msgid "Rounded Total (Company Currency)" 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 'POS Invoice'
#. Label of the rounding_adjustment (Currency) field in DocType 'Purchase #. Label of the rounding_adjustment (Currency) field in DocType 'Purchase
@@ -48069,7 +48069,7 @@ msgstr ""
#: erpnext/setup/doctype/driver/driver.json #: erpnext/setup/doctype/driver/driver.json
#: erpnext/setup/doctype/employee/employee.json #: erpnext/setup/doctype/employee/employee.json
msgid "Suspended" msgid "Suspended"
msgstr "معلق" msgstr ""
#: erpnext/selling/page/point_of_sale/pos_payment.js:442 #: erpnext/selling/page/point_of_sale/pos_payment.js:442
msgid "Switch Between Payment Modes" msgid "Switch Between Payment Modes"
@@ -48420,7 +48420,7 @@ msgstr "نوع المهمة"
#. Option for the '% Complete Method' (Select) field in DocType 'Project' #. Option for the '% Complete Method' (Select) field in DocType 'Project'
#: erpnext/projects/doctype/project/project.json #: erpnext/projects/doctype/project/project.json
msgid "Task Weight" msgid "Task Weight"
msgstr "وزن المهمة" msgstr ""
#: erpnext/projects/doctype/project_template/project_template.py:41 #: erpnext/projects/doctype/project_template/project_template.py:41
msgid "Task {0} depends on Task {1}. Please add Task {1} to the Tasks list." 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/accounts/doctype/payment_terms_template/payment_terms_template.json
#: erpnext/quality_management/doctype/quality_feedback_template/quality_feedback_template.json #: erpnext/quality_management/doctype/quality_feedback_template/quality_feedback_template.json
msgid "Template Name" msgid "Template Name"
msgstr "اسم القالب" msgstr ""
#. Label of the template_task (Data) field in DocType 'Task' #. Label of the template_task (Data) field in DocType 'Task'
#: erpnext/projects/doctype/task/task.json #: erpnext/projects/doctype/task/task.json
@@ -49037,7 +49037,7 @@ msgstr "تفاصيل الشروط"
#: erpnext/stock/doctype/material_request/material_request.json #: erpnext/stock/doctype/material_request/material_request.json
#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json #: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
msgid "Terms" msgid "Terms"
msgstr "الشروط" msgstr ""
#. Label of the terms_section_break (Section Break) field in DocType 'Purchase #. Label of the terms_section_break (Section Break) field in DocType 'Purchase
#. Order' #. Order'
@@ -50056,7 +50056,7 @@ msgstr "الوقت المطلوب (بالدقائق)"
#. Label of the time_sheet (Link) field in DocType 'Sales Invoice Timesheet' #. Label of the time_sheet (Link) field in DocType 'Sales Invoice Timesheet'
#: erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json #: erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json
msgid "Time Sheet" 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 'POS Invoice'
#. Label of the time_sheet_list (Section Break) field in DocType 'Sales #. 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' #. Label of the total_billed_hours (Float) field in DocType 'Timesheet'
#: erpnext/projects/doctype/timesheet/timesheet.json #: erpnext/projects/doctype/timesheet/timesheet.json
msgid "Total Billed Hours" 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 'POS Invoice'
#. Label of the total_billing_amount (Currency) field in DocType 'Sales #. 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' #. Label of the total_holidays (Int) field in DocType 'Holiday List'
#: erpnext/setup/doctype/holiday_list/holiday_list.json #: erpnext/setup/doctype/holiday_list/holiday_list.json
msgid "Total Holidays" msgid "Total Holidays"
msgstr "مجموع العطلات" msgstr ""
#: erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py:115 #: erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py:115
msgid "Total Income" msgid "Total Income"
@@ -51211,7 +51211,7 @@ msgstr "مجموع الضرائب"
#: erpnext/stock/doctype/delivery_note/delivery_note.json #: erpnext/stock/doctype/delivery_note/delivery_note.json
#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json #: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
msgid "Total Taxes and Charges" msgid "Total Taxes and Charges"
msgstr "مجموع الضرائب والرسوم" msgstr ""
#. Label of the base_total_taxes_and_charges (Currency) field in DocType #. Label of the base_total_taxes_and_charges (Currency) field in DocType
#. 'Payment Entry' #. 'Payment Entry'
@@ -51323,7 +51323,7 @@ msgstr ""
#: erpnext/manufacturing/doctype/workstation/workstation.json #: erpnext/manufacturing/doctype/workstation/workstation.json
#: erpnext/projects/doctype/timesheet/timesheet.json #: erpnext/projects/doctype/timesheet/timesheet.json
msgid "Total Working Hours" msgid "Total Working Hours"
msgstr "مجموع ساعات العمل" msgstr ""
#. Label of the total_workstation_time (Int) field in DocType 'Item Lead Time' #. Label of the total_workstation_time (Int) field in DocType 'Item Lead Time'
#: erpnext/stock/doctype/item_lead_time/item_lead_time.json #: erpnext/stock/doctype/item_lead_time/item_lead_time.json
@@ -53764,7 +53764,7 @@ msgstr ""
#: erpnext/setup/setup_wizard/data/marketing_source.txt:10 #: erpnext/setup/setup_wizard/data/marketing_source.txt:10
msgid "Walk In" msgid "Walk In"
msgstr "عميل غير مسجل" msgstr ""
#: erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js:4 #: erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js:4
msgid "Warehouse Capacity Summary" msgid "Warehouse Capacity Summary"

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: frappe\n" "Project-Id-Version: frappe\n"
"Report-Msgid-Bugs-To: hello@frappe.io\n" "Report-Msgid-Bugs-To: hello@frappe.io\n"
"POT-Creation-Date: 2025-11-10 12:11+0000\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" "Last-Translator: hello@frappe.io\n"
"Language-Team: Bosnian\n" "Language-Team: Bosnian\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@@ -32241,7 +32241,7 @@ msgstr "Plati / Uplata od"
#: erpnext/accounts/report/account_balance/account_balance.js:54 #: erpnext/accounts/report/account_balance/account_balance.js:54
#: erpnext/setup/doctype/party_type/party_type.json #: erpnext/setup/doctype/party_type/party_type.json
msgid "Payable" msgid "Payable"
msgstr "Plaća se" msgstr "Obaveze"
#: erpnext/accounts/report/accounts_payable/accounts_payable.js:39 #: erpnext/accounts/report/accounts_payable/accounts_payable.js:39
#: erpnext/accounts/report/accounts_receivable/accounts_receivable.py:1160 #: 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:194
#: erpnext/accounts/report/purchase_register/purchase_register.py:235 #: erpnext/accounts/report/purchase_register/purchase_register.py:235
msgid "Payable Account" msgid "Payable Account"
msgstr "Račun Plaćanja" msgstr "Račun Obaveza"
#. Name of a Workspace #. Name of a Workspace
#. Label of the payables (Check) field in DocType 'Email Digest' #. Label of the payables (Check) field in DocType 'Email Digest'

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: frappe\n" "Project-Id-Version: frappe\n"
"Report-Msgid-Bugs-To: hello@frappe.io\n" "Report-Msgid-Bugs-To: hello@frappe.io\n"
"POT-Creation-Date: 2025-11-10 12:11+0000\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" "Last-Translator: hello@frappe.io\n"
"Language-Team: Spanish\n" "Language-Team: Spanish\n"
"MIME-Version: 1.0\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_entry/payment_entry.json
#: erpnext/accounts/doctype/payment_request/payment_request.json #: erpnext/accounts/doctype/payment_request/payment_request.json
msgid "Bank Account No" msgid "Bank Account No"
msgstr "Número de Cuenta Bancaria" msgstr ""
#. Name of a DocType #. Name of a DocType
#: erpnext/accounts/doctype/bank_account_subtype/bank_account_subtype.json #: 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/accounts/doctype/cheque_print_template/cheque_print_template.json
#: erpnext/setup/doctype/employee/employee.json #: erpnext/setup/doctype/employee/employee.json
msgid "Bank Name" 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.py:98
#: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py:142 #: 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' #. Label of the start (Int) field in DocType 'Task'
#: erpnext/projects/doctype/task/task.json #: erpnext/projects/doctype/task/task.json
msgid "Begin On (Days)" msgid "Begin On (Days)"
msgstr "Comience el (días)" msgstr ""
#. Option for the 'Generate Invoice At' (Select) field in DocType #. Option for the 'Generate Invoice At' (Select) field in DocType
#. 'Subscription' #. 'Subscription'
@@ -11762,7 +11762,7 @@ msgstr ""
#: erpnext/manufacturing/doctype/bom_operation/bom_operation.json #: erpnext/manufacturing/doctype/bom_operation/bom_operation.json
#: erpnext/projects/doctype/task/task.json #: erpnext/projects/doctype/task/task.json
msgid "Costing" msgid "Costing"
msgstr "Presupuesto" msgstr ""
#. Label of the costing_amount (Currency) field in DocType 'Timesheet Detail' #. Label of the costing_amount (Currency) field in DocType 'Timesheet Detail'
#. Label of the base_costing_amount (Currency) field in DocType 'Timesheet #. 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' #. Label of the criteria (Table) field in DocType 'Supplier Scorecard Period'
#: erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.json #: erpnext/buying/doctype/supplier_scorecard_period/supplier_scorecard_period.json
msgid "Criteria" msgid "Criteria"
msgstr "Criterios" msgstr ""
#. Label of the formula (Small Text) field in DocType 'Supplier Scorecard #. Label of the formula (Small Text) field in DocType 'Supplier Scorecard
#. Criteria' #. Criteria'
@@ -13579,7 +13579,7 @@ msgstr "Importación de datos y configuraciones"
#. Label of the date (Date) field in DocType 'Bulk Transaction Log Detail' #. 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 #: erpnext/bulk_transaction/doctype/bulk_transaction_log_detail/bulk_transaction_log_detail.json
msgid "Date " msgid "Date "
msgstr "Fecha " msgstr ""
#: erpnext/assets/report/fixed_asset_register/fixed_asset_register.js:97 #: erpnext/assets/report/fixed_asset_register/fixed_asset_register.js:97
msgid "Date Based On" 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' #. Label of the date_of_birth (Date) field in DocType 'Employee'
#: erpnext/setup/doctype/employee/employee.json #: erpnext/setup/doctype/employee/employee.json
msgid "Date of Birth" msgid "Date of Birth"
msgstr "Fecha de nacimiento" msgstr ""
#: erpnext/setup/doctype/employee/employee.py:147 #: erpnext/setup/doctype/employee/employee.py:147
msgid "Date of Birth cannot be greater than today." 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' #. Label of the date_of_joining (Date) field in DocType 'Employee'
#: erpnext/setup/doctype/employee/employee.json #: erpnext/setup/doctype/employee/employee.json
msgid "Date of Joining" msgid "Date of Joining"
msgstr "Fecha de Ingreso" msgstr ""
#: erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py:273 #: erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py:273
msgid "Date of Transaction" 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/mode_of_payment_account/mode_of_payment_account.json
#: erpnext/accounts/doctype/party_account/party_account.json #: erpnext/accounts/doctype/party_account/party_account.json
msgid "Default Account" msgid "Default Account"
msgstr "Cuenta predeterminada" msgstr ""
#. Label of the default_accounts_section (Section Break) field in DocType #. Label of the default_accounts_section (Section Break) field in DocType
#. 'Supplier' #. 'Supplier'
@@ -14463,7 +14463,7 @@ msgstr "Gasto Diferido"
#: erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json #: erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
#: erpnext/stock/doctype/item_default/item_default.json #: erpnext/stock/doctype/item_default/item_default.json
msgid "Deferred Expense Account" msgid "Deferred Expense Account"
msgstr "Cuenta de Gastos Diferidos" msgstr ""
#. Option for the 'Entry Type' (Select) field in DocType 'Journal Entry' #. Option for the 'Entry Type' (Select) field in DocType 'Journal Entry'
#. Label of the deferred_revenue (Section Break) field in DocType 'POS Invoice #. 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/accounts/doctype/promotional_scheme_product_discount/promotional_scheme_product_discount.json
#: erpnext/stock/doctype/putaway_rule/putaway_rule.json #: erpnext/stock/doctype/putaway_rule/putaway_rule.json
msgid "Disable" msgid "Disable"
msgstr "Desactivar" msgstr ""
#. Label of the disable_capacity_planning (Check) field in DocType #. Label of the disable_capacity_planning (Check) field in DocType
#. 'Manufacturing Settings' #. 'Manufacturing Settings'
@@ -15396,7 +15396,7 @@ msgstr "Desactivar última tasa de compra"
#: erpnext/stock/doctype/delivery_note/delivery_note.json #: erpnext/stock/doctype/delivery_note/delivery_note.json
#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json #: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
msgid "Disable Rounded Total" msgid "Disable Rounded Total"
msgstr "Desactivar redondeo" msgstr ""
#. Label of the disable_serial_no_and_batch_selector (Check) field in DocType #. Label of the disable_serial_no_and_batch_selector (Check) field in DocType
#. 'Stock Settings' #. 'Stock Settings'
@@ -16311,7 +16311,7 @@ msgstr "Duplicado {0} encontrado en la tabla"
#. Label of the duration (Int) field in DocType 'Task' #. Label of the duration (Int) field in DocType 'Task'
#: erpnext/projects/doctype/task/task.json #: erpnext/projects/doctype/task/task.json
msgid "Duration (Days)" msgid "Duration (Days)"
msgstr "Duración (Días)" msgstr ""
#: erpnext/crm/report/lead_conversion_time/lead_conversion_time.py:66 #: erpnext/crm/report/lead_conversion_time/lead_conversion_time.py:66
msgid "Duration in Days" msgid "Duration in Days"
@@ -16687,7 +16687,7 @@ msgstr "Empleado"
#. Account' #. Account'
#: erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json #: erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
msgid "Employee Advance" 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.py:16
#: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py:23 #: 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' #. Label of the employee_number (Data) field in DocType 'Employee'
#: erpnext/setup/doctype/employee/employee.json #: erpnext/setup/doctype/employee/employee.json
msgid "Employee Number" msgid "Employee Number"
msgstr "Número de empleado" msgstr ""
#. Label of the employee_user_id (Link) field in DocType 'Call Log' #. Label of the employee_user_id (Link) field in DocType 'Call Log'
#: erpnext/telephony/doctype/call_log/call_log.json #: erpnext/telephony/doctype/call_log/call_log.json
@@ -16776,7 +16776,7 @@ msgstr ""
#: erpnext/manufacturing/doctype/workstation/workstation.js:351 #: erpnext/manufacturing/doctype/workstation/workstation.js:351
msgid "Employees" msgid "Employees"
msgstr "Empleados" msgstr ""
#: erpnext/stock/doctype/batch/batch_list.js:16 #: erpnext/stock/doctype/batch/batch_list.js:16
msgid "Empty" msgid "Empty"
@@ -16967,7 +16967,7 @@ msgstr ""
#. Label of the encashment_date (Date) field in DocType 'Employee' #. Label of the encashment_date (Date) field in DocType 'Employee'
#: erpnext/setup/doctype/employee/employee.json #: erpnext/setup/doctype/employee/employee.json
msgid "Encashment Date" msgid "Encashment Date"
msgstr "Fecha de Cobro" msgstr ""
#: erpnext/crm/doctype/contract/contract.py:70 #: erpnext/crm/doctype/contract/contract.py:70
msgid "End Date cannot be before Start Date." 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/landed_cost_taxes_and_charges/landed_cost_taxes_and_charges.json
#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json #: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
msgid "Exchange Rate" msgid "Exchange Rate"
msgstr "Tipo de cambio" msgstr ""
#. Name of a DocType #. Name of a DocType
#. Option for the 'Entry Type' (Select) field in DocType 'Journal Entry' #. Option for the 'Entry Type' (Select) field in DocType 'Journal Entry'
@@ -17688,7 +17688,7 @@ msgstr "Falta la cuenta de gastos"
#. Account' #. Account'
#: erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json #: erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
msgid "Expense Claim" msgid "Expense Claim"
msgstr "Reembolso de gastos" msgstr ""
#. Label of the expense_account (Link) field in DocType 'Purchase Invoice Item' #. Label of the expense_account (Link) field in DocType 'Purchase Invoice Item'
#: erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json #: 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' #. Label of the from_employee (Link) field in DocType 'Asset Movement Item'
#: erpnext/assets/doctype/asset_movement_item/asset_movement_item.json #: erpnext/assets/doctype/asset_movement_item/asset_movement_item.json
msgid "From Employee" msgid "From Employee"
msgstr "Desde Empleado" msgstr ""
#: erpnext/assets/doctype/asset_movement/asset_movement.py:85 #: erpnext/assets/doctype/asset_movement/asset_movement.py:85
msgid "From Employee is required while issuing Asset {0}" 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' #. Label of the holidays (Table) field in DocType 'Holiday List'
#: erpnext/setup/doctype/holiday_list/holiday_list.json #: erpnext/setup/doctype/holiday_list/holiday_list.json
msgid "Holidays" msgid "Holidays"
msgstr "Vacaciones" msgstr ""
#. Option for the 'Forecasting Method' (Select) field in DocType 'Sales #. Option for the 'Forecasting Method' (Select) field in DocType 'Sales
#. Forecast' #. Forecast'
@@ -20662,7 +20662,7 @@ msgstr "Hora"
#: erpnext/manufacturing/doctype/job_card/job_card.json #: erpnext/manufacturing/doctype/job_card/job_card.json
#: erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json #: erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json
msgid "Hour Rate" msgid "Hour Rate"
msgstr "Salario por hora" msgstr ""
#. Label of the hours (Float) field in DocType 'Workstation Working Hour' #. Label of the hours (Float) field in DocType 'Workstation Working Hour'
#: erpnext/manufacturing/doctype/workstation_working_hour/workstation_working_hour.json #: 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/manufacturing/doctype/work_order/work_order.json
#: erpnext/stock/doctype/quality_inspection/quality_inspection.json #: erpnext/stock/doctype/quality_inspection/quality_inspection.json
msgid "In Process" msgid "In Process"
msgstr "En Proceso" msgstr ""
#: erpnext/stock/report/item_variant_details/item_variant_details.py:107 #: erpnext/stock/report/item_variant_details/item_variant_details.py:107
msgid "In Production" msgid "In Production"
@@ -22262,7 +22262,7 @@ msgstr "Configuración de transferencia entre almacenes"
#. Label of the interest (Currency) field in DocType 'Overdue Payment' #. Label of the interest (Currency) field in DocType 'Overdue Payment'
#: erpnext/accounts/doctype/overdue_payment/overdue_payment.json #: erpnext/accounts/doctype/overdue_payment/overdue_payment.json
msgid "Interest" msgid "Interest"
msgstr "Interesar" msgstr ""
#: erpnext/accounts/doctype/payment_entry/payment_entry.py:3052 #: erpnext/accounts/doctype/payment_entry/payment_entry.py:3052
msgid "Interest and/or dunning fee" 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' #. Label of the is_mandatory (Check) field in DocType 'Applicable On Account'
#: erpnext/accounts/doctype/applicable_on_account/applicable_on_account.json #: erpnext/accounts/doctype/applicable_on_account/applicable_on_account.json
msgid "Is Mandatory" msgid "Is Mandatory"
msgstr "Es obligatorio" msgstr ""
#. Label of the is_milestone (Check) field in DocType 'Task' #. Label of the is_milestone (Check) field in DocType 'Task'
#: erpnext/projects/doctype/task/task.json #: erpnext/projects/doctype/task/task.json
@@ -23161,7 +23161,7 @@ msgstr ""
#. Label of the is_paid (Check) field in DocType 'Purchase Invoice' #. Label of the is_paid (Check) field in DocType 'Purchase Invoice'
#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json #: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
msgid "Is Paid" msgid "Is Paid"
msgstr "Está pagado" msgstr ""
#. Label of the is_paused (Check) field in DocType 'Job Card' #. Label of the is_paused (Check) field in DocType 'Job Card'
#: erpnext/manufacturing/doctype/job_card/job_card.json #: erpnext/manufacturing/doctype/job_card/job_card.json
@@ -25098,7 +25098,7 @@ msgstr "Trabajo comenzó"
#: erpnext/crm/doctype/lead/lead.json #: erpnext/crm/doctype/lead/lead.json
#: erpnext/crm/doctype/opportunity/opportunity.json #: erpnext/crm/doctype/opportunity/opportunity.json
msgid "Job Title" 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 Order'
#. Label of the supplier (Link) field in DocType 'Subcontracting Receipt' #. 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' #. Label of the license_plate (Data) field in DocType 'Vehicle'
#: erpnext/setup/doctype/vehicle/vehicle.json #: erpnext/setup/doctype/vehicle/vehicle.json
msgid "License Plate" msgid "License Plate"
msgstr "Matrículas" msgstr ""
#: erpnext/controllers/status_updater.py:459 #: erpnext/controllers/status_updater.py:459
msgid "Limit Crossed" msgid "Limit Crossed"
@@ -26017,7 +26017,7 @@ msgstr ""
#. Account' #. Account'
#: erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json #: erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
msgid "Loan" msgid "Loan"
msgstr "Préstamo" msgstr ""
#. Label of the loan_end_date (Date) field in DocType 'Invoice Discounting' #. Label of the loan_end_date (Date) field in DocType 'Invoice Discounting'
#: erpnext/accounts/doctype/invoice_discounting/invoice_discounting.json #: 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' #. Label of the model (Data) field in DocType 'Vehicle'
#: erpnext/setup/doctype/vehicle/vehicle.json #: erpnext/setup/doctype/vehicle/vehicle.json
msgid "Model" msgid "Model"
msgstr "Modelo" msgstr ""
#. Label of the section_break_11 (Section Break) field in DocType 'POS Closing #. Label of the section_break_11 (Section Break) field in DocType 'POS Closing
#. Entry' #. Entry'
@@ -29703,7 +29703,7 @@ msgstr "Valor del cuentakilómetros (Última)"
#. Label of the scheduled_confirmation_date (Date) field in DocType 'Employee' #. Label of the scheduled_confirmation_date (Date) field in DocType 'Employee'
#: erpnext/setup/doctype/employee/employee.json #: erpnext/setup/doctype/employee/employee.json
msgid "Offer Date" 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.py:29
#: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py:42 #: 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/setup/doctype/supplier_group/supplier_group.json
#: erpnext/stock/doctype/warehouse/warehouse.json #: erpnext/stock/doctype/warehouse/warehouse.json
msgid "Old Parent" msgid "Old Parent"
msgstr "Antiguo Padre" msgstr ""
#. Option for the 'Reconciliation Takes Effect On' (Select) field in DocType #. Option for the 'Reconciliation Takes Effect On' (Select) field in DocType
#. 'Company' #. 'Company'
@@ -30712,7 +30712,7 @@ msgstr "Artículo Original"
#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json #: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
#: erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json #: erpnext/subcontracting/doctype/subcontracting_receipt/subcontracting_receipt.json
msgid "Other Details" 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 'Asset'
#. Label of the other_info_tab (Tab Break) field in DocType 'Stock Entry' #. Label of the other_info_tab (Tab Break) field in DocType 'Stock Entry'
@@ -30745,7 +30745,7 @@ msgstr "Otros Reportes"
#. 'Manufacturing Settings' #. 'Manufacturing Settings'
#: erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json #: erpnext/manufacturing/doctype/manufacturing_settings/manufacturing_settings.json
msgid "Other Settings" msgid "Other Settings"
msgstr "Otros ajustes" msgstr ""
#. Name of a UOM #. Name of a UOM
#: erpnext/setup/setup_wizard/data/uom_data.json #: 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' #. Option for the 'Current Address Is' (Select) field in DocType 'Employee'
#: erpnext/setup/doctype/employee/employee.json #: erpnext/setup/doctype/employee/employee.json
msgid "Owned" 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.js:29
#: erpnext/accounts/report/sales_payment_summary/sales_payment_summary.py:23 #: 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' #. Label of the passport_number (Data) field in DocType 'Employee'
#: erpnext/setup/doctype/employee/employee.json #: erpnext/setup/doctype/employee/employee.json
msgid "Passport Number" msgid "Passport Number"
msgstr "Número de pasaporte" msgstr ""
#. Option for the 'Status' (Select) field in DocType 'Subscription' #. Option for the 'Status' (Select) field in DocType 'Subscription'
#: erpnext/accounts/doctype/subscription/subscription.json #: 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_gateway_account/payment_gateway_account.json
#: erpnext/accounts/doctype/payment_request/payment_request.json #: erpnext/accounts/doctype/payment_request/payment_request.json
msgid "Payment Account" 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 'Overdue Payment'
#. Label of the payment_amount (Currency) field in DocType 'Payment Schedule' #. Label of the payment_amount (Currency) field in DocType 'Payment Schedule'
@@ -32874,7 +32874,7 @@ msgstr ""
#. Account' #. Account'
#: erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json #: erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
msgid "Payroll Entry" 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.py:88
#: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py:119 #: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py:119
@@ -35608,7 +35608,7 @@ msgstr ""
#. Request' #. Request'
#: erpnext/stock/doctype/material_request/material_request.json #: erpnext/stock/doctype/material_request/material_request.json
msgid "Printing Details" msgid "Printing Details"
msgstr "Detalles de impresión" msgstr ""
#. Label of the printing_settings_section (Section Break) field in DocType #. Label of the printing_settings_section (Section Break) field in DocType
#. 'Dunning' #. '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' #. Label of the complaint_raised_by (Data) field in DocType 'Warranty Claim'
#: erpnext/support/doctype/warranty_claim/warranty_claim.json #: erpnext/support/doctype/warranty_claim/warranty_claim.json
msgid "Raised By" msgid "Raised By"
msgstr "Propuesto por" msgstr ""
#. Label of the raised_by (Data) field in DocType 'Issue' #. Label of the raised_by (Data) field in DocType 'Issue'
#: erpnext/support/doctype/issue/issue.json #: 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' #. Label of the relieving_date (Date) field in DocType 'Employee'
#: erpnext/setup/doctype/employee/employee.json #: erpnext/setup/doctype/employee/employee.json
msgid "Relieving Date" msgid "Relieving Date"
msgstr "Fecha de relevo" msgstr ""
#: erpnext/public/js/bank_reconciliation_tool/dialog_manager.js:125 #: erpnext/public/js/bank_reconciliation_tool/dialog_manager.js:125
msgid "Remaining" 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' #. Label of the resignation_letter_date (Date) field in DocType 'Employee'
#: erpnext/setup/doctype/employee/employee.json #: erpnext/setup/doctype/employee/employee.json
msgid "Resignation Letter Date" 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 sb_00 (Section Break) field in DocType 'Quality Action'
#. Label of the resolution (Text Editor) 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/issue/issue.json
#: erpnext/support/doctype/warranty_claim/warranty_claim.json #: erpnext/support/doctype/warranty_claim/warranty_claim.json
msgid "Resolution Date" msgid "Resolution Date"
msgstr "Fecha de resolución" msgstr ""
#. Label of the section_break_19 (Section Break) field in DocType 'Issue' #. Label of the section_break_19 (Section Break) field in DocType 'Issue'
#. Label of the resolution_details (Text Editor) 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/issue/issue.json
#: erpnext/support/doctype/warranty_claim/warranty_claim.json #: erpnext/support/doctype/warranty_claim/warranty_claim.json
msgid "Resolution Details" msgid "Resolution Details"
msgstr "Detalles de la resolución" msgstr ""
#. Option for the 'Service Level Agreement Status' (Select) field in DocType #. Option for the 'Service Level Agreement Status' (Select) field in DocType
#. 'Issue' #. 'Issue'
@@ -40328,7 +40328,7 @@ msgstr "Resuelto"
#. Label of the resolved_by (Link) field in DocType 'Warranty Claim' #. Label of the resolved_by (Link) field in DocType 'Warranty Claim'
#: erpnext/support/doctype/warranty_claim/warranty_claim.json #: erpnext/support/doctype/warranty_claim/warranty_claim.json
msgid "Resolved By" msgid "Resolved By"
msgstr "Resuelto por" msgstr ""
#. Label of the response_by (Datetime) field in DocType 'Issue' #. Label of the response_by (Datetime) field in DocType 'Issue'
#: erpnext/support/doctype/issue/issue.json #: erpnext/support/doctype/issue/issue.json
@@ -40600,7 +40600,7 @@ msgstr ""
#: erpnext/selling/page/point_of_sale/pos_past_order_summary.js:138 #: erpnext/selling/page/point_of_sale/pos_past_order_summary.js:138
#: erpnext/stock/doctype/shipment/shipment.json #: erpnext/stock/doctype/shipment/shipment.json
msgid "Returned" msgid "Returned"
msgstr "Devuelto" msgstr ""
#. Label of the returned_against (Data) field in DocType 'Serial and Batch #. Label of the returned_against (Data) field in DocType 'Serial and Batch
#. Bundle' #. Bundle'
@@ -48178,7 +48178,7 @@ msgstr ""
#: erpnext/setup/doctype/driver/driver.json #: erpnext/setup/doctype/driver/driver.json
#: erpnext/setup/doctype/employee/employee.json #: erpnext/setup/doctype/employee/employee.json
msgid "Suspended" msgid "Suspended"
msgstr "Suspendido" msgstr ""
#: erpnext/selling/page/point_of_sale/pos_payment.js:442 #: erpnext/selling/page/point_of_sale/pos_payment.js:442
msgid "Switch Between Payment Modes" msgid "Switch Between Payment Modes"
@@ -48529,7 +48529,7 @@ msgstr "Tipo de tarea"
#. Option for the '% Complete Method' (Select) field in DocType 'Project' #. Option for the '% Complete Method' (Select) field in DocType 'Project'
#: erpnext/projects/doctype/project/project.json #: erpnext/projects/doctype/project/project.json
msgid "Task Weight" msgid "Task Weight"
msgstr "Peso de la Tarea" msgstr ""
#: erpnext/projects/doctype/project_template/project_template.py:41 #: erpnext/projects/doctype/project_template/project_template.py:41
msgid "Task {0} depends on Task {1}. Please add Task {1} to the Tasks list." 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/accounts/doctype/payment_terms_template/payment_terms_template.json
#: erpnext/quality_management/doctype/quality_feedback_template/quality_feedback_template.json #: erpnext/quality_management/doctype/quality_feedback_template/quality_feedback_template.json
msgid "Template Name" msgid "Template Name"
msgstr "Nombre de Plantilla" msgstr ""
#. Label of the template_task (Data) field in DocType 'Task' #. Label of the template_task (Data) field in DocType 'Task'
#: erpnext/projects/doctype/task/task.json #: 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/material_request/material_request.json
#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json #: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
msgid "Terms" msgid "Terms"
msgstr "Términos." msgstr ""
#. Label of the terms_section_break (Section Break) field in DocType 'Purchase #. Label of the terms_section_break (Section Break) field in DocType 'Purchase
#. Order' #. Order'
@@ -50165,7 +50165,7 @@ msgstr "Tiempo requerido (en minutos)"
#. Label of the time_sheet (Link) field in DocType 'Sales Invoice Timesheet' #. Label of the time_sheet (Link) field in DocType 'Sales Invoice Timesheet'
#: erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json #: erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json
msgid "Time Sheet" 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 'POS Invoice'
#. Label of the time_sheet_list (Section Break) field in DocType 'Sales #. 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' #. Label of the total_billed_hours (Float) field in DocType 'Timesheet'
#: erpnext/projects/doctype/timesheet/timesheet.json #: erpnext/projects/doctype/timesheet/timesheet.json
msgid "Total Billed Hours" 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 'POS Invoice'
#. Label of the total_billing_amount (Currency) field in DocType 'Sales #. 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' #. Label of the total_holidays (Int) field in DocType 'Holiday List'
#: erpnext/setup/doctype/holiday_list/holiday_list.json #: erpnext/setup/doctype/holiday_list/holiday_list.json
msgid "Total Holidays" msgid "Total Holidays"
msgstr "Vacaciones Totales" msgstr ""
#: erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py:115 #: erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py:115
msgid "Total Income" msgid "Total Income"
@@ -51320,7 +51320,7 @@ msgstr "Impuesto Total"
#: erpnext/stock/doctype/delivery_note/delivery_note.json #: erpnext/stock/doctype/delivery_note/delivery_note.json
#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json #: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
msgid "Total Taxes and Charges" msgid "Total Taxes and Charges"
msgstr "Total Impuestos y Cargos" msgstr ""
#. Label of the base_total_taxes_and_charges (Currency) field in DocType #. Label of the base_total_taxes_and_charges (Currency) field in DocType
#. 'Payment Entry' #. 'Payment Entry'
@@ -51432,7 +51432,7 @@ msgstr ""
#: erpnext/manufacturing/doctype/workstation/workstation.json #: erpnext/manufacturing/doctype/workstation/workstation.json
#: erpnext/projects/doctype/timesheet/timesheet.json #: erpnext/projects/doctype/timesheet/timesheet.json
msgid "Total Working Hours" msgid "Total Working Hours"
msgstr "Horas de trabajo total" msgstr ""
#. Label of the total_workstation_time (Int) field in DocType 'Item Lead Time' #. Label of the total_workstation_time (Int) field in DocType 'Item Lead Time'
#: erpnext/stock/doctype/item_lead_time/item_lead_time.json #: 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 #: erpnext/setup/setup_wizard/data/marketing_source.txt:10
msgid "Walk In" msgid "Walk In"
msgstr "Entrar" msgstr ""
#: erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js:4 #: erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js:4
msgid "Warehouse Capacity Summary" msgid "Warehouse Capacity Summary"

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: frappe\n" "Project-Id-Version: frappe\n"
"Report-Msgid-Bugs-To: hello@frappe.io\n" "Report-Msgid-Bugs-To: hello@frappe.io\n"
"POT-Creation-Date: 2025-11-10 12:11+0000\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" "Last-Translator: hello@frappe.io\n"
"Language-Team: Persian\n" "Language-Team: Persian\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@@ -1435,7 +1435,7 @@ msgstr "حساب {0} متعلق به شرکت {1} نیست"
#: erpnext/accounts/doctype/account/account.py:541 #: erpnext/accounts/doctype/account/account.py:541
msgid "Account {0} exists in parent company {1}." msgid "Account {0} exists in parent company {1}."
msgstr "حساب {0} در شرکت مادر {1} وجود دارد." msgstr "حساب {0} در شرکت والد {1} وجود دارد."
#: erpnext/accounts/doctype/budget/budget.py:114 #: erpnext/accounts/doctype/budget/budget.py:114
msgid "Account {0} has been entered multiple times" 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:85
#: erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js:92 #: erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.js:92
msgid "Allocate" msgid "Allocate"
msgstr "" msgstr "تخصیص"
#. Label of the allocate_advances_automatically (Check) field in DocType 'POS #. Label of the allocate_advances_automatically (Check) field in DocType 'POS
#. Invoice' #. Invoice'
@@ -7703,7 +7703,7 @@ msgstr "بیوتکنولوژی"
#. Name of a DocType #. Name of a DocType
#: erpnext/accounts/doctype/bisect_accounting_statements/bisect_accounting_statements.json #: erpnext/accounts/doctype/bisect_accounting_statements/bisect_accounting_statements.json
msgid "Bisect Accounting Statements" msgid "Bisect Accounting Statements"
msgstr "" msgstr "صورت‌های حسابداری دوبخشی"
#: erpnext/accounts/doctype/bisect_accounting_statements/bisect_accounting_statements.js:9 #: erpnext/accounts/doctype/bisect_accounting_statements/bisect_accounting_statements.js:9
msgid "Bisect Left" msgid "Bisect Left"
@@ -7812,7 +7812,7 @@ msgstr "گروه خونی"
#. Accounts' #. Accounts'
#: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json #: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json
msgid "Body" 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'
#. Label of the body_text (Text Editor) field in DocType 'Dunning Letter Text' #. 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' #. Label of the warehouse_group (Link) field in DocType 'Item Reorder'
#: erpnext/stock/doctype/item_reorder/item_reorder.json #: erpnext/stock/doctype/item_reorder/item_reorder.json
msgid "Check in (group)" msgid "Check in (group)"
msgstr "اعلام حضور (گروهی)" msgstr "بررسی در (گروه)"
#. Description of the 'Must be Whole Number' (Check) field in DocType 'UOM' #. Description of the 'Must be Whole Number' (Check) field in DocType 'UOM'
#: erpnext/setup/doctype/uom/uom.json #: erpnext/setup/doctype/uom/uom.json
@@ -12413,7 +12413,7 @@ msgstr ""
#. Description of a DocType #. Description of a DocType
#: erpnext/setup/doctype/website_item_group/website_item_group.json #: erpnext/setup/doctype/website_item_group/website_item_group.json
msgid "Cross Listing of Item in multiple groups" msgid "Cross Listing of Item in multiple groups"
msgstr "" msgstr "لیست کردن متقابل آیتم‌ها در چندین گروه"
#. Name of a UOM #. Name of a UOM
#: erpnext/setup/setup_wizard/data/uom_data.json #: erpnext/setup/setup_wizard/data/uom_data.json
@@ -14376,7 +14376,7 @@ msgstr "هزینه معوق"
#: erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json #: erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
#: erpnext/stock/doctype/item_default/item_default.json #: erpnext/stock/doctype/item_default/item_default.json
msgid "Deferred Expense Account" msgid "Deferred Expense Account"
msgstr "حساب هزینه معوق" msgstr ""
#. Option for the 'Entry Type' (Select) field in DocType 'Journal Entry' #. Option for the 'Entry Type' (Select) field in DocType 'Journal Entry'
#. Label of the deferred_revenue (Section Break) field in DocType 'POS Invoice #. 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 #: erpnext/stock/report/incorrect_serial_and_batch_bundle/incorrect_serial_and_batch_bundle.js:28
msgid "Fix SABB Entry" msgid "Fix SABB Entry"
msgstr "" msgstr "رفع مشکل ثبت SABB"
#. Option for the 'Calculate Based On' (Select) field in DocType 'Shipping #. Option for the 'Calculate Based On' (Select) field in DocType 'Shipping
#. Rule' #. Rule'
@@ -20573,7 +20573,7 @@ msgstr "ساعت"
#: erpnext/manufacturing/doctype/job_card/job_card.json #: erpnext/manufacturing/doctype/job_card/job_card.json
#: erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json #: erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json
msgid "Hour Rate" msgid "Hour Rate"
msgstr "نرخ ساعت" msgstr ""
#. Label of the hours (Float) field in DocType 'Workstation Working Hour' #. Label of the hours (Float) field in DocType 'Workstation Working Hour'
#: erpnext/manufacturing/doctype/workstation_working_hour/workstation_working_hour.json #: erpnext/manufacturing/doctype/workstation_working_hour/workstation_working_hour.json
@@ -21311,7 +21311,7 @@ msgstr "در درصد"
#: erpnext/manufacturing/doctype/work_order/work_order.json #: erpnext/manufacturing/doctype/work_order/work_order.json
#: erpnext/stock/doctype/quality_inspection/quality_inspection.json #: erpnext/stock/doctype/quality_inspection/quality_inspection.json
msgid "In Process" msgid "In Process"
msgstr "در جریان" msgstr ""
#: erpnext/stock/report/item_variant_details/item_variant_details.py:107 #: erpnext/stock/report/item_variant_details/item_variant_details.py:107
msgid "In Production" msgid "In Production"
@@ -22361,7 +22361,7 @@ msgstr "ثبت‌های دفتر نامعتبر"
#: erpnext/assets/doctype/asset/asset.py:450 #: erpnext/assets/doctype/asset/asset.py:450
msgid "Invalid Net Purchase Amount" msgid "Invalid Net Purchase Amount"
msgstr "" msgstr "مبلغ خالص خرید نامعتبر است"
#: erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py:77 #: erpnext/accounts/doctype/pos_closing_entry/pos_closing_entry.py:77
#: erpnext/accounts/general_ledger.py:796 #: 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:891
#: erpnext/stock/doctype/stock_entry/stock_entry.py:913 #: erpnext/stock/doctype/stock_entry/stock_entry.py:913
msgid "Invalid Source and Target Warehouse" msgid "Invalid Source and Target Warehouse"
msgstr "" msgstr "انبار منبع و هدف نامعتبر"
#: erpnext/controllers/item_variant.py:145 #: erpnext/controllers/item_variant.py:145
msgid "Invalid Value" msgid "Invalid Value"
@@ -23073,7 +23073,7 @@ msgstr ""
#. Label of the is_paid (Check) field in DocType 'Purchase Invoice' #. Label of the is_paid (Check) field in DocType 'Purchase Invoice'
#: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json #: erpnext/accounts/doctype/purchase_invoice/purchase_invoice.json
msgid "Is Paid" msgid "Is Paid"
msgstr "پرداخت شده" msgstr ""
#. Label of the is_paused (Check) field in DocType 'Job Card' #. Label of the is_paused (Check) field in DocType 'Job Card'
#: erpnext/manufacturing/doctype/job_card/job_card.json #: erpnext/manufacturing/doctype/job_card/job_card.json
@@ -26776,7 +26776,7 @@ msgstr "شماره قطعه تولید کننده <b>{0}</b> نامعتبر اس
#. Description of a DocType #. Description of a DocType
#: erpnext/stock/doctype/manufacturer/manufacturer.json #: erpnext/stock/doctype/manufacturer/manufacturer.json
msgid "Manufacturers used in Items" msgid "Manufacturers used in Items"
msgstr "" msgstr "تولیدکنندگان مورد استفاده در آیتم‌ها"
#. Label of the work_order_details_section (Section Break) field in DocType #. Label of the work_order_details_section (Section Break) field in DocType
#. 'Production Plan Sub Assembly Item' #. 'Production Plan Sub Assembly Item'
@@ -28020,7 +28020,7 @@ msgstr "نحوه پرداخت‌ها"
#. Label of the model (Data) field in DocType 'Vehicle' #. Label of the model (Data) field in DocType 'Vehicle'
#: erpnext/setup/doctype/vehicle/vehicle.json #: erpnext/setup/doctype/vehicle/vehicle.json
msgid "Model" msgid "Model"
msgstr "مدل" msgstr ""
#. Label of the section_break_11 (Section Break) field in DocType 'POS Closing #. Label of the section_break_11 (Section Break) field in DocType 'POS Closing
#. Entry' #. Entry'
@@ -28491,7 +28491,7 @@ msgstr "مبلغ خالص خرید"
#: erpnext/assets/doctype/asset/asset.py:385 #: erpnext/assets/doctype/asset/asset.py:385
msgid "Net Purchase Amount is mandatory" msgid "Net Purchase Amount is mandatory"
msgstr "" msgstr "مبلغ خالص خرید الزامی است"
#: erpnext/assets/doctype/asset/asset.py:445 #: erpnext/assets/doctype/asset/asset.py:445
msgid "Net Purchase Amount should be <b>equal</b> to purchase amount of one single Asset." 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/setup/doctype/supplier_group/supplier_group.json
#: erpnext/stock/doctype/warehouse/warehouse.json #: erpnext/stock/doctype/warehouse/warehouse.json
msgid "Old Parent" msgid "Old Parent"
msgstr "مرجع پیشین" msgstr ""
#. Option for the 'Reconciliation Takes Effect On' (Select) field in DocType #. Option for the 'Reconciliation Takes Effect On' (Select) field in DocType
#. 'Company' #. 'Company'
@@ -31522,7 +31522,7 @@ msgstr "دسته والد"
#. Label of the parent_company (Link) field in DocType 'Company' #. Label of the parent_company (Link) field in DocType 'Company'
#: erpnext/setup/doctype/company/company.json #: erpnext/setup/doctype/company/company.json
msgid "Parent Company" msgid "Parent Company"
msgstr "شرکت مادر" msgstr "شرکت والد"
#: erpnext/setup/doctype/company/company.py:555 #: erpnext/setup/doctype/company/company.py:555
msgid "Parent Company must be a group company" 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_gateway_account/payment_gateway_account.json
#: erpnext/accounts/doctype/payment_request/payment_request.json #: erpnext/accounts/doctype/payment_request/payment_request.json
msgid "Payment Account" 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 'Overdue Payment'
#. Label of the payment_amount (Currency) field in DocType 'Payment Schedule' #. Label of the payment_amount (Currency) field in DocType 'Payment Schedule'
@@ -33687,7 +33687,7 @@ msgstr "لطفا بیش از 500 آیتم را همزمان ایجاد نکنی
#: erpnext/accounts/doctype/budget/budget.py:133 #: erpnext/accounts/doctype/budget/budget.py:133
msgid "Please enable Applicable on Booking Actual Expenses" msgid "Please enable Applicable on Booking Actual Expenses"
msgstr "لطفاً Applicable on Booking Actual Expenses را فعال کنید" msgstr ""
#: erpnext/accounts/doctype/budget/budget.py:129 #: erpnext/accounts/doctype/budget/budget.py:129
msgid "Please enable Applicable on Purchase Order and Applicable on Booking Actual Expenses" 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 #: 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." 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 #: 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." 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 #: erpnext/stock/report/incorrect_serial_and_batch_bundle/incorrect_serial_and_batch_bundle.js:33
msgid "Please select at least one row to fix" msgid "Please select at least one row to fix"
msgstr "" msgstr "لطفا حداقل یک ردیف را برای اصلاح انتخاب کنید"
#: erpnext/selling/doctype/sales_order/sales_order.js:1274 #: erpnext/selling/doctype/sales_order/sales_order.js:1274
msgid "Please select atleast one item to continue" msgid "Please select atleast one item to continue"
@@ -35748,7 +35748,7 @@ msgstr "فرآیندها"
#. Voucher Detail' #. Voucher Detail'
#: erpnext/accounts/doctype/process_period_closing_voucher_detail/process_period_closing_voucher_detail.json #: erpnext/accounts/doctype/process_period_closing_voucher_detail/process_period_closing_voucher_detail.json
msgid "Processing Date" msgid "Processing Date"
msgstr "" msgstr "تاریخ پردازش"
#: erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py:52 #: erpnext/regional/doctype/import_supplier_invoice/import_supplier_invoice.py:52
msgid "Processing XML Files" msgid "Processing XML Files"
@@ -37990,7 +37990,7 @@ msgstr "ایجاد درخواست مواد زمانی که موجودی به س
#. Label of the complaint_raised_by (Data) field in DocType 'Warranty Claim' #. Label of the complaint_raised_by (Data) field in DocType 'Warranty Claim'
#: erpnext/support/doctype/warranty_claim/warranty_claim.json #: erpnext/support/doctype/warranty_claim/warranty_claim.json
msgid "Raised By" msgid "Raised By"
msgstr "مطرح شده توسط" msgstr ""
#. Label of the raised_by (Data) field in DocType 'Issue' #. Label of the raised_by (Data) field in DocType 'Issue'
#: erpnext/support/doctype/issue/issue.json #: erpnext/support/doctype/issue/issue.json
@@ -39160,11 +39160,11 @@ msgstr "منابع"
#: erpnext/stock/doctype/delivery_note/delivery_note.py:387 #: erpnext/stock/doctype/delivery_note/delivery_note.py:387
msgid "References to Sales Invoices are Incomplete" msgid "References to Sales Invoices are Incomplete"
msgstr "" msgstr "ارجاعات به فاکتورهای فروش ناقص است"
#: erpnext/stock/doctype/delivery_note/delivery_note.py:382 #: erpnext/stock/doctype/delivery_note/delivery_note.py:382
msgid "References to Sales Orders are Incomplete" msgid "References to Sales Orders are Incomplete"
msgstr "" msgstr "ارجاعات به سفارش‌های فروش ناقص است"
#: erpnext/accounts/doctype/payment_entry/payment_entry.py:733 #: 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." 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' #. Label of the resignation_letter_date (Date) field in DocType 'Employee'
#: erpnext/setup/doctype/employee/employee.json #: erpnext/setup/doctype/employee/employee.json
msgid "Resignation Letter Date" msgid "Resignation Letter Date"
msgstr "تاریخ استعفا نامه" msgstr ""
#. Label of the sb_00 (Section Break) field in DocType 'Quality Action' #. Label of the sb_00 (Section Break) field in DocType 'Quality Action'
#. Label of the resolution (Text Editor) 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/issue/issue.json
#: erpnext/support/doctype/warranty_claim/warranty_claim.json #: erpnext/support/doctype/warranty_claim/warranty_claim.json
msgid "Resolution Date" msgid "Resolution Date"
msgstr "تاریخ حل و فصل" msgstr ""
#. Label of the section_break_19 (Section Break) field in DocType 'Issue' #. Label of the section_break_19 (Section Break) field in DocType 'Issue'
#. Label of the resolution_details (Text Editor) 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' #. Label of the resolved_by (Link) field in DocType 'Warranty Claim'
#: erpnext/support/doctype/warranty_claim/warranty_claim.json #: erpnext/support/doctype/warranty_claim/warranty_claim.json
msgid "Resolved By" msgid "Resolved By"
msgstr "حل شده توسط" msgstr ""
#. Label of the response_by (Datetime) field in DocType 'Issue' #. Label of the response_by (Datetime) field in DocType 'Issue'
#: erpnext/support/doctype/issue/issue.json #: erpnext/support/doctype/issue/issue.json
@@ -40513,7 +40513,7 @@ msgstr ""
#: erpnext/selling/page/point_of_sale/pos_past_order_summary.js:138 #: erpnext/selling/page/point_of_sale/pos_past_order_summary.js:138
#: erpnext/stock/doctype/shipment/shipment.json #: erpnext/stock/doctype/shipment/shipment.json
msgid "Returned" msgid "Returned"
msgstr "بازگشت" msgstr ""
#. Label of the returned_against (Data) field in DocType 'Serial and Batch #. Label of the returned_against (Data) field in DocType 'Serial and Batch
#. Bundle' #. Bundle'
@@ -40835,7 +40835,7 @@ msgstr "مجموع گرد شده"
#: erpnext/stock/doctype/delivery_note/delivery_note.json #: erpnext/stock/doctype/delivery_note/delivery_note.json
#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json #: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
msgid "Rounded Total (Company Currency)" 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 'POS Invoice'
#. Label of the rounding_adjustment (Currency) field in DocType 'Purchase #. 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 #: erpnext/stock/doctype/landed_cost_voucher/landed_cost_voucher.py:102
msgid "Row {0}: {2} Item {1} does not exist in {2} {3}" 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 #: erpnext/utilities/transaction_base.py:558
msgid "Row {1}: Quantity ({0}) cannot be a fraction. To allow this, disable '{2}' in UOM {3}." 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' #. Label of the salary_currency (Link) field in DocType 'Employee'
#: erpnext/setup/doctype/employee/employee.json #: erpnext/setup/doctype/employee/employee.json
msgid "Salary Currency" msgid "Salary Currency"
msgstr "ارز حقوق و دستمزد" msgstr ""
#. Label of the salary_mode (Select) field in DocType 'Employee' #. Label of the salary_mode (Select) field in DocType 'Employee'
#: erpnext/setup/doctype/employee/employee.json #: erpnext/setup/doctype/employee/employee.json
@@ -43579,7 +43579,7 @@ msgstr "حسابی را برای چاپ با ارز حساب انتخاب کنی
#: erpnext/selling/page/point_of_sale/pos_past_order_summary.js:19 #: erpnext/selling/page/point_of_sale/pos_past_order_summary.js:19
msgid "Select an invoice to load summary data" msgid "Select an invoice to load summary data"
msgstr "" msgstr "برای بارگیری خلاصه داده‌ها، فاکتور را انتخاب کنید"
#: erpnext/selling/doctype/quotation/quotation.js:340 #: erpnext/selling/doctype/quotation/quotation.js:340
msgid "Select an item from each set to be used in the Sales Order." 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 #: 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." 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' #. Label of the serial_no_series (Data) field in DocType 'Item'
#: erpnext/stock/doctype/item/item.json #: erpnext/stock/doctype/item/item.json
@@ -44443,7 +44443,7 @@ msgstr "کل مبلغ هزینه خدمات"
#. 'Asset Capitalization' #. 'Asset Capitalization'
#: erpnext/assets/doctype/asset_capitalization/asset_capitalization.json #: erpnext/assets/doctype/asset_capitalization/asset_capitalization.json
msgid "Service Expenses" msgid "Service Expenses"
msgstr "هزینه های خدمات" msgstr ""
#. Label of the service_item (Link) field in DocType 'Subcontracting BOM' #. Label of the service_item (Link) field in DocType 'Subcontracting BOM'
#: erpnext/subcontracting/doctype/subcontracting_bom/subcontracting_bom.json #: erpnext/subcontracting/doctype/subcontracting_bom/subcontracting_bom.json
@@ -44679,7 +44679,7 @@ msgstr "تنظیم هزینه عملیاتی بر اساس مقدار BOM"
#: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:88 #: erpnext/manufacturing/doctype/bom_creator/bom_creator.py:88
msgid "Set Parent Row No in Items Table" msgid "Set Parent Row No in Items Table"
msgstr "" msgstr "تنظیم شماره ردیف والد در جدول آیتم‌ها"
#. Label of the set_posting_date (Check) field in DocType 'POS Opening Entry' #. Label of the set_posting_date (Check) field in DocType 'POS Opening Entry'
#: erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.json #: erpnext/accounts/doctype/pos_opening_entry/pos_opening_entry.json
@@ -48089,7 +48089,7 @@ msgstr ""
#: erpnext/setup/doctype/driver/driver.json #: erpnext/setup/doctype/driver/driver.json
#: erpnext/setup/doctype/employee/employee.json #: erpnext/setup/doctype/employee/employee.json
msgid "Suspended" msgid "Suspended"
msgstr "معلق" msgstr ""
#: erpnext/selling/page/point_of_sale/pos_payment.js:442 #: erpnext/selling/page/point_of_sale/pos_payment.js:442
msgid "Switch Between Payment Modes" msgid "Switch Between Payment Modes"
@@ -48372,7 +48372,7 @@ msgstr ""
#: erpnext/manufacturing/doctype/work_order/work_order.py:741 #: erpnext/manufacturing/doctype/work_order/work_order.py:741
msgid "Target Warehouse is required before Submit" msgid "Target Warehouse is required before Submit"
msgstr "" msgstr "انبار هدف قبل از ارسال الزامی است"
#: erpnext/controllers/selling_controller.py:840 #: erpnext/controllers/selling_controller.py:840
msgid "Target Warehouse is set for some items but the customer is not an internal customer." 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' #. Label of the time_sheet (Link) field in DocType 'Sales Invoice Timesheet'
#: erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json #: erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json
msgid "Time Sheet" 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 'POS Invoice'
#. Label of the time_sheet_list (Section Break) field in DocType 'Sales #. 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' #. Label of the total_billed_hours (Float) field in DocType 'Timesheet'
#: erpnext/projects/doctype/timesheet/timesheet.json #: erpnext/projects/doctype/timesheet/timesheet.json
msgid "Total Billed Hours" 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 'POS Invoice'
#. Label of the total_billing_amount (Currency) field in DocType 'Sales #. 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' #. Label of the total_holidays (Int) field in DocType 'Holiday List'
#: erpnext/setup/doctype/holiday_list/holiday_list.json #: erpnext/setup/doctype/holiday_list/holiday_list.json
msgid "Total Holidays" msgid "Total Holidays"
msgstr "کل تعطیلات" msgstr ""
#: erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py:115 #: erpnext/accounts/report/profit_and_loss_statement/profit_and_loss_statement.py:115
msgid "Total Income" msgid "Total Income"
@@ -51231,7 +51231,7 @@ msgstr "کل مالیات"
#: erpnext/stock/doctype/delivery_note/delivery_note.json #: erpnext/stock/doctype/delivery_note/delivery_note.json
#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json #: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
msgid "Total Taxes and Charges" msgid "Total Taxes and Charges"
msgstr "کل مالیات ها و هزینه ها" msgstr ""
#. Label of the base_total_taxes_and_charges (Currency) field in DocType #. Label of the base_total_taxes_and_charges (Currency) field in DocType
#. 'Payment Entry' #. 'Payment Entry'
@@ -52174,7 +52174,7 @@ msgstr "واحد"
#: erpnext/controllers/accounts_controller.py:3830 #: erpnext/controllers/accounts_controller.py:3830
msgid "Unit Price" msgid "Unit Price"
msgstr "" msgstr "قیمت واحد"
#: erpnext/buying/report/procurement_tracker/procurement_tracker.py:68 #: erpnext/buying/report/procurement_tracker/procurement_tracker.py:68
msgid "Unit of Measure" msgid "Unit of Measure"

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: frappe\n" "Project-Id-Version: frappe\n"
"Report-Msgid-Bugs-To: hello@frappe.io\n" "Report-Msgid-Bugs-To: hello@frappe.io\n"
"POT-Creation-Date: 2025-11-10 12:11+0000\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" "Last-Translator: hello@frappe.io\n"
"Language-Team: French\n" "Language-Team: French\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@@ -820,7 +820,7 @@ msgstr ""
#. Header text in the Stock Workspace #. Header text in the Stock Workspace
#: erpnext/stock/workspace/stock/stock.json #: erpnext/stock/workspace/stock/stock.json
msgid "<span class=\"h4\"><b>Masters &amp; Reports</b></span>" msgid "<span class=\"h4\"><b>Masters &amp; 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 Selling Workspace
#. Header text in the Stock 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/buying/doctype/purchase_order/purchase_order.json
#: erpnext/selling/doctype/sales_order/sales_order.json #: erpnext/selling/doctype/sales_order/sales_order.json
msgid "Advance Paid" msgid "Advance Paid"
msgstr "Avance Payée" msgstr ""
#: erpnext/buying/doctype/purchase_order/purchase_order_list.js:75 #: erpnext/buying/doctype/purchase_order/purchase_order_list.js:75
#: erpnext/selling/doctype/sales_order/sales_order_list.js:122 #: 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/purchase_invoice/purchase_invoice.json
#: erpnext/accounts/doctype/sales_invoice/sales_invoice.json #: erpnext/accounts/doctype/sales_invoice/sales_invoice.json
msgid "Advances" msgid "Advances"
msgstr "Avances" msgstr ""
#: erpnext/setup/setup_wizard/data/marketing_source.txt:3 #: erpnext/setup/setup_wizard/data/marketing_source.txt:3
msgid "Advertisement" msgid "Advertisement"
@@ -7824,7 +7824,7 @@ msgstr "Groupe Sanguin"
#. Accounts' #. Accounts'
#: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json #: erpnext/accounts/doctype/process_statement_of_accounts/process_statement_of_accounts.json
msgid "Body" 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'
#. Label of the body_text (Text Editor) field in DocType 'Dunning Letter Text' #. 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' #. Label of the date_of_birth (Date) field in DocType 'Employee'
#: erpnext/setup/doctype/employee/employee.json #: erpnext/setup/doctype/employee/employee.json
msgid "Date of Birth" msgid "Date of Birth"
msgstr "Date de naissance" msgstr ""
#: erpnext/setup/doctype/employee/employee.py:147 #: erpnext/setup/doctype/employee/employee.py:147
msgid "Date of Birth cannot be greater than today." 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' #. Label of the date_of_joining (Date) field in DocType 'Employee'
#: erpnext/setup/doctype/employee/employee.json #: erpnext/setup/doctype/employee/employee.json
msgid "Date of Joining" msgid "Date of Joining"
msgstr "Date d'Embauche" msgstr ""
#: erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py:273 #: erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py:273
msgid "Date of Transaction" msgid "Date of Transaction"
@@ -14388,7 +14388,7 @@ msgstr "Frais différés"
#: erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json #: erpnext/accounts/doctype/purchase_invoice_item/purchase_invoice_item.json
#: erpnext/stock/doctype/item_default/item_default.json #: erpnext/stock/doctype/item_default/item_default.json
msgid "Deferred Expense Account" msgid "Deferred Expense Account"
msgstr "Compte de dépenses différées" msgstr ""
#. Option for the 'Entry Type' (Select) field in DocType 'Journal Entry' #. Option for the 'Entry Type' (Select) field in DocType 'Journal Entry'
#. Label of the deferred_revenue (Section Break) field in DocType 'POS Invoice #. 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' #. Label of the exit (Tab Break) field in DocType 'Employee'
#: erpnext/setup/doctype/employee/employee.json #: erpnext/setup/doctype/employee/employee.json
msgid "Employee Exit" msgid "Employee Exit"
msgstr "Sortie de lemployé" msgstr ""
#. Name of a DocType #. Name of a DocType
#: erpnext/setup/doctype/employee_external_work_history/employee_external_work_history.json #: 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' #. Label of the employee_number (Data) field in DocType 'Employee'
#: erpnext/setup/doctype/employee/employee.json #: erpnext/setup/doctype/employee/employee.json
msgid "Employee Number" msgid "Employee Number"
msgstr "Numéro d'Employé" msgstr ""
#. Label of the employee_user_id (Link) field in DocType 'Call Log' #. Label of the employee_user_id (Link) field in DocType 'Call Log'
#: erpnext/telephony/doctype/call_log/call_log.json #: erpnext/telephony/doctype/call_log/call_log.json
@@ -16892,7 +16892,7 @@ msgstr ""
#. Label of the encashment_date (Date) field in DocType 'Employee' #. Label of the encashment_date (Date) field in DocType 'Employee'
#: erpnext/setup/doctype/employee/employee.json #: erpnext/setup/doctype/employee/employee.json
msgid "Encashment Date" msgid "Encashment Date"
msgstr "Date de l'Encaissement" msgstr ""
#: erpnext/crm/doctype/contract/contract.py:70 #: erpnext/crm/doctype/contract/contract.py:70
msgid "End Date cannot be before Start Date." 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' #. Label of the holidays (Table) field in DocType 'Holiday List'
#: erpnext/setup/doctype/holiday_list/holiday_list.json #: erpnext/setup/doctype/holiday_list/holiday_list.json
msgid "Holidays" msgid "Holidays"
msgstr "Jours Fériés" msgstr ""
#. Option for the 'Forecasting Method' (Select) field in DocType 'Sales #. Option for the 'Forecasting Method' (Select) field in DocType 'Sales
#. Forecast' #. Forecast'
@@ -20585,7 +20585,7 @@ msgstr ""
#: erpnext/manufacturing/doctype/job_card/job_card.json #: erpnext/manufacturing/doctype/job_card/job_card.json
#: erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json #: erpnext/manufacturing/doctype/work_order_operation/work_order_operation.json
msgid "Hour Rate" msgid "Hour Rate"
msgstr "Tarif Horaire" msgstr ""
#. Label of the hours (Float) field in DocType 'Workstation Working Hour' #. Label of the hours (Float) field in DocType 'Workstation Working Hour'
#: erpnext/manufacturing/doctype/workstation_working_hour/workstation_working_hour.json #: 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/manufacturing/doctype/work_order/work_order.json
#: erpnext/stock/doctype/quality_inspection/quality_inspection.json #: erpnext/stock/doctype/quality_inspection/quality_inspection.json
msgid "In Process" msgid "In Process"
msgstr "En Cours" msgstr ""
#: erpnext/stock/report/item_variant_details/item_variant_details.py:107 #: erpnext/stock/report/item_variant_details/item_variant_details.py:107
msgid "In Production" 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' #. Label of the interest (Currency) field in DocType 'Overdue Payment'
#: erpnext/accounts/doctype/overdue_payment/overdue_payment.json #: erpnext/accounts/doctype/overdue_payment/overdue_payment.json
msgid "Interest" msgid "Interest"
msgstr "Intérêt" msgstr ""
#: erpnext/accounts/doctype/payment_entry/payment_entry.py:3052 #: erpnext/accounts/doctype/payment_entry/payment_entry.py:3052
msgid "Interest and/or dunning fee" 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' #. Label of the scheduled_confirmation_date (Date) field in DocType 'Employee'
#: erpnext/setup/doctype/employee/employee.json #: erpnext/setup/doctype/employee/employee.json
msgid "Offer Date" 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.py:29
#: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py:42 #: 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/setup/doctype/supplier_group/supplier_group.json
#: erpnext/stock/doctype/warehouse/warehouse.json #: erpnext/stock/doctype/warehouse/warehouse.json
msgid "Old Parent" msgid "Old Parent"
msgstr "Grand Parent" msgstr ""
#. Option for the 'Reconciliation Takes Effect On' (Select) field in DocType #. Option for the 'Reconciliation Takes Effect On' (Select) field in DocType
#. 'Company' #. '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' #. Option for the 'Current Address Is' (Select) field in DocType 'Employee'
#: erpnext/setup/doctype/employee/employee.json #: erpnext/setup/doctype/employee/employee.json
msgid "Owned" 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.js:29
#: erpnext/accounts/report/sales_payment_summary/sales_payment_summary.py:23 #: 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_gateway_account/payment_gateway_account.json
#: erpnext/accounts/doctype/payment_request/payment_request.json #: erpnext/accounts/doctype/payment_request/payment_request.json
msgid "Payment Account" 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 'Overdue Payment'
#. Label of the payment_amount (Currency) field in DocType 'Payment Schedule' #. Label of the payment_amount (Currency) field in DocType 'Payment Schedule'
@@ -32797,7 +32797,7 @@ msgstr ""
#. Account' #. Account'
#: erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json #: erpnext/accounts/doctype/journal_entry_account/journal_entry_account.json
msgid "Payroll Entry" 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.py:88
#: erpnext/accounts/doctype/account/chart_of_accounts/verified/standard_chart_of_accounts_with_account_number.py:119 #: 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' #. Label of the complaint_raised_by (Data) field in DocType 'Warranty Claim'
#: erpnext/support/doctype/warranty_claim/warranty_claim.json #: erpnext/support/doctype/warranty_claim/warranty_claim.json
msgid "Raised By" msgid "Raised By"
msgstr "Créé par" msgstr ""
#. Label of the raised_by (Data) field in DocType 'Issue' #. Label of the raised_by (Data) field in DocType 'Issue'
#: erpnext/support/doctype/issue/issue.json #: 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' #. Label of the relieving_date (Date) field in DocType 'Employee'
#: erpnext/setup/doctype/employee/employee.json #: erpnext/setup/doctype/employee/employee.json
msgid "Relieving Date" msgid "Relieving Date"
msgstr "Date de Relève" msgstr ""
#: erpnext/public/js/bank_reconciliation_tool/dialog_manager.js:125 #: erpnext/public/js/bank_reconciliation_tool/dialog_manager.js:125
msgid "Remaining" 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' #. Label of the resignation_letter_date (Date) field in DocType 'Employee'
#: erpnext/setup/doctype/employee/employee.json #: erpnext/setup/doctype/employee/employee.json
msgid "Resignation Letter Date" 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 sb_00 (Section Break) field in DocType 'Quality Action'
#. Label of the resolution (Text Editor) 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/selling/page/point_of_sale/pos_past_order_summary.js:138
#: erpnext/stock/doctype/shipment/shipment.json #: erpnext/stock/doctype/shipment/shipment.json
msgid "Returned" msgid "Returned"
msgstr "retourné" msgstr ""
#. Label of the returned_against (Data) field in DocType 'Serial and Batch #. Label of the returned_against (Data) field in DocType 'Serial and Batch
#. Bundle' #. Bundle'
@@ -40845,7 +40845,7 @@ msgstr "Total arrondi"
#: erpnext/stock/doctype/delivery_note/delivery_note.json #: erpnext/stock/doctype/delivery_note/delivery_note.json
#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json #: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
msgid "Rounded Total (Company Currency)" 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 'POS Invoice'
#. Label of the rounding_adjustment (Currency) field in DocType 'Purchase #. 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' #. Option for the '% Complete Method' (Select) field in DocType 'Project'
#: erpnext/projects/doctype/project/project.json #: erpnext/projects/doctype/project/project.json
msgid "Task Weight" msgid "Task Weight"
msgstr "Poids de la Tâche" msgstr ""
#: erpnext/projects/doctype/project_template/project_template.py:41 #: erpnext/projects/doctype/project_template/project_template.py:41
msgid "Task {0} depends on Task {1}. Please add Task {1} to the Tasks list." 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/accounts/doctype/payment_terms_template/payment_terms_template.json
#: erpnext/quality_management/doctype/quality_feedback_template/quality_feedback_template.json #: erpnext/quality_management/doctype/quality_feedback_template/quality_feedback_template.json
msgid "Template Name" msgid "Template Name"
msgstr "Nom du Modèle" msgstr ""
#. Label of the template_task (Data) field in DocType 'Task' #. Label of the template_task (Data) field in DocType 'Task'
#: erpnext/projects/doctype/task/task.json #: 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/material_request/material_request.json
#: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json #: erpnext/stock/doctype/purchase_receipt/purchase_receipt.json
msgid "Terms" msgid "Terms"
msgstr "Termes" msgstr ""
#. Label of the terms_section_break (Section Break) field in DocType 'Purchase #. Label of the terms_section_break (Section Break) field in DocType 'Purchase
#. Order' #. Order'
@@ -51537,7 +51537,7 @@ msgstr ""
#: erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.py:45 #: erpnext/accounts/report/calculated_discount_mismatch/calculated_discount_mismatch.py:45
msgid "Transaction Name" msgid "Transaction Name"
msgstr "Nom de la transaction" msgstr ""
#. Label of the transaction_settings_section (Tab Break) field in DocType #. Label of the transaction_settings_section (Tab Break) field in DocType
#. 'Buying Settings' #. 'Buying Settings'
@@ -53793,7 +53793,7 @@ msgstr ""
#: erpnext/setup/setup_wizard/data/marketing_source.txt:10 #: erpnext/setup/setup_wizard/data/marketing_source.txt:10
msgid "Walk In" msgid "Walk In"
msgstr "Spontané" msgstr ""
#: erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js:4 #: erpnext/stock/page/warehouse_capacity_summary/warehouse_capacity_summary.js:4
msgid "Warehouse Capacity Summary" msgid "Warehouse Capacity Summary"

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: frappe\n" "Project-Id-Version: frappe\n"
"Report-Msgid-Bugs-To: hello@frappe.io\n" "Report-Msgid-Bugs-To: hello@frappe.io\n"
"POT-Creation-Date: 2025-11-10 12:11+0000\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" "Last-Translator: hello@frappe.io\n"
"Language-Team: Hungarian\n" "Language-Team: Hungarian\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@@ -25,11 +25,11 @@ msgstr ""
#: erpnext/selling/doctype/quotation/quotation.js:73 #: erpnext/selling/doctype/quotation/quotation.js:73
msgid " Address" msgid " Address"
msgstr "" msgstr " Cím"
#: erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py:677 #: erpnext/accounts/report/item_wise_sales_register/item_wise_sales_register.py:677
msgid " Amount" msgid " Amount"
msgstr "" msgstr " Összeg"
#: erpnext/public/js/bom_configurator/bom_configurator.bundle.js:114 #: erpnext/public/js/bom_configurator/bom_configurator.bundle.js:114
msgid " BOM" msgid " BOM"

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: frappe\n" "Project-Id-Version: frappe\n"
"Report-Msgid-Bugs-To: hello@frappe.io\n" "Report-Msgid-Bugs-To: hello@frappe.io\n"
"POT-Creation-Date: 2025-11-10 12:11+0000\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" "Last-Translator: hello@frappe.io\n"
"Language-Team: Indonesian\n" "Language-Team: Indonesian\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@@ -16771,7 +16771,7 @@ msgstr ""
#: erpnext/manufacturing/doctype/workstation/workstation.js:351 #: erpnext/manufacturing/doctype/workstation/workstation.js:351
msgid "Employees" msgid "Employees"
msgstr "Para karyawan" msgstr ""
#: erpnext/stock/doctype/batch/batch_list.js:16 #: erpnext/stock/doctype/batch/batch_list.js:16
msgid "Empty" msgid "Empty"
@@ -19907,7 +19907,7 @@ msgstr "Sasaran dan Prosedur"
#. Group in Quality Procedure's connections #. Group in Quality Procedure's connections
#: erpnext/quality_management/doctype/quality_procedure/quality_procedure.json #: erpnext/quality_management/doctype/quality_procedure/quality_procedure.json
msgid "Goals" msgid "Goals"
msgstr "tujuan" msgstr ""
#. Option for the 'Shipment Type' (Select) field in DocType 'Shipment' #. Option for the 'Shipment Type' (Select) field in DocType 'Shipment'
#: erpnext/stock/doctype/shipment/shipment.json #: erpnext/stock/doctype/shipment/shipment.json

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: frappe\n" "Project-Id-Version: frappe\n"
"Report-Msgid-Bugs-To: hello@frappe.io\n" "Report-Msgid-Bugs-To: hello@frappe.io\n"
"POT-Creation-Date: 2025-11-10 12:11+0000\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" "Last-Translator: hello@frappe.io\n"
"Language-Team: Polish\n" "Language-Team: Polish\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@@ -16652,7 +16652,7 @@ msgstr ""
#. Label of the exit (Tab Break) field in DocType 'Employee' #. Label of the exit (Tab Break) field in DocType 'Employee'
#: erpnext/setup/doctype/employee/employee.json #: erpnext/setup/doctype/employee/employee.json
msgid "Employee Exit" msgid "Employee Exit"
msgstr "Odejście pracownika" msgstr ""
#. Name of a DocType #. Name of a DocType
#: erpnext/setup/doctype/employee_external_work_history/employee_external_work_history.json #: erpnext/setup/doctype/employee_external_work_history/employee_external_work_history.json

View File

@@ -3,7 +3,7 @@ msgstr ""
"Project-Id-Version: frappe\n" "Project-Id-Version: frappe\n"
"Report-Msgid-Bugs-To: hello@frappe.io\n" "Report-Msgid-Bugs-To: hello@frappe.io\n"
"POT-Creation-Date: 2025-11-10 12:11+0000\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" "Last-Translator: hello@frappe.io\n"
"Language-Team: Turkish\n" "Language-Team: Turkish\n"
"MIME-Version: 1.0\n" "MIME-Version: 1.0\n"
@@ -16709,7 +16709,7 @@ msgstr "Eğitim Hayatı"
#. Label of the exit (Tab Break) field in DocType 'Employee' #. Label of the exit (Tab Break) field in DocType 'Employee'
#: erpnext/setup/doctype/employee/employee.json #: erpnext/setup/doctype/employee/employee.json
msgid "Employee Exit" msgid "Employee Exit"
msgstr "Çalışan Çıkışı" msgstr ""
#. Name of a DocType #. Name of a DocType
#: erpnext/setup/doctype/employee_external_work_history/employee_external_work_history.json #: erpnext/setup/doctype/employee_external_work_history/employee_external_work_history.json

View File

@@ -45,7 +45,7 @@ frappe.ui.form.on("BOM", {
return { return {
query: "erpnext.manufacturing.doctype.bom.bom.item_query", query: "erpnext.manufacturing.doctype.bom.bom.item_query",
filters: { 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( frm.add_custom_button(
__("Work Order"), __("Work Order"),
function () { function () {
@@ -529,6 +529,14 @@ frappe.ui.form.on("BOM", {
frm.set_value("process_loss_qty", qty); 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", { frappe.ui.form.on("BOM Operation", {

View File

@@ -16,6 +16,7 @@
"is_default", "is_default",
"allow_alternative_item", "allow_alternative_item",
"set_rate_of_sub_assembly_item_based_on_bom", "set_rate_of_sub_assembly_item_based_on_bom",
"is_phantom_bom",
"project", "project",
"image", "image",
"currency_detail", "currency_detail",
@@ -201,6 +202,7 @@
}, },
{ {
"collapsible": 1, "collapsible": 1,
"depends_on": "eval:!doc.is_phantom_bom",
"fieldname": "currency_detail", "fieldname": "currency_detail",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Cost Configuration" "label": "Cost Configuration"
@@ -293,6 +295,7 @@
}, },
{ {
"collapsible": 1, "collapsible": 1,
"depends_on": "eval:!doc.is_phantom_bom",
"fieldname": "scrap_section", "fieldname": "scrap_section",
"fieldtype": "Tab Break", "fieldtype": "Tab Break",
"label": "Scrap & Process Loss" "label": "Scrap & Process Loss"
@@ -310,6 +313,7 @@
"oldfieldtype": "Section Break" "oldfieldtype": "Section Break"
}, },
{ {
"depends_on": "eval:!doc.is_phantom_bom",
"fieldname": "operating_cost", "fieldname": "operating_cost",
"fieldtype": "Currency", "fieldtype": "Currency",
"label": "Operating Cost", "label": "Operating Cost",
@@ -324,6 +328,7 @@
"read_only": 1 "read_only": 1
}, },
{ {
"depends_on": "eval:!doc.is_phantom_bom",
"fieldname": "scrap_material_cost", "fieldname": "scrap_material_cost",
"fieldtype": "Currency", "fieldtype": "Currency",
"label": "Scrap Material Cost", "label": "Scrap Material Cost",
@@ -336,6 +341,7 @@
"fieldtype": "Column Break" "fieldtype": "Column Break"
}, },
{ {
"depends_on": "eval:!doc.is_phantom_bom",
"fieldname": "base_operating_cost", "fieldname": "base_operating_cost",
"fieldtype": "Currency", "fieldtype": "Currency",
"label": "Operating Cost (Company Currency)", "label": "Operating Cost (Company Currency)",
@@ -352,6 +358,7 @@
"read_only": 1 "read_only": 1
}, },
{ {
"depends_on": "eval:!doc.is_phantom_bom",
"fieldname": "base_scrap_material_cost", "fieldname": "base_scrap_material_cost",
"fieldtype": "Currency", "fieldtype": "Currency",
"label": "Scrap Material Cost(Company Currency)", "label": "Scrap Material Cost(Company Currency)",
@@ -380,6 +387,7 @@
"read_only": 1 "read_only": 1
}, },
{ {
"depends_on": "eval:!doc.is_phantom_bom",
"fieldname": "project", "fieldname": "project",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Project", "label": "Project",
@@ -427,6 +435,7 @@
}, },
{ {
"collapsible": 1, "collapsible": 1,
"depends_on": "eval:!doc.is_phantom_bom",
"fieldname": "website_section", "fieldname": "website_section",
"fieldtype": "Tab Break", "fieldtype": "Tab Break",
"label": "Website" "label": "Website"
@@ -536,6 +545,7 @@
{ {
"collapsible": 1, "collapsible": 1,
"collapsible_depends_on": "eval:doc.with_operations", "collapsible_depends_on": "eval:doc.with_operations",
"depends_on": "eval:!doc.is_phantom_bom",
"fieldname": "operations_section_section", "fieldname": "operations_section_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Operations" "label": "Operations"
@@ -570,6 +580,7 @@
"fieldtype": "Column Break" "fieldtype": "Column Break"
}, },
{ {
"depends_on": "eval:!doc.is_phantom_bom",
"fieldname": "quality_inspection_section_break", "fieldname": "quality_inspection_section_break",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Quality Inspection" "label": "Quality Inspection"
@@ -659,6 +670,12 @@
"fieldtype": "Link", "fieldtype": "Link",
"label": "Default Target Warehouse", "label": "Default Target Warehouse",
"options": "Warehouse" "options": "Warehouse"
},
{
"default": "0",
"fieldname": "is_phantom_bom",
"fieldtype": "Check",
"label": "Is Phantom BOM"
} }
], ],
"icon": "fa fa-sitemap", "icon": "fa fa-sitemap",
@@ -666,7 +683,7 @@
"image_field": "image", "image_field": "image",
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2025-10-29 17:43:12.966753", "modified": "2025-11-06 15:27:54.806116",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Manufacturing", "module": "Manufacturing",
"name": "BOM", "name": "BOM",

View File

@@ -135,6 +135,7 @@ class BOM(WebsiteGenerator):
inspection_required: DF.Check inspection_required: DF.Check
is_active: DF.Check is_active: DF.Check
is_default: DF.Check is_default: DF.Check
is_phantom_bom: DF.Check
item: DF.Link item: DF.Link
item_name: DF.Data | None item_name: DF.Data | None
items: DF.Table[BOMItem] 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 "", "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, "conversion_factor": args["conversion_factor"] if args.get("conversion_factor") else 1,
"bom_no": args["bom_no"], "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, "rate": rate,
"qty": args.get("qty") or args.get("stock_qty") or 1, "qty": args.get("qty") or args.get("stock_qty") or 1,
"stock_qty": args.get("stock_qty") or args.get("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), "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"): if args.get("do_not_explode"):
ret_item["bom_no"] = "" 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( if not frappe.db.get_value("Item", arg["item_code"], "is_customer_provided_item") and not arg.get(
"sourced_by_supplier" "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) rate = flt(self.get_bom_unitcost(arg["bom_no"])) * (arg.get("conversion_factor") or 1)
else: else:
rate = get_bom_item_rate(arg, self) rate = get_bom_item_rate(arg, self)
@@ -888,7 +897,7 @@ class BOM(WebsiteGenerator):
for d in self.get("items"): for d in self.get("items"):
old_rate = d.rate 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( d.rate = self.get_rm_rate(
{ {
"company": self.company, "company": self.company,
@@ -899,6 +908,7 @@ class BOM(WebsiteGenerator):
"stock_uom": d.stock_uom, "stock_uom": d.stock_uom,
"conversion_factor": d.conversion_factor, "conversion_factor": d.conversion_factor,
"sourced_by_supplier": d.sourced_by_supplier, "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 where
bom_item.docstatus < 2 bom_item.docstatus < 2
and bom.name = %(bom)s 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} {where_conditions}
{group_by_cond} {group_by_cond}
order by idx""" 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): if cint(fetch_exploded):
query = query.format( query = query.format(
table="BOM Explosion Item", table="BOM Explosion Item",
where_conditions="", where_conditions=")",
is_stock_item=is_stock_item, is_stock_item=is_stock_item,
qty_field="stock_qty", qty_field="stock_qty",
group_by_cond=group_by_cond, group_by_cond=group_by_cond,
@@ -1301,7 +1311,7 @@ def get_bom_items_as_dict(
elif fetch_scrap_items: elif fetch_scrap_items:
query = query.format( query = query.format(
table="BOM Scrap Item", table="BOM Scrap Item",
where_conditions="", where_conditions=")",
select_columns=", item.description", select_columns=", item.description",
is_stock_item=is_stock_item, is_stock_item=is_stock_item,
qty_field="stock_qty", qty_field="stock_qty",
@@ -1312,12 +1322,12 @@ def get_bom_items_as_dict(
else: else:
query = query.format( query = query.format(
table="BOM Item", table="BOM Item",
where_conditions="", where_conditions="or bom_item.is_phantom_item)",
is_stock_item=is_stock_item, is_stock_item=is_stock_item,
qty_field="stock_qty" if fetch_qty_in_stock_uom else "qty", 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, 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.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, group_by_cond=group_by_cond,
) )
items = frappe.db.sql(query, {"qty": qty, "bom": bom, "company": company}, as_dict=True) 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: if item.operation_row_id:
key = (item.item_code, 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) item_dict[key]["qty"] += flt(item.qty)
else: else:
item_dict[key] = item item_dict[key] = item
@@ -1379,7 +1406,7 @@ def validate_bom_no(item, bom_no):
@frappe.whitelist() @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": if not parent or parent == "BOM":
frappe.msgprint(_("Please select a BOM")) frappe.msgprint(_("Please select a BOM"))
return 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) bom_doc = frappe.get_cached_doc("BOM", frappe.form_dict.parent)
frappe.has_permission("BOM", doc=bom_doc, throw=True) 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_items = frappe.get_all(
"BOM Item", "BOM Item",
fields=["item_code", "bom_no as value", "stock_qty", "qty"], fields=["item_code", "bom_no as value", "stock_qty", "qty", "is_phantom_item", "bom_no"],
filters=[["parent", "=", frappe.form_dict.parent]], filters=filters,
order_by="idx", order_by="idx",
) )

View File

@@ -12,7 +12,10 @@
{{ __("Description") }} {{ __("Description") }}
</h4> </h4>
<div style="padding-top: 10px;"> <div style="padding-top: 10px;">
{{ data.description }} {% if data.is_phantom_item %}
<p><b>{{ __("Phantom Item") }}</b></p>
{% endif %}
<p>{{ data.description }}</p>
</div> </div>
<hr style="margin: 15px -15px;"> <hr style="margin: 15px -15px;">
<p> <p>

View File

@@ -794,7 +794,7 @@ def level_order_traversal(node):
return traversal 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)""" """Helper function to create a simple nested bom from tree describing item names. (along with required items)"""
def create_items(bom_tree): def create_items(bom_tree):
@@ -806,6 +806,9 @@ def create_nested_bom(tree, prefix="_Test bom ", submit=True):
).insert() ).insert()
create_items(subtree) create_items(subtree)
if not phantom_items:
phantom_items = []
create_items(tree) create_items(tree)
def dfs(tree, node): def dfs(tree, node):
@@ -824,7 +827,7 @@ def create_nested_bom(tree, prefix="_Test bom ", submit=True):
child_items = dfs(tree, item) child_items = dfs(tree, item)
if child_items: if child_items:
bom_item_code = prefix + item 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(): for child_item in child_items.keys():
bom.append("items", {"item_code": prefix + child_item}) bom.append("items", {"item_code": prefix + child_item})
bom.company = "_Test Company" 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}) return make_item(item_code, {"stock_uom": stock_uom, "valuation_rate": 100})
else: else:
return frappe.get_doc("Item", item_code) 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"]

View File

@@ -6,7 +6,7 @@ from collections import OrderedDict
import frappe import frappe
from frappe import _ from frappe import _
from frappe.model.document import Document 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 from erpnext.manufacturing.doctype.bom.bom import get_bom_item_rate
@@ -29,6 +29,7 @@ BOM_ITEM_FIELDS = [
"conversion_factor", "conversion_factor",
"do_not_explode", "do_not_explode",
"operation", "operation",
"is_phantom_item",
] ]
@@ -305,6 +306,7 @@ class BOMCreator(Document):
"allow_alternative_item": 1, "allow_alternative_item": 1,
"bom_creator": self.name, "bom_creator": self.name,
"bom_creator_item": bom_creator_item, "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, "bom_no": bom_no,
"allow_alternative_item": 1, "allow_alternative_item": 1,
"allow_scrap_items": 1, "allow_scrap_items": not item.get("is_phantom_item"),
"include_item_in_manufacturing": 1, "include_item_in_manufacturing": 1,
} }
) )
@@ -456,12 +458,16 @@ def add_sub_assembly(**kwargs):
"is_expandable": 1, "is_expandable": 1,
"stock_uom": item_info.stock_uom, "stock_uom": item_info.stock_uom,
"operation": bom_item.operation, "operation": bom_item.operation,
"is_phantom_item": sbool(kwargs.phantom),
}, },
) )
parent_row_no = item_row.idx parent_row_no = item_row.idx
name = "" name = ""
else: 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) parent_row_no = get_parent_row_no(doc, kwargs.fg_reference_id)
for row in bom_item.get("items"): for row in bom_item.get("items"):

View File

@@ -15,6 +15,7 @@
"sourced_by_supplier", "sourced_by_supplier",
"bom_created", "bom_created",
"is_subcontracted", "is_subcontracted",
"is_phantom_item",
"operation_section", "operation_section",
"operation", "operation",
"column_break_cbnk", "column_break_cbnk",
@@ -159,8 +160,8 @@
"fieldname": "amount", "fieldname": "amount",
"fieldtype": "Currency", "fieldtype": "Currency",
"label": "Amount", "label": "Amount",
"read_only": 1, "options": "currency",
"options": "currency" "read_only": 1
}, },
{ {
"fieldname": "column_break_yuca", "fieldname": "column_break_yuca",
@@ -229,6 +230,7 @@
"print_hide": 1 "print_hide": 1
}, },
{ {
"depends_on": "eval:!doc.is_phantom_item",
"fieldname": "operation_section", "fieldname": "operation_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
"label": "Operation" "label": "Operation"
@@ -245,21 +247,30 @@
}, },
{ {
"default": "0", "default": "0",
"depends_on": "eval:!doc.is_phantom_item",
"fieldname": "is_subcontracted", "fieldname": "is_subcontracted",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Is Subcontracted", "label": "Is Subcontracted",
"read_only": 1 "read_only": 1
},
{
"default": "0",
"fieldname": "is_phantom_item",
"fieldtype": "Check",
"label": "Is Phantom Item",
"read_only": 1
} }
], ],
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2024-11-25 18:13:34.542391", "modified": "2025-11-05 21:15:55.187671",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Manufacturing", "module": "Manufacturing",
"name": "BOM Creator Item", "name": "BOM Creator Item",
"owner": "Administrator", "owner": "Administrator",
"permissions": [], "permissions": [],
"row_format": "Dynamic",
"sort_field": "creation", "sort_field": "creation",
"sort_order": "DESC", "sort_order": "DESC",
"states": [] "states": []

View File

@@ -25,6 +25,7 @@ class BOMCreatorItem(Document):
fg_reference_id: DF.Data | None fg_reference_id: DF.Data | None
instruction: DF.SmallText | None instruction: DF.SmallText | None
is_expandable: DF.Check is_expandable: DF.Check
is_phantom_item: DF.Check
is_subcontracted: DF.Check is_subcontracted: DF.Check
item_code: DF.Link item_code: DF.Link
item_group: DF.Link | None item_group: DF.Link | None

View File

@@ -42,7 +42,8 @@
"original_item", "original_item",
"column_break_33", "column_break_33",
"sourced_by_supplier", "sourced_by_supplier",
"is_sub_assembly_item" "is_sub_assembly_item",
"is_phantom_item"
], ],
"fields": [ "fields": [
{ {
@@ -81,6 +82,7 @@
"fieldtype": "Link", "fieldtype": "Link",
"in_filter": 1, "in_filter": 1,
"label": "BOM No", "label": "BOM No",
"mandatory_depends_on": "eval:doc.is_phantom_item",
"oldfieldname": "bom_no", "oldfieldname": "bom_no",
"oldfieldtype": "Link", "oldfieldtype": "Link",
"options": "BOM", "options": "BOM",
@@ -278,6 +280,7 @@
}, },
{ {
"default": "0", "default": "0",
"depends_on": "eval:!doc.is_phantom_item",
"fieldname": "sourced_by_supplier", "fieldname": "sourced_by_supplier",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Sourced by Supplier" "label": "Sourced by Supplier"
@@ -286,7 +289,8 @@
"default": "0", "default": "0",
"fieldname": "do_not_explode", "fieldname": "do_not_explode",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Do Not Explode" "label": "Do Not Explode",
"read_only_depends_on": "eval:doc.is_phantom_item"
}, },
{ {
"default": "0", "default": "0",
@@ -304,18 +308,26 @@
}, },
{ {
"default": "0", "default": "0",
"depends_on": "eval:!doc.is_phantom_item",
"fieldname": "is_sub_assembly_item", "fieldname": "is_sub_assembly_item",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Is Sub Assembly Item", "label": "Is Sub Assembly Item",
"no_copy": 1, "no_copy": 1,
"read_only": 1 "read_only": 1
},
{
"default": "0",
"fieldname": "is_phantom_item",
"fieldtype": "Check",
"label": "Is Phantom Item",
"read_only": 1
} }
], ],
"idx": 1, "idx": 1,
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2025-08-12 20:01:59.532613", "modified": "2025-11-05 19:00:38.646539",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Manufacturing", "module": "Manufacturing",
"name": "BOM Item", "name": "BOM Item",

View File

@@ -25,6 +25,7 @@ class BOMItem(Document):
has_variants: DF.Check has_variants: DF.Check
image: DF.Attach | None image: DF.Attach | None
include_item_in_manufacturing: DF.Check include_item_in_manufacturing: DF.Check
is_phantom_item: DF.Check
is_stock_item: DF.Check is_stock_item: DF.Check
is_sub_assembly_item: DF.Check is_sub_assembly_item: DF.Check
item_code: DF.Link item_code: DF.Link

View File

@@ -1085,7 +1085,7 @@ class JobCard(Document):
def set_wip_warehouse(self): def set_wip_warehouse(self):
if not self.wip_warehouse: 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): def validate_operation_id(self):
if ( if (

View File

@@ -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." "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", fieldname: "update_bom_costs_automatically",
title: __("Update BOM Cost Automatically"), title: __("Update BOM Cost Automatically"),

View File

@@ -16,11 +16,6 @@
"update_bom_costs_automatically", "update_bom_costs_automatically",
"column_break_lhyt", "column_break_lhyt",
"allow_editing_of_items_and_quantities_in_work_order", "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", "over_production_for_sales_and_work_order_section",
"overproduction_percentage_for_sales_order", "overproduction_percentage_for_sales_order",
"column_break_16", "column_break_16",
@@ -86,11 +81,6 @@
"fieldtype": "Int", "fieldtype": "Int",
"label": "Time Between Operations (Mins)" "label": "Time Between Operations (Mins)"
}, },
{
"fieldname": "section_break_6",
"fieldtype": "Section Break",
"label": "Default Warehouses for Production"
},
{ {
"fieldname": "overproduction_percentage_for_sales_order", "fieldname": "overproduction_percentage_for_sales_order",
"fieldtype": "Percent", "fieldtype": "Percent",
@@ -122,34 +112,12 @@
"fieldtype": "Check", "fieldtype": "Check",
"label": "Update BOM Cost Automatically" "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", "default": "0",
"fieldname": "disable_capacity_planning", "fieldname": "disable_capacity_planning",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Disable Capacity Planning" "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", "fieldname": "over_production_for_sales_and_work_order_section",
"fieldtype": "Section Break", "fieldtype": "Section Break",
@@ -275,7 +243,7 @@
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"issingle": 1, "issingle": 1,
"links": [], "links": [],
"modified": "2025-11-07 14:52:56.241459", "modified": "2025-11-13 12:30:29.006822",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Manufacturing", "module": "Manufacturing",
"name": "Manufacturing Settings", "name": "Manufacturing Settings",

View File

@@ -23,9 +23,6 @@ class ManufacturingSettings(Document):
allow_production_on_holidays: DF.Check allow_production_on_holidays: DF.Check
backflush_raw_materials_based_on: DF.Literal["BOM", "Material Transferred for Manufacture"] backflush_raw_materials_based_on: DF.Literal["BOM", "Material Transferred for Manufacture"]
capacity_planning_for_days: DF.Int 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 disable_capacity_planning: DF.Check
enforce_time_logs: DF.Check enforce_time_logs: DF.Check
get_rm_cost_from_consumption_entry: DF.Check get_rm_cost_from_consumption_entry: DF.Check

View File

@@ -6,6 +6,8 @@ frappe.ui.form.on("Master Production Schedule", {
frm.trigger("set_query_filters"); frm.trigger("set_query_filters");
frm.set_df_property("items", "cannot_add_rows", true); 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.fields_dict.items.$wrapper.find("[data-action='duplicate_rows']").css("display", "none");
frm.trigger("set_custom_buttons"); 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) { get_actual_demand(frm) {

View File

@@ -26,8 +26,8 @@
"material_requests", "material_requests",
"section_break_xtby", "section_break_xtby",
"column_break_yhkr", "column_break_yhkr",
"column_break_vvys",
"get_actual_demand", "get_actual_demand",
"column_break_vvys",
"section_break_cmgo", "section_break_cmgo",
"items", "items",
"forecast_demand_section", "forecast_demand_section",
@@ -60,7 +60,6 @@
"fieldtype": "Column Break" "fieldtype": "Column Break"
}, },
{ {
"allow_bulk_edit": 1,
"fieldname": "items", "fieldname": "items",
"fieldtype": "Table", "fieldtype": "Table",
"label": "Items", "label": "Items",
@@ -189,7 +188,7 @@
"grid_page_length": 50, "grid_page_length": 50,
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"links": [], "links": [],
"modified": "2025-09-02 19:33:28.244544", "modified": "2025-11-13 19:15:36.090622",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Manufacturing", "module": "Manufacturing",
"name": "Master Production Schedule", "name": "Master Production Schedule",

View File

@@ -4,7 +4,7 @@
import math import math
import frappe import frappe
from frappe import _ from frappe import _, bold
from frappe.model.document import Document from frappe.model.document import Document
from frappe.model.mapper import get_mapped_doc from frappe.model.mapper import get_mapped_doc
from frappe.query_builder.functions import Sum from frappe.query_builder.functions import Sum
@@ -64,6 +64,22 @@ class MasterProductionSchedule(Document):
def validate(self): def validate(self):
self.set_to_date() 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): def set_to_date(self):
self.to_date = None self.to_date = None

View File

@@ -8,12 +8,16 @@
"item_code", "item_code",
"from_warehouse", "from_warehouse",
"warehouse", "warehouse",
"item_name",
"material_request_type", "material_request_type",
"column_break_4", "column_break_4",
"item_name",
"uom", "uom",
"conversion_factor", "conversion_factor",
"section_break_azee", "section_break_azee",
"from_bom",
"column_break_scnz",
"main_item_code",
"section_break_qnpt",
"required_bom_qty", "required_bom_qty",
"projected_qty", "projected_qty",
"column_break_wack", "column_break_wack",
@@ -25,6 +29,7 @@
"min_order_qty", "min_order_qty",
"section_break_8", "section_break_8",
"sales_order", "sales_order",
"sub_assembly_item_reference",
"bin_qty_section", "bin_qty_section",
"actual_qty", "actual_qty",
"requested_qty", "requested_qty",
@@ -220,12 +225,48 @@
"label": "Stock Reserved Qty", "label": "Stock Reserved Qty",
"no_copy": 1, "no_copy": 1,
"read_only": 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, "grid_page_length": 50,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2025-05-01 14:50:55.805442", "modified": "2025-10-30 17:01:25.996352",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Manufacturing", "module": "Manufacturing",
"name": "Material Request Plan Item", "name": "Material Request Plan Item",

View File

@@ -17,9 +17,11 @@ class MaterialRequestPlanItem(Document):
actual_qty: DF.Float actual_qty: DF.Float
conversion_factor: DF.Float conversion_factor: DF.Float
description: DF.TextEditor | None description: DF.TextEditor | None
from_bom: DF.Link | None
from_warehouse: DF.Link | None from_warehouse: DF.Link | None
item_code: DF.Link item_code: DF.Link
item_name: DF.Data | None item_name: DF.Data | None
main_item_code: DF.Link | None
material_request_type: DF.Literal[ material_request_type: DF.Literal[
"", "",
"Purchase", "Purchase",
@@ -43,6 +45,7 @@ class MaterialRequestPlanItem(Document):
sales_order: DF.Link | None sales_order: DF.Link | None
schedule_date: DF.Date | None schedule_date: DF.Date | None
stock_reserved_qty: DF.Float stock_reserved_qty: DF.Float
sub_assembly_item_reference: DF.Data | None
uom: DF.Link | None uom: DF.Link | None
warehouse: DF.Link warehouse: DF.Link
# end: auto-generated types # end: auto-generated types

View File

@@ -568,6 +568,7 @@ class ProductionPlan(Document):
def on_submit(self): def on_submit(self):
self.update_bin_qty() self.update_bin_qty()
self.update_sales_order() self.update_sales_order()
self.add_reference_to_raw_materials()
self.update_stock_reservation() self.update_stock_reservation()
def on_cancel(self): def on_cancel(self):
@@ -583,6 +584,24 @@ class ProductionPlan(Document):
make_stock_reservation_entries(self) 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): def update_sales_order(self):
sales_orders = [row.sales_order for row in self.po_items if row.sales_order] sales_orders = [row.sales_order for row in self.po_items if row.sales_order]
if sales_orders: if sales_orders:
@@ -737,7 +756,7 @@ class ProductionPlan(Document):
wo_list, po_list = [], [] wo_list, po_list = [], []
subcontracted_po = {} 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_finished_goods(wo_list, default_warehouses)
self.make_work_order_for_subassembly_items(wo_list, subcontracted_po, 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.purchase_uom,
item_uom.conversion_factor, item_uom.conversion_factor,
bom.item.as_("main_bom_item"), bom.item.as_("main_bom_item"),
bom_item.is_phantom_item,
) )
.where( .where(
(bom.name == bom_no) (bom.name == bom_no)
& (bom_item.is_sub_assembly_item == 0) & (bom_item.is_sub_assembly_item == 0)
& (bom_item.docstatus < 2) & (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) .groupby(bom_item.item_code)
.orderby(bom_item.idx)
).run(as_dict=True) ).run(as_dict=True)
for d in items: for d in items:
@@ -1355,10 +1379,12 @@ def get_subitems(
item_details[d.item_code] = d 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 ( if (
d.default_material_request_type in ["Manufacture", "Purchase"] and not d.is_sub_contracted (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_sub_contracted and include_subcontracted_items)
or d.is_phantom_item
):
if d.qty > 0: if d.qty > 0:
get_subitems( get_subitems(
doc, doc,
@@ -1370,7 +1396,7 @@ def get_subitems(
include_subcontracted_items, include_subcontracted_items,
d.qty, 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( def get_material_request_items(
@@ -1382,14 +1408,14 @@ def get_material_request_items(
include_safety_stock, include_safety_stock,
warehouse, warehouse,
bin_dict, bin_dict,
total_qty,
): ):
total_qty = row["qty"]
required_qty = 0 required_qty = 0
if not ignore_existing_ordered_qty or bin_dict.get("projected_qty", 0) < 0: if not ignore_existing_ordered_qty or bin_dict.get("projected_qty", 0) < 0:
required_qty = total_qty required_qty = total_qty[row.get("item_code")]
elif total_qty > bin_dict.get("projected_qty", 0): elif total_qty[row.get("item_code")] > bin_dict.get("projected_qty", 0):
required_qty = total_qty - 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"]: if doc.get("consider_minimum_order_qty") and required_qty > 0 and required_qty < row["min_order_qty"]:
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, "item_name": row.item_name,
"quantity": required_qty / conversion_factor, "quantity": required_qty / conversion_factor,
"conversion_factor": conversion_factor, "conversion_factor": conversion_factor,
"required_bom_qty": total_qty, "required_bom_qty": row.get("qty"),
"stock_uom": row.get("stock_uom"), "stock_uom": row.get("stock_uom"),
"warehouse": warehouse "warehouse": warehouse
or row.get("source_warehouse") or row.get("source_warehouse")
@@ -1448,7 +1474,8 @@ def get_material_request_items(
"sales_order": sales_order, "sales_order": sales_order,
"description": row.get("description"), "description": row.get("description"),
"uom": row.get("purchase_uom") or row.get("stock_uom"), "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) sub_assembly_items = defaultdict(int)
if doc.get("skip_available_sub_assembly_item") and doc.get("sub_assembly_items"): if doc.get("skip_available_sub_assembly_item") and doc.get("sub_assembly_items"):
for d in 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: for data in po_items:
if not data.get("include_exploded_items") and doc.get("sub_assembly_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, sub_assembly_items,
planned_qty=planned_qty, planned_qty=planned_qty,
) )
elif data.get("include_exploded_items") and include_subcontracted_items: elif data.get("include_exploded_items") and include_subcontracted_items:
# fetch exploded items from BOM # fetch exploded items from BOM
item_details = get_exploded_items( 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 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, "item_name": item_master.item_name,
"default_bom": doc.bom, "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, "min_order_qty": item_master.min_order_qty,
"default_material_request_type": item_master.default_material_request_type, "default_material_request_type": item_master.default_material_request_type,
"qty": planned_qty or 1, "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, "item_code": item_master.name,
"description": item_master.description, "description": item_master.description,
"stock_uom": item_master.stock_uom, "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") 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()) so_item_details.setdefault(sales_order, frappe._dict())
if item_code in so_item_details.get(sales_order, {}): if key in so_item_details.get(sales_order, {}):
so_item_details[sales_order][item_code]["qty"] = so_item_details[sales_order][item_code].get( so_item_details[sales_order][key]["qty"] = so_item_details[sales_order][key].get(
"qty", 0 "qty", 0
) + flt(details.qty) ) + flt(details.qty)
else: else:
so_item_details[sales_order][item_code] = details so_item_details[sales_order][key] = details
mr_items = [] mr_items = []
for sales_order in so_item_details: for sales_order in so_item_details:
item_dict = so_item_details[sales_order] item_dict = so_item_details[sales_order]
total_qty = defaultdict(float)
for details in item_dict.values(): 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 = get_bin_details(details, doc.company, warehouse)
bin_dict = bin_dict[0] if bin_dict else {} 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, include_safety_stock,
warehouse, warehouse,
bin_dict, bin_dict,
total_qty,
) )
if items: if items:
mr_items.append(items) mr_items.append(items)
@@ -1847,8 +1896,9 @@ def get_sub_assembly_items(
warehouse=None, warehouse=None,
indent=0, indent=0,
skip_available_sub_assembly_item=False, 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: for d in data:
if d.expandable: if d.expandable:
parent_item_code = frappe.get_cached_value("BOM", bom_no, "item") 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) "projected_qty": bin_details[d.item_code][0].get("projected_qty", 0)
if bin_details.get(d.item_code) if bin_details.get(d.item_code)
else 0, else 0,
"main_bom": bom_no,
} }
) )
) )
@@ -1907,6 +1958,7 @@ def get_sub_assembly_items(
warehouse, warehouse,
indent=indent + 1, indent=indent + 1,
skip_available_sub_assembly_item=skip_available_sub_assembly_item, 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_default = frappe.qb.DocType("Item Default")
item_uom = frappe.qb.DocType("UOM Conversion Detail") item_uom = frappe.qb.DocType("UOM Conversion Detail")
items = ( query = (
frappe.qb.from_(bei) frappe.qb.from_(bei)
.join(bom) .join(bom)
.on(bom.name == bei.parent) .on(bom.name == bei.parent)
@@ -2014,6 +2066,7 @@ def get_raw_materials_of_sub_assembly_items(
item.name.as_("item_code"), item.name.as_("item_code"),
bei.description, bei.description,
bei.stock_uom, bei.stock_uom,
bei.is_phantom_item,
bei.bom_no, bei.bom_no,
item.min_order_qty, item.min_order_qty,
bei.source_warehouse, bei.source_warehouse,
@@ -2024,19 +2077,28 @@ def get_raw_materials_of_sub_assembly_items(
item_uom.conversion_factor, item_uom.conversion_factor,
item.safety_stock, item.safety_stock,
bom.item.as_("main_bom_item"), bom.item.as_("main_bom_item"),
bom.name.as_("main_bom"),
) )
.where( .where(
(bei.docstatus == 1) (bei.docstatus == 1)
& (bei.is_sub_assembly_item == 0) & (bei.is_sub_assembly_item == 0)
& (bom.name == bom_no) & (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) .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) 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 continue
if item.bom_no: if item.bom_no:
@@ -2050,15 +2112,15 @@ def get_raw_materials_of_sub_assembly_items(
sub_assembly_items, sub_assembly_items,
planned_qty=planned_qty, 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: else:
if not item.conversion_factor and item.purchase_uom: if not item.conversion_factor and item.purchase_uom:
item.conversion_factor = get_uom_conversion_factor(item.item_code, 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") details.qty += item.get("qty")
else: else:
item_details.setdefault(item.get("item_code"), item) item_details.setdefault((item.get("item_code"), item.get("main_bom")), item)
return item_details return item_details

View File

@@ -1944,11 +1944,17 @@ class TestProductionPlan(IntegrationTestCase):
mr_items = get_items_for_material_requests(plan.as_dict()) 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) # 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) # 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): def test_stock_reservation_against_production_plan(self):
from erpnext.buying.doctype.purchase_order.purchase_order import make_purchase_receipt 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) frappe.db.set_single_value("Stock Settings", "enable_stock_reservation", 0)
def test_production_plan_for_partial_sub_assembly_items(self): 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.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 frappe.flags.test_print = False
@@ -2418,6 +2420,30 @@ class TestProductionPlan(IntegrationTestCase):
for row in plan.sub_assembly_items: for row in plan.sub_assembly_items:
self.assertEqual(row.ordered_qty, 10.0) 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): 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, "skip_available_sub_assembly_item": args.skip_available_sub_assembly_item or 0,
"sub_assembly_warehouse": args.sub_assembly_warehouse, "sub_assembly_warehouse": args.sub_assembly_warehouse,
"reserve_stock": args.reserve_stock or 0, "reserve_stock": args.reserve_stock or 0,
"for_warehouse": args.for_warehouse or None,
} }
) )

View File

@@ -79,13 +79,14 @@
"fieldname": "received_qty", "fieldname": "received_qty",
"fieldtype": "Float", "fieldtype": "Float",
"label": "Received Qty", "label": "Received Qty",
"no_copy": 1,
"read_only": 1 "read_only": 1
}, },
{ {
"fieldname": "bom_no", "fieldname": "bom_no",
"fieldtype": "Link", "fieldtype": "Link",
"in_list_view": 1, "in_list_view": 1,
"label": "Bom No", "label": "BOM No",
"options": "BOM" "options": "BOM"
}, },
{ {
@@ -245,7 +246,7 @@
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2025-06-10 13:36:24.759101", "modified": "2025-11-03 14:33:50.677717",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Manufacturing", "module": "Manufacturing",
"name": "Production Plan Sub Assembly Item", "name": "Production Plan Sub Assembly Item",

View File

@@ -2,6 +2,16 @@
// For license information, please see license.txt // For license information, please see license.txt
frappe.ui.form.on("Routing", { frappe.ui.form.on("Routing", {
setup: function (frm) {
frm.set_query("bom_no", "operations", function () {
return {
filters: {
is_phantom_bom: 0,
},
};
});
},
refresh: function (frm) { refresh: function (frm) {
frm.trigger("display_sequence_id_column"); frm.trigger("display_sequence_id_column");
}, },

View File

@@ -3270,6 +3270,14 @@ class TestWorkOrder(IntegrationTestCase):
) )
frappe.db.set_single_value("Stock Settings", "auto_reserve_serial_and_batch", original_auto_reserve) 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): def get_reserved_entries(voucher_no, warehouse=None):
doctype = frappe.qb.DocType("Stock Reservation Entry") doctype = frappe.qb.DocType("Stock Reservation Entry")

View File

@@ -932,6 +932,9 @@ erpnext.work_order = {
if (!(frm.doc.wip_warehouse || frm.doc.fg_warehouse)) { if (!(frm.doc.wip_warehouse || frm.doc.fg_warehouse)) {
frappe.call({ frappe.call({
method: "erpnext.manufacturing.doctype.work_order.work_order.get_default_warehouse", method: "erpnext.manufacturing.doctype.work_order.work_order.get_default_warehouse",
args: {
company: frm.doc.company,
},
callback: function (r) { callback: function (r) {
if (!r.exe) { if (!r.exe) {
frm.set_value("wip_warehouse", r.message.wip_warehouse); frm.set_value("wip_warehouse", r.message.wip_warehouse);

View File

@@ -453,9 +453,9 @@ class WorkOrder(Document):
def set_default_warehouse(self): def set_default_warehouse(self):
if not self.wip_warehouse and not self.skip_transfer: 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: 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): def check_wip_warehouse_skip(self):
if self.skip_transfer and not self.from_wip_warehouse: if self.skip_transfer and not self.from_wip_warehouse:
@@ -2318,13 +2318,14 @@ def make_stock_entry(
@frappe.whitelist() @frappe.whitelist()
def get_default_warehouse(): def get_default_warehouse(company):
doc = frappe.get_cached_doc("Manufacturing Settings") wip, fg, scrap = frappe.get_cached_value(
"Company", company, ["default_wip_warehouse", "default_fg_warehouse", "default_scrap_warehouse"]
)
return { return {
"wip_warehouse": doc.default_wip_warehouse, "wip_warehouse": wip,
"fg_warehouse": doc.default_fg_warehouse, "fg_warehouse": fg,
"scrap_warehouse": doc.default_scrap_warehouse, "scrap_warehouse": scrap,
} }

View File

@@ -21,7 +21,17 @@ def get_exploded_items(bom, data, indent=0, qty=1):
exploded_items = frappe.get_all( exploded_items = frappe.get_all(
"BOM Item", "BOM Item",
filters={"parent": bom}, 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", order_by="idx ASC",
) )
@@ -37,6 +47,7 @@ def get_exploded_items(bom, data, indent=0, qty=1):
"qty": item.qty * qty, "qty": item.qty * qty,
"uom": item.uom, "uom": item.uom,
"description": item.description, "description": item.description,
"is_phantom_item": item.is_phantom_item,
} }
) )
if item.bom_no: if item.bom_no:
@@ -54,6 +65,7 @@ def get_columns():
}, },
{"label": _("Item Name"), "fieldtype": "data", "fieldname": "item_name", "width": 100}, {"label": _("Item Name"), "fieldtype": "data", "fieldname": "item_name", "width": 100},
{"label": _("BOM"), "fieldtype": "Link", "fieldname": "bom", "width": 150, "options": "BOM"}, {"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": _("Qty"), "fieldtype": "data", "fieldname": "qty", "width": 100},
{"label": _("UOM"), "fieldtype": "data", "fieldname": "uom", "width": 100}, {"label": _("UOM"), "fieldtype": "data", "fieldname": "uom", "width": 100},
{"label": _("BOM Level"), "fieldtype": "Int", "fieldname": "bom_level", "width": 100}, {"label": _("BOM Level"), "fieldtype": "Int", "fieldname": "bom_level", "width": 100},

View File

@@ -32,6 +32,7 @@ def get_report_data(last_purchase_rate, required_qty, row, manufacture_details):
return [ return [
row.item_code, row.item_code,
row.description, 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", []), add_quotes=False),
comma_and(manufacture_details.get(row.item_code, {}).get("manufacturer_part", []), add_quotes=False), comma_and(manufacture_details.get(row.item_code, {}).get("manufacturer_part", []), add_quotes=False),
qty_per_unit, qty_per_unit,
@@ -57,6 +58,13 @@ def get_columns():
"fieldtype": "Data", "fieldtype": "Data",
"width": 150, "width": 150,
}, },
{
"fieldname": "from_bom_no",
"label": _("From BOM No"),
"fieldtype": "Link",
"options": "BOM",
"width": 150,
},
{ {
"fieldname": "manufacturer", "fieldname": "manufacturer",
"label": _("Manufacturer"), "label": _("Manufacturer"),
@@ -103,10 +111,7 @@ def get_columns():
def get_bom_data(filters): def get_bom_data(filters):
if filters.get("show_exploded_view"): bom_item_table = "BOM Explosion Item" if filters.get("show_exploded_view") else "BOM Item"
bom_item_table = "BOM Explosion Item"
else:
bom_item_table = "BOM Item"
bom_item = frappe.qb.DocType(bom_item_table) bom_item = frappe.qb.DocType(bom_item_table)
bin = frappe.qb.DocType("Bin") bin = frappe.qb.DocType("Bin")
@@ -118,11 +123,13 @@ def get_bom_data(filters):
.select( .select(
bom_item.item_code, bom_item.item_code,
bom_item.description, bom_item.description,
bom_item.parent.as_("from_bom_no"),
bom_item.qty_consumed_per_unit.as_("qty_per_unit"), bom_item.qty_consumed_per_unit.as_("qty_per_unit"),
IfNull(Sum(bin.actual_qty), 0).as_("actual_qty"), IfNull(Sum(bin.actual_qty), 0).as_("actual_qty"),
) )
.where((bom_item.parent == filters.get("bom")) & (bom_item.parenttype == "BOM")) .where((bom_item.parent == filters.get("bom")) & (bom_item.parenttype == "BOM"))
.groupby(bom_item.item_code) .groupby(bom_item.item_code)
.orderby(bom_item.idx)
) )
if filters.get("warehouse"): if filters.get("warehouse"):
@@ -146,7 +153,36 @@ def get_bom_data(filters):
else: else:
query = query.where(bin.warehouse == filters.get("warehouse")) 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(): def get_manufacturer_records():

View File

@@ -102,6 +102,7 @@ def get_expected_data(bom, qty_to_make):
[ [
bom.items[idx].item_code, bom.items[idx].item_code,
bom.items[idx].item_code, bom.items[idx].item_code,
bom.name,
"", "",
"", "",
float(bom.items[idx].stock_qty / bom.quantity), float(bom.items[idx].stock_qty / bom.quantity),

View File

@@ -22,8 +22,9 @@ def get_columns():
_("Item") + ":Link/Item:150", _("Item") + ":Link/Item:150",
_("Item Name") + "::240", _("Item Name") + "::240",
_("Description") + "::300", _("Description") + "::300",
_("From BOM No") + "::200",
_("BOM Qty") + ":Float:160", _("BOM Qty") + ":Float:160",
_("BOM UoM") + "::160", _("BOM UOM") + "::160",
_("Required Qty") + ":Float:120", _("Required Qty") + ":Float:120",
_("In Stock Qty") + ":Float:120", _("In Stock Qty") + ":Float:120",
_("Enough Parts to Build") + ":Float:200", _("Enough Parts to Build") + ":Float:200",
@@ -72,6 +73,7 @@ def get_bom_stock(filters):
BOM_ITEM.item_code, BOM_ITEM.item_code,
BOM_ITEM.item_name, BOM_ITEM.item_name,
BOM_ITEM.description, BOM_ITEM.description,
BOM.name,
Sum(BOM_ITEM.stock_qty), Sum(BOM_ITEM.stock_qty),
BOM_ITEM.stock_uom, BOM_ITEM.stock_uom,
(Sum(BOM_ITEM.stock_qty) * qty_to_produce) / BOM.quantity, (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")) .where((BOM_ITEM.parent == filters.get("bom")) & (BOM_ITEM.parenttype == "BOM"))
.groupby(BOM_ITEM.item_code) .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

View File

@@ -96,6 +96,7 @@ def get_expected_data(bom, warehouse, qty_to_produce, show_exploded_view=False):
item.item_code, item.item_code,
item.item_name, item.item_name,
item.description, item.description,
bom.name,
item.stock_qty, item.stock_qty,
item.stock_uom, item.stock_uom,
item.stock_qty * qty_to_produce / bom.quantity, 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)) floor(in_stock_qty / (item.stock_qty * qty_to_produce / bom.quantity))
if in_stock_qty if in_stock_qty
else None, else None,
item.bom_no,
item.is_phantom_item,
] ]
) )

View File

@@ -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.set_asset_status_if_not_already_set
erpnext.patches.v15_0.toggle_legacy_controller_for_period_closing erpnext.patches.v15_0.toggle_legacy_controller_for_period_closing
erpnext.patches.v16_0.update_serial_batch_entries 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

View 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)

View File

@@ -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)

View File

@@ -140,6 +140,17 @@ class BOMConfigurator {
}, },
btnClass: "hidden-xs", 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"), label: __("Collapse All"),
click: function (node) { click: function (node) {
@@ -170,6 +181,17 @@ class BOMConfigurator {
}, },
btnClass: "hidden-xs", 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"), label: __(frappe.utils.icon("delete", "sm") + " Item"),
click: function (node) { 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({ let dialog = new frappe.ui.Dialog({
fields: view.events.get_sub_assembly_modal_fields(view, node.is_root), fields: view.events.get_sub_assembly_modal_fields(view, node.is_root, false, phantom),
title: __("Add Sub Assembly"), title: phantom ? __("Add Phantom Item") : __("Add Sub Assembly"),
}); });
view.events.set_query_for_workstation(dialog); view.events.set_query_for_workstation(dialog);
@@ -282,6 +304,7 @@ class BOMConfigurator {
operation: node.data.operation, operation: node.data.operation,
workstation_type: node.data.workstation_type, workstation_type: node.data.workstation_type,
operation_time: node.data.operation_time, operation_time: node.data.operation_time,
phantom: phantom,
}, },
callback: (r) => { callback: (r) => {
view.events.load_tree(r, node); 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 = [ let fields = [
{ {
label: __("Sub Assembly Item"), label: phantom ? __("Phantom Item") : __("Sub Assembly Item"),
fieldname: "item_code", fieldname: "item_code",
fieldtype: "Link", fieldtype: "Link",
options: "Item", options: "Item",
reqd: 1, reqd: 1,
read_only: read_only, read_only: read_only,
filters: {
is_stock_item: !phantom,
},
}, },
{ fieldtype: "Column Break" }, { fieldtype: "Column Break" },
{ {
@@ -320,7 +346,7 @@ class BOMConfigurator {
}, },
]; ];
if (is_root) { if (is_root && !phantom) {
fields.push( fields.push(
...[ ...[
{ fieldtype: "Section Break" }, { fieldtype: "Section Break" },
@@ -384,10 +410,10 @@ class BOMConfigurator {
return fields; return fields;
} }
convert_to_sub_assembly(node, view) { convert_to_sub_assembly(node, view, phantom = false) {
let dialog = new frappe.ui.Dialog({ let dialog = new frappe.ui.Dialog({
fields: view.events.get_sub_assembly_modal_fields(view, node.is_root, true), fields: view.events.get_sub_assembly_modal_fields(view, node.is_root, true, phantom),
title: __("Add Sub Assembly"), title: phantom ? __("Add Phantom Item") : __("Add Sub Assembly"),
}); });
dialog.set_values({ dialog.set_values({
@@ -400,7 +426,9 @@ class BOMConfigurator {
let bom_item = dialog.get_values(); let bom_item = dialog.get_values();
if (!bom_item.item_code) { 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) => { bom_item.items.forEach((d) => {
@@ -425,6 +453,7 @@ class BOMConfigurator {
workstation_type: node.data.workstation_type, workstation_type: node.data.workstation_type,
operation_time: node.data.operation_time, operation_time: node.data.operation_time,
workstation: node.data.workstation, workstation: node.data.workstation,
phantom: phantom,
}, },
callback: (r) => { callback: (r) => {
node.expandable = true; node.expandable = true;

View File

@@ -1,6 +1,16 @@
let beforePrintHandled = false; let beforePrintHandled = false;
frappe.realtime.on("sales_invoice_before_print", (data) => { 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(); const route = frappe.get_route();
if (!beforePrintHandled && route[0] === "print" && route[1] === "Sales Invoice") { if (!beforePrintHandled && route[0] === "print" && route[1] === "Sales Invoice") {

View File

@@ -1796,7 +1796,7 @@ class TestSalesOrder(AccountsTestMixin, IntegrationTestCase):
mr.submit() mr.submit()
# WO from MR # 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 = frappe.get_doc("Work Order", wo_name)
wo.wip_warehouse = "Work In Progress - _TC" wo.wip_warehouse = "Work In Progress - _TC"
wo.skip_transfer = True wo.skip_transfer = True

View File

@@ -117,6 +117,7 @@
"enable_item_wise_inventory_account", "enable_item_wise_inventory_account",
"enable_provisional_accounting_for_non_stock_items", "enable_provisional_accounting_for_non_stock_items",
"default_inventory_account", "default_inventory_account",
"valuation_method",
"column_break_32", "column_break_32",
"stock_adjustment_account", "stock_adjustment_account",
"stock_received_but_not_billed", "stock_received_but_not_billed",
@@ -124,6 +125,10 @@
"default_in_transit_warehouse", "default_in_transit_warehouse",
"manufacturing_section", "manufacturing_section",
"default_operating_cost_account", "default_operating_cost_account",
"column_break_9prc",
"default_wip_warehouse",
"default_fg_warehouse",
"default_scrap_warehouse",
"dashboard_tab" "dashboard_tab"
], ],
"fields": [ "fields": [
@@ -885,6 +890,39 @@
"fieldname": "enable_item_wise_inventory_account", "fieldname": "enable_item_wise_inventory_account",
"fieldtype": "Check", "fieldtype": "Check",
"label": "Enable Item-wise Inventory Account" "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", "icon": "fa fa-building",
@@ -892,7 +930,7 @@
"image_field": "company_logo", "image_field": "company_logo",
"is_tree": 1, "is_tree": 1,
"links": [], "links": [],
"modified": "2025-10-23 13:15:52.411984", "modified": "2025-11-16 16:50:27.624096",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Setup", "module": "Setup",
"name": "Company", "name": "Company",

View File

@@ -59,6 +59,7 @@ class Company(NestedSet):
default_deferred_revenue_account: DF.Link | None default_deferred_revenue_account: DF.Link | None
default_discount_account: DF.Link | None default_discount_account: DF.Link | None
default_expense_account: DF.Link | None default_expense_account: DF.Link | None
default_fg_warehouse: DF.Link | None
default_finance_book: DF.Link | None default_finance_book: DF.Link | None
default_holiday_list: DF.Link | None default_holiday_list: DF.Link | None
default_in_transit_warehouse: DF.Link | None default_in_transit_warehouse: DF.Link | None
@@ -69,8 +70,10 @@ class Company(NestedSet):
default_payable_account: DF.Link | None default_payable_account: DF.Link | None
default_provisional_account: DF.Link | None default_provisional_account: DF.Link | None
default_receivable_account: DF.Link | None default_receivable_account: DF.Link | None
default_scrap_warehouse: DF.Link | None
default_selling_terms: DF.Link | None default_selling_terms: DF.Link | None
default_warehouse_for_sales_return: DF.Link | None default_warehouse_for_sales_return: DF.Link | None
default_wip_warehouse: DF.Link | None
depreciation_cost_center: DF.Link | None depreciation_cost_center: DF.Link | None
depreciation_expense_account: DF.Link | None depreciation_expense_account: DF.Link | None
disposal_account: DF.Link | None disposal_account: DF.Link | None
@@ -113,6 +116,7 @@ class Company(NestedSet):
transactions_annual_history: DF.Code | None transactions_annual_history: DF.Code | None
unrealized_exchange_gain_loss_account: DF.Link | None unrealized_exchange_gain_loss_account: DF.Link | None
unrealized_profit_loss_account: DF.Link | None unrealized_profit_loss_account: DF.Link | None
valuation_method: DF.Literal["FIFO", "Moving Average", "LIFO"]
website: DF.Data | None website: DF.Data | None
write_off_account: DF.Link | None write_off_account: DF.Link | None
# end: auto-generated types # end: auto-generated types
@@ -163,6 +167,32 @@ class Company(NestedSet):
self.validate_parent_company() self.validate_parent_company()
self.set_reporting_currency() self.set_reporting_currency()
self.validate_inventory_account_settings() 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): def validate_inventory_account_settings(self):
doc_before_save = self.get_doc_before_save() doc_before_save = self.get_doc_before_save()

View File

@@ -5,12 +5,11 @@
import os import os
import frappe import frappe
from frappe import _
from frappe.custom.doctype.custom_field.custom_field import create_custom_fields 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.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.doctype.incoterm.incoterm import create_incoterms
from erpnext.setup.utils import identity as _
from .default_success_action import get_default_success_action from .default_success_action import get_default_success_action
@@ -184,33 +183,27 @@ def add_company_to_session_defaults():
def add_standard_navbar_items(): def add_standard_navbar_items():
navbar_settings = frappe.get_single("Navbar Settings") navbar_settings = frappe.get_single("Navbar Settings")
# Translatable strings for below navbar items
__ = _("Documentation")
__ = _("User Forum")
__ = _("Report an Issue")
erpnext_navbar_items = [ erpnext_navbar_items = [
{ {
"item_label": "Documentation", "item_label": _("Documentation"),
"item_type": "Route", "item_type": "Route",
"route": "https://docs.erpnext.com/", "route": "https://docs.erpnext.com/",
"is_standard": 1, "is_standard": 1,
}, },
{ {
"item_label": "User Forum", "item_label": _("User Forum"),
"item_type": "Route", "item_type": "Route",
"route": "https://discuss.frappe.io", "route": "https://discuss.frappe.io",
"is_standard": 1, "is_standard": 1,
}, },
{ {
"item_label": "Frappe School", "item_label": _("Frappe School"),
"item_type": "Route", "item_type": "Route",
"route": "https://frappe.io/school?utm_source=in_app", "route": "https://frappe.io/school?utm_source=in_app",
"is_standard": 1, "is_standard": 1,
}, },
{ {
"item_label": "Report an Issue", "item_label": _("Report an Issue"),
"item_type": "Route", "item_type": "Route",
"route": "https://github.com/frappe/erpnext/issues", "route": "https://github.com/frappe/erpnext/issues",
"is_standard": 1, "is_standard": 1,
@@ -319,26 +312,26 @@ def create_letter_head():
DEFAULT_ROLE_PROFILES = { DEFAULT_ROLE_PROFILES = {
"Inventory": [ _("Inventory"): [
"Stock User", "Stock User",
"Stock Manager", "Stock Manager",
"Item Manager", "Item Manager",
], ],
"Manufacturing": [ _("Manufacturing"): [
"Stock User", "Stock User",
"Manufacturing User", "Manufacturing User",
"Manufacturing Manager", "Manufacturing Manager",
], ],
"Accounts": [ _("Accounts"): [
"Accounts User", "Accounts User",
"Accounts Manager", "Accounts Manager",
], ],
"Sales": [ _("Sales"): [
"Sales User", "Sales User",
"Stock User", "Stock User",
"Sales Manager", "Sales Manager",
], ],
"Purchase": [ _("Purchase"): [
"Item Manager", "Item Manager",
"Stock User", "Stock User",
"Purchase User", "Purchase User",

View File

@@ -15,14 +15,7 @@ from frappe.utils import cstr, getdate
from erpnext.accounts.doctype.account.account import RootNotEditable from erpnext.accounts.doctype.account.account import RootNotEditable
from erpnext.regional.address_template.setup import set_up_address_templates from erpnext.regional.address_template.setup import set_up_address_templates
from erpnext.setup.utils import identity as _
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
def read_lines(filename: str) -> list[str]: def read_lines(filename: str) -> list[str]:
@@ -579,7 +572,7 @@ def create_bank_account(args, demo=False):
return doc return doc
except RootNotEditable: 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: except frappe.DuplicateEntryError:
# bank account same as a CoA entry # bank account same as a CoA entry
pass pass

View File

@@ -232,3 +232,15 @@ def welcome_email():
site_name = get_default_company() or "ERPNext" site_name = get_default_company() or "ERPNext"
title = _("Welcome to {0}").format(site_name) title = _("Welcome to {0}").format(site_name)
return title 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

View File

@@ -495,6 +495,7 @@ frappe.ui.form.on("Material Request", {
method: "erpnext.stock.doctype.material_request.material_request.raise_work_orders", method: "erpnext.stock.doctype.material_request.material_request.raise_work_orders",
args: { args: {
material_request: frm.doc.name, material_request: frm.doc.name,
company: frm.doc.company,
}, },
freeze: true, freeze: true,
callback: function (r) { callback: function (r) {

View File

@@ -833,11 +833,11 @@ def make_stock_entry(source_name, target_doc=None):
@frappe.whitelist() @frappe.whitelist()
def raise_work_orders(material_request): def raise_work_orders(material_request, company):
mr = frappe.get_doc("Material Request", material_request) mr = frappe.get_doc("Material Request", material_request)
errors = [] errors = []
work_orders = [] 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: for d in mr.items:
if (d.stock_qty - d.ordered_qty) > 0: if (d.stock_qty - d.ordered_qty) > 0:

View File

@@ -747,7 +747,7 @@ class TestMaterialRequest(IntegrationTestCase):
(mr.items[0].item_code, mr.items[0].warehouse), (mr.items[0].item_code, mr.items[0].warehouse),
)[0][0] )[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 = frappe.get_doc("Work Order", prod_order[0])
po.wip_warehouse = "_Test Warehouse 1 - _TC" po.wip_warehouse = "_Test Warehouse 1 - _TC"
po.submit() po.submit()
@@ -789,7 +789,7 @@ class TestMaterialRequest(IntegrationTestCase):
self.assertEqual(requested_qty, existing_requested_qty + 120) 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 = frappe.get_doc("Work Order", work_order[0])
wo.qty = 50 wo.qty = 50
wo.wip_warehouse = "_Test Warehouse 1 - _TC" 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 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 = frappe.get_doc("Work Order", work_order[0])
wo.wip_warehouse = "_Test Warehouse 1 - _TC" wo.wip_warehouse = "_Test Warehouse 1 - _TC"
wo.submit() wo.submit()

View File

@@ -514,7 +514,7 @@ class SerialandBatchBundle(Document):
if hasattr(sn_obj, "stock_queue") and sn_obj.stock_queue: if hasattr(sn_obj, "stock_queue") and sn_obj.stock_queue:
stock_queue = parse_json(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: for d in self.entries:
available_qty = 0 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): def set_incoming_rate_for_inward_transaction(self, row=None, save=False, prev_sle=None):
from erpnext.stock.utils import get_valuation_method 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" valuation_field = "valuation_rate"
if self.voucher_type in ["Sales Invoice", "Delivery Note", "Quotation"]: 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): 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") table = frappe.qb.DocType("Stock Reservation Entry")
child_table = frappe.qb.DocType("Serial and Batch 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 = ( query = (
frappe.qb.from_(table) frappe.qb.from_(table)
.join(child_table) .join(child_table)
@@ -2522,7 +2515,6 @@ def get_batch_nos_from_sre(kwargs):
child_table.batch_no, child_table.batch_no,
child_table.warehouse, child_table.warehouse,
Sum(child_table.qty - child_table.delivered_qty).as_("qty"), Sum(child_table.qty - child_table.delivered_qty).as_("qty"),
creation_field,
) )
.where( .where(
(table.docstatus == 1) (table.docstatus == 1)
@@ -2530,7 +2522,6 @@ def get_batch_nos_from_sre(kwargs):
& (child_table.qty != child_table.delivered_qty) & (child_table.qty != child_table.delivered_qty)
) )
.groupby(child_table.batch_no, child_table.warehouse) .groupby(child_table.batch_no, child_table.warehouse)
.orderby("sort_creation", order=order)
.orderby(child_table.batch_no, order=frappe.query_builder.Order.asc) .orderby(child_table.batch_no, order=frappe.query_builder.Order.asc)
) )

View File

@@ -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"]); 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 (this.frm.doc.from_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.to_warehouse) row.t_warehouse = this.frm.doc.to_warehouse;
if (cint(frappe.user_defaults?.use_serial_batch_fields)) { if (cint(frappe.user_defaults?.use_serial_batch_fields)) {
frappe.model.set_value(row.doctype, row.name, "use_serial_batch_fields", 1); frappe.model.set_value(row.doctype, row.name, "use_serial_batch_fields", 1);

View File

@@ -202,6 +202,12 @@ class StockEntry(StockController, SubcontractingInwardController):
for item in self.get("items"): for item in self.get("items"):
item.update(get_bin_details(item.item_code, item.s_warehouse)) 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): def before_validate(self):
from erpnext.stock.doctype.putaway_rule.putaway_rule import apply_putaway_rule 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_work_order()
self.update_disassembled_order() self.update_disassembled_order()
self.adjust_stock_reservation_entries_for_return() self.adjust_stock_reservation_entries_for_return()
self.update_sre_for_subcontracting_delivery() self.update_stock_reservation_entries()
self.update_stock_ledger() self.update_stock_ledger()
self.make_stock_reserve_for_wip_and_fg() self.make_stock_reserve_for_wip_and_fg()
self.reserve_stock_for_subcontracting()
self.update_subcontract_order_supplied_items() self.update_subcontract_order_supplied_items()
self.update_subcontracting_order_status() self.update_subcontracting_order_status()
@@ -324,7 +331,7 @@ class StockEntry(StockController, SubcontractingInwardController):
self.update_transferred_qty() self.update_transferred_qty()
self.update_quality_inspection() self.update_quality_inspection()
self.adjust_stock_reservation_entries_for_return() 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_auto_created_batches()
self.delete_linked_stock_entry() self.delete_linked_stock_entry()
@@ -1889,6 +1896,30 @@ class StockEntry(StockController, SubcontractingInwardController):
pro_doc.set_reserved_qty_for_wip_and_fg(self) 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): def cancel_stock_reserve_for_wip_and_fg(self):
if self.is_stock_reserve_for_work_order(): if self.is_stock_reserve_for_work_order():
pro_doc = frappe.get_doc("Work Order", self.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) self.calculate_rate_and_amount(raise_error_if_no_rate=False)
def set_serial_batch_from_reserved_entry(self): def set_serial_batch_from_reserved_entry(self):
if not self.work_order: if self.work_order and 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 not frappe.get_cached_value("Work Order", self.work_order, "reserve_stock"): if (
return self.purpose not in ["Material Transfer for Manufacture"]
and frappe.db.get_single_value("Manufacturing Settings", "backflush_raw_materials_based_on")
skip_transfer = frappe.get_cached_value("Work Order", self.work_order, "skip_transfer") != "BOM"
and not skip_transfer
if ( ):
self.purpose not in ["Material Transfer for Manufacture"] return
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() reservation_entries = self.get_available_reserved_materials()
if not reservation_entries: if not reservation_entries:
@@ -2252,6 +2278,9 @@ class StockEntry(StockController, SubcontractingInwardController):
new_items_to_add = [] new_items_to_add = []
for d in self.items: 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) key = (d.item_code, d.s_warehouse)
if details := reservation_entries.get(key): if details := reservation_entries.get(key):
original_qty = d.qty original_qty = d.qty
@@ -2363,7 +2392,7 @@ class StockEntry(StockController, SubcontractingInwardController):
) )
.where( .where(
(doctype.docstatus == 1) (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) & (serial_batch_doc.delivered_qty < serial_batch_doc.qty)
) )
.orderby(serial_batch_doc.idx) .orderby(serial_batch_doc.idx)

View File

@@ -84,7 +84,7 @@
"no_copy": 1, "no_copy": 1,
"oldfieldname": "voucher_type", "oldfieldname": "voucher_type",
"oldfieldtype": "Data", "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", "print_width": "150px",
"read_only": 1, "read_only": 1,
"width": "150px" "width": "150px"
@@ -315,8 +315,7 @@
}, },
{ {
"fieldname": "production_section", "fieldname": "production_section",
"fieldtype": "Section Break", "fieldtype": "Section Break"
"label": "Production"
}, },
{ {
"fieldname": "column_break_qdwj", "fieldname": "column_break_qdwj",
@@ -335,7 +334,7 @@
{ {
"fieldname": "transferred_qty", "fieldname": "transferred_qty",
"fieldtype": "Float", "fieldtype": "Float",
"label": "Qty in WIP Warehouse" "label": "Transferred Qty"
} }
], ],
"grid_page_length": 50, "grid_page_length": 50,
@@ -344,7 +343,7 @@
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"is_submittable": 1, "is_submittable": 1,
"links": [], "links": [],
"modified": "2025-10-12 19:48:33.170835", "modified": "2025-11-10 16:09:10.380024",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Stock", "module": "Stock",
"name": "Stock Reservation Entry", "name": "Stock Reservation Entry",

View File

@@ -64,7 +64,12 @@ class StockReservationEntry(Document):
voucher_no: DF.DynamicLink | None voucher_no: DF.DynamicLink | None
voucher_qty: DF.Float voucher_qty: DF.Float
voucher_type: DF.Literal[ 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 warehouse: DF.Link | None
# end: auto-generated types # end: auto-generated types
@@ -338,7 +343,7 @@ class StockReservationEntry(Document):
def validate_reservation_based_on_serial_and_batch(self) -> None: 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`.""" """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 return
if self.reservation_based_on == "Serial and Batch": if self.reservation_based_on == "Serial and Batch":
@@ -460,13 +465,14 @@ class StockReservationEntry(Document):
"Sales Order": "Sales Order Item", "Sales Order": "Sales Order Item",
"Work Order": "Work Order Item", "Work Order": "Work Order Item",
"Production Plan": "Production Plan Sub Assembly Item", "Production Plan": "Production Plan Sub Assembly Item",
"Subcontracting Order": "Subcontracting Order Supplied Item",
}.get(self.voucher_type, None) }.get(self.voucher_type, None)
if item_doctype: if item_doctype:
sre = frappe.qb.DocType("Stock Reservation Entry") sre = frappe.qb.DocType("Stock Reservation Entry")
reserved_qty = ( reserved_qty = (
frappe.qb.from_(sre) frappe.qb.from_(sre)
.select(Sum(sre.reserved_qty)) .select(Sum(sre.reserved_qty - sre.delivered_qty - sre.transferred_qty - sre.consumed_qty))
.where( .where(
(sre.docstatus == 1) (sre.docstatus == 1)
& (sre.voucher_type == self.voucher_type) & (sre.voucher_type == self.voucher_type)
@@ -574,7 +580,7 @@ class StockReservationEntry(Document):
) )
from_voucher_detail_no = None 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 from_voucher_detail_no = self.from_voucher_detail_no
total_reserved_qty = get_sre_reserved_qty_for_voucher_detail_no( total_reserved_qty = get_sre_reserved_qty_for_voucher_detail_no(
@@ -1276,7 +1282,7 @@ class StockReservation:
if not reservation_entries: if not reservation_entries:
return return
entries_to_reserve = frappe._dict({}) entries_to_reserve = frappe._dict()
for row in reservation_entries: for row in reservation_entries:
reserved_qty_field = "reserved_qty" if row.reservation_based_on == "Qty" else "sabb_qty" reserved_qty_field = "reserved_qty" if row.reservation_based_on == "Qty" else "sabb_qty"
delivered_qty_field = ( delivered_qty_field = (
@@ -1293,7 +1299,7 @@ class StockReservation:
if available_qty <= 0: if available_qty <= 0:
continue continue
key = (row.item_code, row.warehouse) key = (row.item_code, row.warehouse, entry.voucher_detail_no)
if key not in entries_to_reserve: if key not in entries_to_reserve:
entries_to_reserve.setdefault( entries_to_reserve.setdefault(
@@ -1303,7 +1309,7 @@ class StockReservation:
"qty_to_reserve": 0.0, "qty_to_reserve": 0.0,
"item_code": row.item_code, "item_code": row.item_code,
"warehouse": row.warehouse, "warehouse": row.warehouse,
"voucher_type": entry.voucher_type, "voucher_type": entry.voucher_type or to_doctype,
"voucher_no": entry.voucher_no, "voucher_no": entry.voucher_no,
"voucher_detail_no": entry.voucher_detail_no, "voucher_detail_no": entry.voucher_detail_no,
"serial_nos": [], "serial_nos": [],
@@ -1475,6 +1481,9 @@ class StockReservation:
.orderby(sabb_entry.idx) .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: if against_fg_item:
query = query.where( query = query.where(
sre.voucher_detail_no.isin( sre.voucher_detail_no.isin(
@@ -1490,9 +1499,14 @@ class StockReservation:
def get_items_to_reserve(self, docnames, from_doctype, to_doctype): def get_items_to_reserve(self, docnames, from_doctype, to_doctype):
field = frappe.scrub(from_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) 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 = ( query = (
frappe.qb.from_(doctype) frappe.qb.from_(doctype)
@@ -1501,11 +1515,12 @@ class StockReservation:
.select( .select(
doctype.name.as_("voucher_no"), doctype.name.as_("voucher_no"),
child_doctype.name.as_("voucher_detail_no"), child_doctype.name.as_("voucher_detail_no"),
child_doctype.item_code, child_doctype[item_code_fieldname].as_("item_code"),
doctype.company, doctype.company,
child_doctype.stock_uom, child_doctype.stock_uom,
) )
.where((doctype.docstatus == 1) & (doctype[field].isin(docnames))) .where((doctype.docstatus == 1) & (doctype[field].isin(docnames)))
.groupby(child_doctype.name)
) )
if to_doctype == "Work Order": if to_doctype == "Work Order":
@@ -1523,6 +1538,15 @@ class StockReservation:
(doctype.qty > doctype.material_transferred_for_manufacturing) (doctype.qty > doctype.material_transferred_for_manufacturing)
& (doctype.status != "Completed") & (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) data = query.run(as_dict=True)
items = [] items = []

View File

@@ -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) range_values = get_range_age(filters, fifo_queue, to_date, item_dict)
check_and_replace_valuations_if_moving_average( 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] 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 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 ( if item_valuation_method == "Moving Average" or (
not item_valuation_method 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): for i in range(0, len(range_values), 2):
range_values[i + 1] = range_values[i] * valuation_rate range_values[i + 1] = range_values[i] * valuation_rate

View File

@@ -201,7 +201,7 @@ def get_columns():
def get_data(filters=None): def get_data(filters=None):
filters = frappe._dict(filters or {}) filters = frappe._dict(filters or {})
item_warehouse_map = get_item_warehouse_combinations(filters) 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 = [] data = []
if item_warehouse_map: if item_warehouse_map:

View File

@@ -861,9 +861,9 @@ class BatchNoValuation(DeprecatedBatchNoValuation):
self.batchwise_valuation_batches = [] self.batchwise_valuation_batches = []
self.non_batchwise_valuation_batches = [] self.non_batchwise_valuation_batches = []
if get_valuation_method(self.sle.item_code) == "Moving Average" and frappe.get_single_value( if get_valuation_method(
"Stock Settings", "do_not_use_batchwise_valuation" 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 self.non_batchwise_valuation_batches = self.batches
return return

View File

@@ -563,7 +563,7 @@ class update_entries_after:
self.company = frappe.get_cached_value("Warehouse", self.args.warehouse, "company") self.company = frappe.get_cached_value("Warehouse", self.args.warehouse, "company")
self.set_precision() 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.new_items_found = False
self.distinct_item_warehouses = args.get("distinct_item_warehouses", frappe._dict()) self.distinct_item_warehouses = args.get("distinct_item_warehouses", frappe._dict())
@@ -1087,7 +1087,7 @@ class update_entries_after:
avg_rate = 0.0 avg_rate = 0.0
for d in sabb_data: 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) amount = incoming_rate * flt(d.qty)
tot_amt += flt(amount) tot_amt += flt(amount)
total_qty += flt(d.qty) 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: if row.serial_no:
return abs(sn_obj.serial_no_incoming_rate.get(row.serial_no, 0.0)) return abs(sn_obj.serial_no_incoming_rate.get(row.serial_no, 0.0))
else: 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: if hasattr(sn_obj, "stock_queue") and sn_obj.stock_queue:
stock_queue = parse_json(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 actual_qty = row.qty
if stock_queue and val_method == "FIFO" and row.batch_no in sn_obj.non_batchwise_valuation_batches: if stock_queue and val_method == "FIFO" and row.batch_no in sn_obj.non_batchwise_valuation_batches:

View File

@@ -302,7 +302,7 @@ def get_incoming_rate(args, raise_error_if_no_rate=True):
return batch_obj.get_incoming_rate() return batch_obj.get_incoming_rate()
else: 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) previous_sle = get_previous_sle(args)
if valuation_method in ("FIFO", "LIFO"): if valuation_method in ("FIFO", "LIFO"):
if previous_sle: if previous_sle:
@@ -374,11 +374,15 @@ def get_avg_purchase_rate(serial_nos):
@frappe.request_cache @frappe.request_cache
def get_valuation_method(item_code): def get_valuation_method(item_code, company=None):
"""get valuation method from item or default""" """get valuation method from item or default"""
val_method = frappe.get_cached_value("Item", item_code, "valuation_method") val_method = frappe.get_cached_value("Item", item_code, "valuation_method")
if not val_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 return val_method

View File

@@ -172,11 +172,279 @@ frappe.ui.form.on("Subcontracting Order", {
__("Status") __("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"); 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) { update_subcontracting_order_status(frm, status) {
frappe.call({ frappe.call({
method: "erpnext.subcontracting.doctype.subcontracting_order.subcontracting_order.update_subcontracting_order_status", method: "erpnext.subcontracting.doctype.subcontracting_order.subcontracting_order.update_subcontracting_order_status",

View File

@@ -36,6 +36,7 @@
"service_items", "service_items",
"raw_materials_supplied_section", "raw_materials_supplied_section",
"set_reserve_warehouse", "set_reserve_warehouse",
"reserve_stock",
"supplied_items", "supplied_items",
"tab_address_and_contact", "tab_address_and_contact",
"supplier_address", "supplier_address",
@@ -62,7 +63,8 @@
"select_print_heading", "select_print_heading",
"column_break_43", "column_break_43",
"letter_head", "letter_head",
"tab_connections" "tab_connections",
"production_plan"
], ],
"fields": [ "fields": [
{ {
@@ -471,6 +473,22 @@
"no_copy": 1, "no_copy": 1,
"options": "Currency", "options": "Currency",
"read_only": 1 "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", "icon": "fa fa-file-text",

View File

@@ -8,6 +8,10 @@ from frappe.utils import flt
from erpnext.buying.utils import check_on_hold_or_closed_status from erpnext.buying.utils import check_on_hold_or_closed_status
from erpnext.controllers.subcontracting_controller import SubcontractingController 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.stock_balance import update_bin_qty
from erpnext.stock.utils import get_bin from erpnext.stock.utils import get_bin
@@ -50,8 +54,10 @@ class SubcontractingOrder(SubcontractingController):
letter_head: DF.Link | None letter_head: DF.Link | None
naming_series: DF.Literal["SC-ORD-.YYYY.-"] naming_series: DF.Literal["SC-ORD-.YYYY.-"]
per_received: DF.Percent per_received: DF.Percent
production_plan: DF.Data | None
project: DF.Link | None project: DF.Link | None
purchase_order: DF.Link purchase_order: DF.Link
reserve_stock: DF.Check
schedule_date: DF.Date | None schedule_date: DF.Date | None
select_print_heading: DF.Link | None select_print_heading: DF.Link | None
service_items: DF.Table[SubcontractingOrderServiceItem] service_items: DF.Table[SubcontractingOrderServiceItem]
@@ -105,6 +111,13 @@ class SubcontractingOrder(SubcontractingController):
frappe.db.get_single_value("Buying Settings", "over_transfer_allowance"), 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): def before_validate(self):
super().before_validate() super().before_validate()
@@ -121,6 +134,7 @@ class SubcontractingOrder(SubcontractingController):
self.update_prevdoc_status() self.update_prevdoc_status()
self.update_status() self.update_status()
self.update_subcontracted_quantity_in_po() self.update_subcontracted_quantity_in_po()
self.reserve_raw_materials()
def on_cancel(self): def on_cancel(self):
self.update_prevdoc_status() self.update_prevdoc_status()
@@ -253,10 +267,10 @@ class SubcontractingOrder(SubcontractingController):
if si.fg_item: if si.fg_item:
item = frappe.get_doc("Item", 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", "Purchase Order Item",
si.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) available_qty = flt(qty) - flt(subcontracted_qty)
@@ -292,6 +306,7 @@ class SubcontractingOrder(SubcontractingController):
"purchase_order_item": si.purchase_order_item, "purchase_order_item": si.purchase_order_item,
"material_request": si.material_request, "material_request": si.material_request,
"material_request_item": si.material_request_item, "material_request_item": si.material_request_item,
"production_plan_sub_assembly_item": production_plan_sub_assembly_item,
} }
) )
else: else:
@@ -362,6 +377,90 @@ class SubcontractingOrder(SubcontractingController):
subcontracted_qty, 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() @frappe.whitelist()
def make_subcontracting_receipt(source_name, target_doc=None): def make_subcontracting_receipt(source_name, target_doc=None):

View File

@@ -4,5 +4,15 @@ from frappe import _
def get_data(): def get_data():
return { return {
"fieldname": "subcontracting_order", "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"],
},
],
} }

View File

@@ -700,6 +700,126 @@ class TestSubcontractingOrder(IntegrationTestCase):
self.assertEqual(sco.supplied_items[0].required_qty, 210.149) 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): def create_subcontracting_order(**args):
args = frappe._dict(args) args = frappe._dict(args)

View File

@@ -55,7 +55,8 @@
"section_break_34", "section_break_34",
"purchase_order_item", "purchase_order_item",
"page_break", "page_break",
"subcontracting_conversion_factor" "subcontracting_conversion_factor",
"production_plan_sub_assembly_item"
], ],
"fields": [ "fields": [
{ {
@@ -407,6 +408,16 @@
"hidden": 1, "hidden": 1,
"label": "Subcontracting Conversion Factor", "label": "Subcontracting Conversion Factor",
"read_only": 1 "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, "grid_page_length": 50,
@@ -414,7 +425,7 @@
"index_web_pages_for_search": 1, "index_web_pages_for_search": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2025-08-10 22:37:39.863628", "modified": "2025-11-03 12:29:45.156101",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Subcontracting", "module": "Subcontracting",
"name": "Subcontracting Order Item", "name": "Subcontracting Order Item",

View File

@@ -35,6 +35,7 @@ class SubcontractingOrderItem(Document):
parent: DF.Data parent: DF.Data
parentfield: DF.Data parentfield: DF.Data
parenttype: DF.Data parenttype: DF.Data
production_plan_sub_assembly_item: DF.Data | None
project: DF.Link | None project: DF.Link | None
purchase_order_item: DF.Data | None purchase_order_item: DF.Data | None
qty: DF.Float qty: DF.Float

View File

@@ -21,6 +21,7 @@
"section_break_13", "section_break_13",
"required_qty", "required_qty",
"supplied_qty", "supplied_qty",
"stock_reserved_qty",
"column_break_16", "column_break_16",
"consumed_qty", "consumed_qty",
"returned_qty", "returned_qty",
@@ -52,7 +53,7 @@
{ {
"fieldname": "stock_uom", "fieldname": "stock_uom",
"fieldtype": "Link", "fieldtype": "Link",
"label": "Stock Uom", "label": "Stock UOM",
"options": "UOM", "options": "UOM",
"read_only": 1 "read_only": 1
}, },
@@ -160,17 +161,28 @@
"no_copy": 1, "no_copy": 1,
"print_hide": 1, "print_hide": 1,
"read_only": 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, "hide_toolbar": 1,
"istable": 1, "istable": 1,
"links": [], "links": [],
"modified": "2024-03-27 13:10:46.680164", "modified": "2025-10-30 16:00:43.379828",
"modified_by": "Administrator", "modified_by": "Administrator",
"module": "Subcontracting", "module": "Subcontracting",
"name": "Subcontracting Order Supplied Item", "name": "Subcontracting Order Supplied Item",
"owner": "Administrator", "owner": "Administrator",
"permissions": [], "permissions": [],
"row_format": "Dynamic",
"sort_field": "creation", "sort_field": "creation",
"sort_order": "DESC", "sort_order": "DESC",
"states": [] "states": []

View File

@@ -28,6 +28,7 @@ class SubcontractingOrderSuppliedItem(Document):
reserve_warehouse: DF.Link | None reserve_warehouse: DF.Link | None
returned_qty: DF.Float returned_qty: DF.Float
rm_item_code: DF.Link | None rm_item_code: DF.Link | None
stock_reserved_qty: DF.Float
stock_uom: DF.Link | None stock_uom: DF.Link | None
supplied_qty: DF.Float supplied_qty: DF.Float
total_supplied_qty: DF.Float total_supplied_qty: DF.Float

View File

@@ -164,6 +164,8 @@ class SubcontractingReceipt(SubcontractingController):
for table_name in ["items", "supplied_items"]: for table_name in ["items", "supplied_items"]:
self.make_bundle_using_old_serial_batch_fields(table_name) self.make_bundle_using_old_serial_batch_fields(table_name)
self.update_stock_reservation_entries()
self.update_stock_ledger() self.update_stock_ledger()
self.make_gl_entries() self.make_gl_entries()
self.repost_future_sle_and_gle() self.repost_future_sle_and_gle()
@@ -189,6 +191,7 @@ class SubcontractingReceipt(SubcontractingController):
self.set_consumed_qty_in_subcontract_order() self.set_consumed_qty_in_subcontract_order()
self.set_subcontracting_order_status(update_bin=False) self.set_subcontracting_order_status(update_bin=False)
self.update_stock_ledger() self.update_stock_ledger()
self.update_stock_reservation_entries()
self.make_gl_entries_on_cancel() self.make_gl_entries_on_cancel()
self.repost_future_sle_and_gle() self.repost_future_sle_and_gle()
self.update_status() self.update_status()
@@ -199,7 +202,7 @@ class SubcontractingReceipt(SubcontractingController):
def reset_raw_materials(self): def reset_raw_materials(self):
self.supplied_items = [] self.supplied_items = []
self.flags.reset_raw_materials = True self.flags.reset_raw_materials = True
self.create_raw_materials_supplied() self.create_raw_materials_supplied_or_received()
def validate_closed_subcontracting_order(self): def validate_closed_subcontracting_order(self):
for item in self.items: for item in self.items:
@@ -853,6 +856,17 @@ class SubcontractingReceipt(SubcontractingController):
if frappe.db.get_single_value("Buying Settings", "auto_create_purchase_receipt"): if frappe.db.get_single_value("Buying Settings", "auto_create_purchase_receipt"):
make_purchase_receipt(self, save=True, notify=True) 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() @frappe.whitelist()
def make_subcontract_return_against_rejected_warehouse(source_name): def make_subcontract_return_against_rejected_warehouse(source_name):