Merge pull request #39880 from frappe/version-15-hotfix
chore: release v15
This commit is contained in:
commit
8c12b3dbe0
@ -56,7 +56,9 @@
|
||||
"Constru\u00e7\u00f5es em Andamento de Im\u00f3veis Destinados \u00e0 Venda": {},
|
||||
"Estoques Destinados \u00e0 Doa\u00e7\u00e3o": {},
|
||||
"Im\u00f3veis Destinados \u00e0 Venda": {},
|
||||
"Insumos (materiais diretos)": {},
|
||||
"Insumos (materiais diretos)": {
|
||||
"account_type": "Stock"
|
||||
},
|
||||
"Insumos Agropecu\u00e1rios": {},
|
||||
"Mercadorias para Revenda": {},
|
||||
"Outras 11": {},
|
||||
@ -146,6 +148,65 @@
|
||||
"root_type": "Asset"
|
||||
},
|
||||
"CUSTOS DE PRODU\u00c7\u00c3O": {
|
||||
"CUSTO DOS PRODUTOS E SERVI\u00c7OS VENDIDOS": {
|
||||
"CUSTO DOS PRODUTOS VENDIDOS": {
|
||||
"CUSTO DOS PRODUTOS VENDIDOS PARA AS DEMAIS ATIVIDADES": {
|
||||
"Custos dos Produtos Vendidos em Geral": {
|
||||
"account_type": "Cost of Goods Sold"
|
||||
},
|
||||
"Outros Custos 4": {},
|
||||
"account_type": "Cost of Goods Sold"
|
||||
},
|
||||
"CUSTO DOS PRODUTOS VENDIDOS PARA ASSIST\u00caNCIA SOCIAL": {
|
||||
"Custos dos Produtos para Assist\u00eancia Social - Gratuidades": {},
|
||||
"Custos dos Produtos para Assist\u00eancia Social - Vendidos": {},
|
||||
"Outras": {}
|
||||
},
|
||||
"CUSTO DOS PRODUTOS VENDIDOS PARA EDUCA\u00c7\u00c3O": {
|
||||
"Custos dos Produtos para Educa\u00e7\u00e3o - Gratuidades": {},
|
||||
"Custos dos Produtos para Educa\u00e7\u00e3o - Vendidos": {},
|
||||
"Outros Custos 6": {}
|
||||
},
|
||||
"CUSTO DOS PRODUTOS VENDIDOS PARA SA\u00daDE": {
|
||||
"Custos dos Produtos para Sa\u00fade - Gratuidades": {},
|
||||
"Custos dos Produtos para Sa\u00fade \u2013 Vendidos": {},
|
||||
"Outros Custos 5": {}
|
||||
},
|
||||
"account_type": "Cost of Goods Sold"
|
||||
},
|
||||
"CUSTO DOS SERVI\u00c7OS PRESTADOS": {
|
||||
"CUSTO DOS SERVI\u00c7OS PRESTADOS PARA AS DEMAIS ATIVIDADES": {
|
||||
"Custo dos Servi\u00e7os Prestados em Geral": {},
|
||||
"Outros Custos": {}
|
||||
},
|
||||
"CUSTO DOS SERVI\u00c7OS PRESTADOS PARA ASSIST\u00caNCIA SOCIAL": {
|
||||
"Custo dos Servi\u00e7os Prestados a Conv\u00eanios/Contratos/Parcerias": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es 1": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es/Subven\u00e7\u00f5es Vinculadas 1": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Gratuidade 1": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Pacientes Particulares": {},
|
||||
"Outros Custos 2": {}
|
||||
},
|
||||
"CUSTO DOS SERVI\u00c7OS PRESTADOS PARA EDUCA\u00c7\u00c3O": {
|
||||
"Custo dos Servi\u00e7os Prestados a Alunos N\u00e3o Bolsistas": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Conv\u00eanios/Contratos/Parcerias (Exceto PROUNI)": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es/Subven\u00e7\u00f5es Vinculadas": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Gratuidade": {},
|
||||
"Custo dos Servi\u00e7os Prestados ao PROUNI": {},
|
||||
"Outros Custos 1": {}
|
||||
},
|
||||
"CUSTO DOS SERVI\u00c7OS PRESTADOS PARA SA\u00daDE": {
|
||||
"Custo dos Servi\u00e7os Prestados a Conv\u00eanios SUS": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Conv\u00eanios/Contratos/Parcerias 1": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es 2": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es/Subven\u00e7\u00f5es Vinculadas 2": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Gratuidade 2": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Pacientes Particulares 1": {},
|
||||
"Outros Custos 3": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"CUSTO DOS BENS E SERVI\u00c7OS PRODUZIDOS": {
|
||||
"CUSTO DOS PRODUTOS DE FABRICA\u00c7\u00c3O PR\u00d3PRIA PRODUZIDOS": {
|
||||
"Alimenta\u00e7\u00e3o do Trabalhador": {},
|
||||
@ -621,7 +682,9 @@
|
||||
"Receita das Unidades Imobili\u00e1rias Vendidas": {},
|
||||
"Receita de Exporta\u00e7\u00e3o Direta de Mercadorias e Produtos": {},
|
||||
"Receita de Exporta\u00e7\u00e3o de Servi\u00e7os": {},
|
||||
"Receita de Loca\u00e7\u00e3o de Bens M\u00f3veis e Im\u00f3veis": {},
|
||||
"Receita de Loca\u00e7\u00e3o de Bens M\u00f3veis e Im\u00f3veis": {
|
||||
"account_type": "Income Account"
|
||||
},
|
||||
"Receita de Vendas de Mercadorias e Produtos a Comercial Exportadora com Fim Espec\u00edfico de Exporta\u00e7\u00e3o": {}
|
||||
}
|
||||
}
|
||||
@ -645,65 +708,6 @@
|
||||
}
|
||||
},
|
||||
"RESULTADO OPERACIONAL": {
|
||||
"CUSTO DOS PRODUTOS E SERVI\u00c7OS VENDIDOS": {
|
||||
"CUSTO DOS PRODUTOS VENDIDOS": {
|
||||
"CUSTO DOS PRODUTOS VENDIDOS PARA AS DEMAIS ATIVIDADES": {
|
||||
"Custos dos Produtos Vendidos em Geral": {
|
||||
"account_type": "Cost of Goods Sold"
|
||||
},
|
||||
"Outros Custos 4": {},
|
||||
"account_type": "Cost of Goods Sold"
|
||||
},
|
||||
"CUSTO DOS PRODUTOS VENDIDOS PARA ASSIST\u00caNCIA SOCIAL": {
|
||||
"Custos dos Produtos para Assist\u00eancia Social - Gratuidades": {},
|
||||
"Custos dos Produtos para Assist\u00eancia Social - Vendidos": {},
|
||||
"Outras": {}
|
||||
},
|
||||
"CUSTO DOS PRODUTOS VENDIDOS PARA EDUCA\u00c7\u00c3O": {
|
||||
"Custos dos Produtos para Educa\u00e7\u00e3o - Gratuidades": {},
|
||||
"Custos dos Produtos para Educa\u00e7\u00e3o - Vendidos": {},
|
||||
"Outros Custos 6": {}
|
||||
},
|
||||
"CUSTO DOS PRODUTOS VENDIDOS PARA SA\u00daDE": {
|
||||
"Custos dos Produtos para Sa\u00fade - Gratuidades": {},
|
||||
"Custos dos Produtos para Sa\u00fade \u2013 Vendidos": {},
|
||||
"Outros Custos 5": {}
|
||||
},
|
||||
"account_type": "Cost of Goods Sold"
|
||||
},
|
||||
"CUSTO DOS SERVI\u00c7OS PRESTADOS": {
|
||||
"CUSTO DOS SERVI\u00c7OS PRESTADOS PARA AS DEMAIS ATIVIDADES": {
|
||||
"Custo dos Servi\u00e7os Prestados em Geral": {},
|
||||
"Outros Custos": {}
|
||||
},
|
||||
"CUSTO DOS SERVI\u00c7OS PRESTADOS PARA ASSIST\u00caNCIA SOCIAL": {
|
||||
"Custo dos Servi\u00e7os Prestados a Conv\u00eanios/Contratos/Parcerias": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es 1": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es/Subven\u00e7\u00f5es Vinculadas 1": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Gratuidade 1": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Pacientes Particulares": {},
|
||||
"Outros Custos 2": {}
|
||||
},
|
||||
"CUSTO DOS SERVI\u00c7OS PRESTADOS PARA EDUCA\u00c7\u00c3O": {
|
||||
"Custo dos Servi\u00e7os Prestados a Alunos N\u00e3o Bolsistas": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Conv\u00eanios/Contratos/Parcerias (Exceto PROUNI)": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es/Subven\u00e7\u00f5es Vinculadas": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Gratuidade": {},
|
||||
"Custo dos Servi\u00e7os Prestados ao PROUNI": {},
|
||||
"Outros Custos 1": {}
|
||||
},
|
||||
"CUSTO DOS SERVI\u00c7OS PRESTADOS PARA SA\u00daDE": {
|
||||
"Custo dos Servi\u00e7os Prestados a Conv\u00eanios SUS": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Conv\u00eanios/Contratos/Parcerias 1": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es 2": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Doa\u00e7\u00f5es/Subven\u00e7\u00f5es Vinculadas 2": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Gratuidade 2": {},
|
||||
"Custo dos Servi\u00e7os Prestados a Pacientes Particulares 1": {},
|
||||
"Outros Custos 3": {}
|
||||
}
|
||||
}
|
||||
},
|
||||
"DESPESAS OPERACIONAIS": {
|
||||
"DESPESAS OPERACIONAIS 1": {
|
||||
"DESPESAS OPERACIONAIS 2": {
|
||||
|
@ -2651,7 +2651,7 @@ def get_advance_journal_entries(
|
||||
|
||||
if order_list:
|
||||
q = q.where(
|
||||
(journal_acc.reference_type == order_doctype) & ((journal_acc.reference_type).isin(order_list))
|
||||
(journal_acc.reference_type == order_doctype) & ((journal_acc.reference_name).isin(order_list))
|
||||
)
|
||||
|
||||
q = q.orderby(journal_entry.posting_date)
|
||||
|
@ -217,8 +217,8 @@ class BuyingController(SubcontractingController):
|
||||
lc_voucher_data = frappe.db.sql(
|
||||
"""select sum(applicable_charges), cost_center
|
||||
from `tabLanded Cost Item`
|
||||
where docstatus = 1 and purchase_receipt_item = %s""",
|
||||
d.name,
|
||||
where docstatus = 1 and purchase_receipt_item = %s and receipt_document = %s""",
|
||||
(d.name, self.name),
|
||||
)
|
||||
d.landed_cost_voucher_amount = lc_voucher_data[0][0] if lc_voucher_data else 0.0
|
||||
if not d.cost_center and lc_voucher_data and lc_voucher_data[0][1]:
|
||||
|
@ -46,6 +46,9 @@ class BatchExpiredError(frappe.ValidationError):
|
||||
class StockController(AccountsController):
|
||||
def validate(self):
|
||||
super(StockController, self).validate()
|
||||
|
||||
if self.docstatus == 0:
|
||||
self.validate_duplicate_serial_and_batch_bundle()
|
||||
if not self.get("is_return"):
|
||||
self.validate_inspection()
|
||||
self.validate_serialized_batch()
|
||||
@ -55,6 +58,32 @@ class StockController(AccountsController):
|
||||
self.validate_internal_transfer()
|
||||
self.validate_putaway_capacity()
|
||||
|
||||
def validate_duplicate_serial_and_batch_bundle(self):
|
||||
if sbb_list := [
|
||||
item.get("serial_and_batch_bundle")
|
||||
for item in self.items
|
||||
if item.get("serial_and_batch_bundle")
|
||||
]:
|
||||
SLE = frappe.qb.DocType("Stock Ledger Entry")
|
||||
data = (
|
||||
frappe.qb.from_(SLE)
|
||||
.select(SLE.voucher_type, SLE.voucher_no, SLE.serial_and_batch_bundle)
|
||||
.where(
|
||||
(SLE.docstatus == 1)
|
||||
& (SLE.serial_and_batch_bundle.notnull())
|
||||
& (SLE.serial_and_batch_bundle.isin(sbb_list))
|
||||
)
|
||||
.limit(1)
|
||||
).run(as_dict=True)
|
||||
|
||||
if data:
|
||||
data = data[0]
|
||||
frappe.throw(
|
||||
_("Serial and Batch Bundle {0} is already used in {1} {2}.").format(
|
||||
frappe.bold(data.serial_and_batch_bundle), data.voucher_type, data.voucher_no
|
||||
)
|
||||
)
|
||||
|
||||
def make_gl_entries(self, gl_entries=None, from_repost=False):
|
||||
if self.docstatus == 2:
|
||||
make_reverse_gl_entries(voucher_type=self.doctype, voucher_no=self.name)
|
||||
@ -156,14 +185,18 @@ class StockController(AccountsController):
|
||||
if self.doctype == "Stock Reconciliation":
|
||||
qty = row.qty
|
||||
type_of_transaction = "Inward"
|
||||
warehouse = row.warehouse
|
||||
else:
|
||||
qty = row.stock_qty
|
||||
qty = row.stock_qty if self.doctype != "Stock Entry" else row.transfer_qty
|
||||
type_of_transaction = get_type_of_transaction(self, row)
|
||||
warehouse = (
|
||||
row.warehouse if self.doctype != "Stock Entry" else row.s_warehouse or row.t_warehouse
|
||||
)
|
||||
|
||||
sn_doc = SerialBatchCreation(
|
||||
{
|
||||
"item_code": row.item_code,
|
||||
"warehouse": row.warehouse,
|
||||
"warehouse": warehouse,
|
||||
"posting_date": self.posting_date,
|
||||
"posting_time": self.posting_time,
|
||||
"voucher_type": self.doctype,
|
||||
@ -938,6 +971,9 @@ class StockController(AccountsController):
|
||||
"Stock Reconciliation",
|
||||
)
|
||||
|
||||
if not frappe.get_all("Putaway Rule", limit=1):
|
||||
return
|
||||
|
||||
if self.doctype == "Purchase Invoice" and self.get("update_stock") == 0:
|
||||
valid_doctype = False
|
||||
|
||||
|
@ -38,7 +38,8 @@
|
||||
"in_list_view": 1,
|
||||
"label": "Item Code",
|
||||
"options": "Item",
|
||||
"reqd": 1
|
||||
"reqd": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "item_name",
|
||||
@ -53,7 +54,8 @@
|
||||
"in_standard_filter": 1,
|
||||
"label": "For Warehouse",
|
||||
"options": "Warehouse",
|
||||
"reqd": 1
|
||||
"reqd": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"columns": 1,
|
||||
@ -141,7 +143,8 @@
|
||||
"fieldname": "from_warehouse",
|
||||
"fieldtype": "Link",
|
||||
"label": "From Warehouse",
|
||||
"options": "Warehouse"
|
||||
"options": "Warehouse",
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"fetch_from": "item_code.safety_stock",
|
||||
@ -199,7 +202,7 @@
|
||||
],
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2023-09-12 12:09:08.358326",
|
||||
"modified": "2024-02-11 16:21:11.977018",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Material Request Plan Item",
|
||||
|
@ -298,7 +298,8 @@
|
||||
"no_copy": 1,
|
||||
"options": "\nDraft\nSubmitted\nNot Started\nIn Process\nCompleted\nClosed\nCancelled\nMaterial Requested",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
"read_only": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "amended_from",
|
||||
@ -436,7 +437,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2023-12-26 16:31:13.740777",
|
||||
"modified": "2024-02-11 15:42:47.642481",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Production Plan",
|
||||
|
@ -312,9 +312,10 @@ class ProductionPlan(Document):
|
||||
so_item.parent,
|
||||
so_item.item_code,
|
||||
so_item.warehouse,
|
||||
(
|
||||
(so_item.qty - so_item.work_order_qty - so_item.delivered_qty) * so_item.conversion_factor
|
||||
).as_("pending_qty"),
|
||||
so_item.qty,
|
||||
so_item.work_order_qty,
|
||||
so_item.delivered_qty,
|
||||
so_item.conversion_factor,
|
||||
so_item.description,
|
||||
so_item.name,
|
||||
so_item.bom_no,
|
||||
@ -337,6 +338,11 @@ class ProductionPlan(Document):
|
||||
|
||||
items = items_query.run(as_dict=True)
|
||||
|
||||
for item in items:
|
||||
item.pending_qty = (
|
||||
flt(item.qty) - max(item.work_order_qty, item.delivered_qty, 0) * item.conversion_factor
|
||||
)
|
||||
|
||||
pi = frappe.qb.DocType("Packed Item")
|
||||
|
||||
packed_items_query = (
|
||||
@ -646,7 +652,10 @@ class ProductionPlan(Document):
|
||||
"project": self.project,
|
||||
}
|
||||
|
||||
key = (d.item_code, d.sales_order, d.warehouse)
|
||||
key = (d.item_code, d.sales_order, d.sales_order_item, d.warehouse)
|
||||
if self.combine_items:
|
||||
key = (d.item_code, d.sales_order, d.warehouse)
|
||||
|
||||
if not d.sales_order:
|
||||
key = (d.name, d.item_code, d.warehouse)
|
||||
|
||||
@ -1762,23 +1771,23 @@ def get_reserved_qty_for_production_plan(item_code, warehouse):
|
||||
return reserved_qty_for_production_plan - reserved_qty_for_production
|
||||
|
||||
|
||||
@frappe.request_cache
|
||||
def get_non_completed_production_plans():
|
||||
table = frappe.qb.DocType("Production Plan")
|
||||
child = frappe.qb.DocType("Production Plan Item")
|
||||
|
||||
query = (
|
||||
return (
|
||||
frappe.qb.from_(table)
|
||||
.inner_join(child)
|
||||
.on(table.name == child.parent)
|
||||
.select(table.name)
|
||||
.distinct()
|
||||
.where(
|
||||
(table.docstatus == 1)
|
||||
& (table.status.notin(["Completed", "Closed"]))
|
||||
& (child.planned_qty > child.ordered_qty)
|
||||
)
|
||||
).run(as_dict=True)
|
||||
|
||||
return list(set([d.name for d in query]))
|
||||
).run(pluck="name")
|
||||
|
||||
|
||||
def get_raw_materials_of_sub_assembly_items(
|
||||
|
@ -447,7 +447,8 @@
|
||||
"no_copy": 1,
|
||||
"options": "Production Plan",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
"read_only": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "production_plan_item",
|
||||
@ -592,7 +593,7 @@
|
||||
"image_field": "image",
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2023-08-11 18:35:49.852069",
|
||||
"modified": "2024-02-11 15:47:13.454422",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Work Order",
|
||||
|
@ -36,7 +36,8 @@
|
||||
"fieldtype": "Link",
|
||||
"in_list_view": 1,
|
||||
"label": "Item Code",
|
||||
"options": "Item"
|
||||
"options": "Item",
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"fieldname": "source_warehouse",
|
||||
@ -141,7 +142,7 @@
|
||||
],
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2022-09-28 10:50:43.512562",
|
||||
"modified": "2024-02-11 15:45:32.318374",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Manufacturing",
|
||||
"name": "Work Order Item",
|
||||
|
@ -20,6 +20,7 @@ from erpnext.manufacturing.doctype.blanket_order.test_blanket_order import make_
|
||||
from erpnext.selling.doctype.product_bundle.test_product_bundle import make_product_bundle
|
||||
from erpnext.selling.doctype.sales_order.sales_order import (
|
||||
WarehouseRequired,
|
||||
create_pick_list,
|
||||
make_delivery_note,
|
||||
make_material_request,
|
||||
make_raw_material_request,
|
||||
@ -1973,6 +1974,83 @@ class TestSalesOrder(FrappeTestCase):
|
||||
self.assertEqual(so.items[0].rate, scenario.get("expected_rate"))
|
||||
self.assertEqual(so.packed_items[0].rate, scenario.get("expected_rate"))
|
||||
|
||||
def test_pick_list_without_rejected_materials(self):
|
||||
serial_and_batch_item = make_item(
|
||||
"_Test Serial and Batch Item for Rejected Materials",
|
||||
properties={
|
||||
"has_serial_no": 1,
|
||||
"has_batch_no": 1,
|
||||
"create_new_batch": 1,
|
||||
"batch_number_series": "BAT-TSBIFRM-.#####",
|
||||
"serial_no_series": "SN-TSBIFRM-.#####",
|
||||
},
|
||||
).name
|
||||
|
||||
serial_item = make_item(
|
||||
"_Test Serial Item for Rejected Materials",
|
||||
properties={
|
||||
"has_serial_no": 1,
|
||||
"serial_no_series": "SN-TSIFRM-.#####",
|
||||
},
|
||||
).name
|
||||
|
||||
batch_item = make_item(
|
||||
"_Test Batch Item for Rejected Materials",
|
||||
properties={
|
||||
"has_batch_no": 1,
|
||||
"create_new_batch": 1,
|
||||
"batch_number_series": "BAT-TBIFRM-.#####",
|
||||
},
|
||||
).name
|
||||
|
||||
normal_item = make_item("_Test Normal Item for Rejected Materials").name
|
||||
|
||||
warehouse = "_Test Warehouse - _TC"
|
||||
rejected_warehouse = "_Test Dummy Rejected Warehouse - _TC"
|
||||
|
||||
if not frappe.db.exists("Warehouse", rejected_warehouse):
|
||||
frappe.get_doc(
|
||||
{
|
||||
"doctype": "Warehouse",
|
||||
"warehouse_name": rejected_warehouse,
|
||||
"company": "_Test Company",
|
||||
"warehouse_group": "_Test Warehouse Group",
|
||||
"is_rejected_warehouse": 1,
|
||||
}
|
||||
).insert()
|
||||
|
||||
se = make_stock_entry(item_code=normal_item, qty=1, to_warehouse=warehouse, do_not_submit=True)
|
||||
for item in [serial_and_batch_item, serial_item, batch_item]:
|
||||
se.append("items", {"item_code": item, "qty": 1, "t_warehouse": warehouse})
|
||||
|
||||
se.save()
|
||||
se.submit()
|
||||
|
||||
se = make_stock_entry(
|
||||
item_code=normal_item, qty=1, to_warehouse=rejected_warehouse, do_not_submit=True
|
||||
)
|
||||
for item in [serial_and_batch_item, serial_item, batch_item]:
|
||||
se.append("items", {"item_code": item, "qty": 1, "t_warehouse": rejected_warehouse})
|
||||
|
||||
se.save()
|
||||
se.submit()
|
||||
|
||||
so = make_sales_order(item_code=normal_item, qty=2, do_not_submit=True)
|
||||
|
||||
for item in [serial_and_batch_item, serial_item, batch_item]:
|
||||
so.append("items", {"item_code": item, "qty": 2, "warehouse": warehouse})
|
||||
|
||||
so.save()
|
||||
so.submit()
|
||||
|
||||
pick_list = create_pick_list(so.name)
|
||||
|
||||
pick_list.save()
|
||||
for row in pick_list.locations:
|
||||
self.assertEqual(row.qty, 1.0)
|
||||
self.assertFalse(row.warehouse == rejected_warehouse)
|
||||
self.assertTrue(row.warehouse == warehouse)
|
||||
|
||||
|
||||
def automatically_fetch_payment_terms(enable=1):
|
||||
accounts_settings = frappe.get_doc("Accounts Settings")
|
||||
|
@ -1122,6 +1122,7 @@ def validate_cancelled_item(item_code, docstatus=None):
|
||||
frappe.throw(_("Item {0} is cancelled").format(item_code))
|
||||
|
||||
|
||||
@frappe.request_cache
|
||||
def get_last_purchase_details(item_code, doc_name=None, conversion_rate=1.0):
|
||||
"""returns last purchase details in stock uom"""
|
||||
# get last purchase order item details
|
||||
|
@ -65,6 +65,7 @@ class LandedCostVoucher(Document):
|
||||
def validate(self):
|
||||
self.check_mandatory()
|
||||
self.validate_receipt_documents()
|
||||
self.validate_line_items()
|
||||
init_landed_taxes_and_totals(self)
|
||||
self.set_total_taxes_and_charges()
|
||||
if not self.get("items"):
|
||||
@ -72,6 +73,26 @@ class LandedCostVoucher(Document):
|
||||
|
||||
self.set_applicable_charges_on_item()
|
||||
|
||||
def validate_line_items(self):
|
||||
for d in self.get("items"):
|
||||
if (
|
||||
d.docstatus == 0
|
||||
and d.purchase_receipt_item
|
||||
and not frappe.db.exists(
|
||||
d.receipt_document_type + " Item",
|
||||
{"name": d.purchase_receipt_item, "parent": d.receipt_document},
|
||||
)
|
||||
):
|
||||
frappe.throw(
|
||||
_("Row {0}: {2} Item {1} does not exist in {2} {3}").format(
|
||||
d.idx,
|
||||
frappe.bold(d.purchase_receipt_item),
|
||||
d.receipt_document_type,
|
||||
frappe.bold(d.receipt_document),
|
||||
),
|
||||
title=_("Incorrect Reference Document (Purchase Receipt Item)"),
|
||||
)
|
||||
|
||||
def check_mandatory(self):
|
||||
if not self.get("purchase_receipts"):
|
||||
frappe.throw(_("Please enter Receipt Document"))
|
||||
|
@ -228,9 +228,17 @@ frappe.ui.form.on('Material Request', {
|
||||
const qty_fields = ['actual_qty', 'projected_qty', 'min_order_qty'];
|
||||
|
||||
if(!r.exc) {
|
||||
$.each(r.message, function(k, v) {
|
||||
if(!d[k] || in_list(qty_fields, k)) d[k] = v;
|
||||
$.each(r.message, function(key, value) {
|
||||
if(!d[key] || qty_fields.includes(key)) {
|
||||
d[key] = value;
|
||||
}
|
||||
});
|
||||
|
||||
if (d.price_list_rate != r.message.price_list_rate) {
|
||||
d.price_list_rate = r.message.price_list_rate;
|
||||
|
||||
frappe.model.set_value(d.doctype, d.name, "rate", d.price_list_rate);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -432,7 +440,6 @@ frappe.ui.form.on("Material Request Item", {
|
||||
item.amount = flt(item.qty) * flt(item.rate);
|
||||
frappe.model.set_value(doctype, name, "amount", item.amount);
|
||||
refresh_field("amount", item.name, item.parentfield);
|
||||
frm.events.get_item_data(frm, item, false);
|
||||
},
|
||||
|
||||
item_code: function(frm, doctype, name) {
|
||||
@ -452,7 +459,12 @@ frappe.ui.form.on("Material Request Item", {
|
||||
set_schedule_date(frm);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
conversion_factor: function(frm, doctype, name) {
|
||||
const item = locals[doctype][name];
|
||||
frm.events.get_item_data(frm, item, false);
|
||||
},
|
||||
});
|
||||
|
||||
erpnext.buying.MaterialRequestController = class MaterialRequestController extends erpnext.buying.BuyingController {
|
||||
|
@ -35,6 +35,7 @@
|
||||
"received_qty",
|
||||
"rate_and_amount_section_break",
|
||||
"rate",
|
||||
"price_list_rate",
|
||||
"col_break3",
|
||||
"amount",
|
||||
"accounting_details_section",
|
||||
@ -473,13 +474,22 @@
|
||||
"fieldtype": "Link",
|
||||
"label": "WIP Composite Asset",
|
||||
"options": "Asset"
|
||||
},
|
||||
{
|
||||
"fieldname": "price_list_rate",
|
||||
"fieldtype": "Currency",
|
||||
"hidden": 1,
|
||||
"label": "Price List Rate",
|
||||
"options": "currency",
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"index_web_pages_for_search": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2023-11-14 18:37:59.599115",
|
||||
"modified": "2024-02-08 16:30:56.137858",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Material Request Item",
|
||||
|
@ -41,6 +41,7 @@ class MaterialRequestItem(Document):
|
||||
parent: DF.Data
|
||||
parentfield: DF.Data
|
||||
parenttype: DF.Data
|
||||
price_list_rate: DF.Currency
|
||||
production_plan: DF.Link | None
|
||||
project: DF.Link | None
|
||||
projected_qty: DF.Float
|
||||
|
@ -77,6 +77,9 @@ frappe.ui.form.on('Pick List', {
|
||||
},
|
||||
freeze: 1,
|
||||
freeze_message: __("Setting Item Locations..."),
|
||||
callback(r) {
|
||||
refresh_field("locations");
|
||||
}
|
||||
});
|
||||
}
|
||||
},
|
||||
|
@ -16,6 +16,7 @@
|
||||
"for_qty",
|
||||
"column_break_4",
|
||||
"parent_warehouse",
|
||||
"consider_rejected_warehouses",
|
||||
"get_item_locations",
|
||||
"section_break_6",
|
||||
"scan_barcode",
|
||||
@ -184,11 +185,18 @@
|
||||
"report_hide": 1,
|
||||
"reqd": 1,
|
||||
"search_index": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"description": "Enable it if users want to consider rejected materials to dispatch.",
|
||||
"fieldname": "consider_rejected_warehouses",
|
||||
"fieldtype": "Check",
|
||||
"label": "Consider Rejected Warehouses"
|
||||
}
|
||||
],
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2024-02-01 16:17:44.877426",
|
||||
"modified": "2024-02-02 16:17:44.877426",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Pick List",
|
||||
@ -260,4 +268,4 @@
|
||||
"sort_order": "DESC",
|
||||
"states": [],
|
||||
"track_changes": 1
|
||||
}
|
||||
}
|
||||
|
@ -348,9 +348,9 @@ class PickList(Document):
|
||||
picked_items_details = self.get_picked_items_details(items)
|
||||
self.item_location_map = frappe._dict()
|
||||
|
||||
from_warehouses = None
|
||||
from_warehouses = [self.parent_warehouse] if self.parent_warehouse else []
|
||||
if self.parent_warehouse:
|
||||
from_warehouses = get_descendants_of("Warehouse", self.parent_warehouse)
|
||||
from_warehouses.extend(get_descendants_of("Warehouse", self.parent_warehouse))
|
||||
|
||||
# Create replica before resetting, to handle empty table on update after submit.
|
||||
locations_replica = self.get("locations")
|
||||
@ -369,6 +369,7 @@ class PickList(Document):
|
||||
self.item_count_map.get(item_code),
|
||||
self.company,
|
||||
picked_item_details=picked_items_details.get(item_code),
|
||||
consider_rejected_warehouses=self.consider_rejected_warehouses,
|
||||
),
|
||||
)
|
||||
|
||||
@ -710,6 +711,7 @@ def get_available_item_locations(
|
||||
company,
|
||||
ignore_validation=False,
|
||||
picked_item_details=None,
|
||||
consider_rejected_warehouses=False,
|
||||
):
|
||||
locations = []
|
||||
total_picked_qty = (
|
||||
@ -725,18 +727,34 @@ def get_available_item_locations(
|
||||
required_qty,
|
||||
company,
|
||||
total_picked_qty,
|
||||
consider_rejected_warehouses=consider_rejected_warehouses,
|
||||
)
|
||||
elif has_serial_no:
|
||||
locations = get_available_item_locations_for_serialized_item(
|
||||
item_code, from_warehouses, required_qty, company, total_picked_qty
|
||||
item_code,
|
||||
from_warehouses,
|
||||
required_qty,
|
||||
company,
|
||||
total_picked_qty,
|
||||
consider_rejected_warehouses=consider_rejected_warehouses,
|
||||
)
|
||||
elif has_batch_no:
|
||||
locations = get_available_item_locations_for_batched_item(
|
||||
item_code, from_warehouses, required_qty, company, total_picked_qty
|
||||
item_code,
|
||||
from_warehouses,
|
||||
required_qty,
|
||||
company,
|
||||
total_picked_qty,
|
||||
consider_rejected_warehouses=consider_rejected_warehouses,
|
||||
)
|
||||
else:
|
||||
locations = get_available_item_locations_for_other_item(
|
||||
item_code, from_warehouses, required_qty, company, total_picked_qty
|
||||
item_code,
|
||||
from_warehouses,
|
||||
required_qty,
|
||||
company,
|
||||
total_picked_qty,
|
||||
consider_rejected_warehouses=consider_rejected_warehouses,
|
||||
)
|
||||
|
||||
total_qty_available = sum(location.get("qty") for location in locations)
|
||||
@ -775,6 +793,7 @@ def get_available_item_locations_for_serial_and_batched_item(
|
||||
required_qty,
|
||||
company,
|
||||
total_picked_qty=0,
|
||||
consider_rejected_warehouses=False,
|
||||
):
|
||||
# Get batch nos by FIFO
|
||||
locations = get_available_item_locations_for_batched_item(
|
||||
@ -782,6 +801,7 @@ def get_available_item_locations_for_serial_and_batched_item(
|
||||
from_warehouses,
|
||||
required_qty,
|
||||
company,
|
||||
consider_rejected_warehouses=consider_rejected_warehouses,
|
||||
)
|
||||
|
||||
if locations:
|
||||
@ -811,7 +831,12 @@ def get_available_item_locations_for_serial_and_batched_item(
|
||||
|
||||
|
||||
def get_available_item_locations_for_serialized_item(
|
||||
item_code, from_warehouses, required_qty, company, total_picked_qty=0
|
||||
item_code,
|
||||
from_warehouses,
|
||||
required_qty,
|
||||
company,
|
||||
total_picked_qty=0,
|
||||
consider_rejected_warehouses=False,
|
||||
):
|
||||
picked_serial_nos = get_picked_serial_nos(item_code, from_warehouses)
|
||||
|
||||
@ -828,6 +853,10 @@ def get_available_item_locations_for_serialized_item(
|
||||
else:
|
||||
query = query.where(Coalesce(sn.warehouse, "") != "")
|
||||
|
||||
if not consider_rejected_warehouses:
|
||||
if rejected_warehouses := get_rejected_warehouses():
|
||||
query = query.where(sn.warehouse.notin(rejected_warehouses))
|
||||
|
||||
serial_nos = query.run(as_list=True)
|
||||
|
||||
warehouse_serial_nos_map = frappe._dict()
|
||||
@ -860,7 +889,12 @@ def get_available_item_locations_for_serialized_item(
|
||||
|
||||
|
||||
def get_available_item_locations_for_batched_item(
|
||||
item_code, from_warehouses, required_qty, company, total_picked_qty=0
|
||||
item_code,
|
||||
from_warehouses,
|
||||
required_qty,
|
||||
company,
|
||||
total_picked_qty=0,
|
||||
consider_rejected_warehouses=False,
|
||||
):
|
||||
locations = []
|
||||
data = get_auto_batch_nos(
|
||||
@ -875,7 +909,14 @@ def get_available_item_locations_for_batched_item(
|
||||
)
|
||||
|
||||
warehouse_wise_batches = frappe._dict()
|
||||
rejected_warehouses = get_rejected_warehouses()
|
||||
|
||||
for d in data:
|
||||
if (
|
||||
not consider_rejected_warehouses and rejected_warehouses and d.warehouse in rejected_warehouses
|
||||
):
|
||||
continue
|
||||
|
||||
if d.warehouse not in warehouse_wise_batches:
|
||||
warehouse_wise_batches.setdefault(d.warehouse, defaultdict(float))
|
||||
|
||||
@ -898,7 +939,12 @@ def get_available_item_locations_for_batched_item(
|
||||
|
||||
|
||||
def get_available_item_locations_for_other_item(
|
||||
item_code, from_warehouses, required_qty, company, total_picked_qty=0
|
||||
item_code,
|
||||
from_warehouses,
|
||||
required_qty,
|
||||
company,
|
||||
total_picked_qty=0,
|
||||
consider_rejected_warehouses=False,
|
||||
):
|
||||
bin = frappe.qb.DocType("Bin")
|
||||
query = (
|
||||
@ -915,6 +961,10 @@ def get_available_item_locations_for_other_item(
|
||||
wh = frappe.qb.DocType("Warehouse")
|
||||
query = query.from_(wh).where((bin.warehouse == wh.name) & (wh.company == company))
|
||||
|
||||
if not consider_rejected_warehouses:
|
||||
if rejected_warehouses := get_rejected_warehouses():
|
||||
query = query.where(bin.warehouse.notin(rejected_warehouses))
|
||||
|
||||
item_locations = query.run(as_dict=True)
|
||||
|
||||
return item_locations
|
||||
@ -1236,3 +1286,15 @@ def update_common_item_properties(item, location):
|
||||
item.serial_no = location.serial_no
|
||||
item.batch_no = location.batch_no
|
||||
item.material_request_item = location.material_request_item
|
||||
|
||||
|
||||
def get_rejected_warehouses():
|
||||
if not hasattr(frappe.local, "rejected_warehouses"):
|
||||
frappe.local.rejected_warehouses = []
|
||||
|
||||
if not frappe.local.rejected_warehouses:
|
||||
frappe.local.rejected_warehouses = frappe.get_all(
|
||||
"Warehouse", filters={"is_rejected_warehouse": 1}, pluck="name"
|
||||
)
|
||||
|
||||
return frappe.local.rejected_warehouses
|
||||
|
@ -684,9 +684,7 @@ class PurchaseReceipt(BuyingController):
|
||||
)
|
||||
|
||||
stock_value_diff = (
|
||||
flt(d.base_net_amount)
|
||||
+ flt(d.item_tax_amount / self.conversion_rate)
|
||||
+ flt(d.landed_cost_voucher_amount)
|
||||
flt(d.base_net_amount) + flt(d.item_tax_amount) + flt(d.landed_cost_voucher_amount)
|
||||
)
|
||||
elif warehouse_account.get(d.warehouse):
|
||||
stock_value_diff = get_stock_value_difference(self.name, d.name, d.warehouse)
|
||||
|
@ -4,7 +4,7 @@
|
||||
import json
|
||||
|
||||
import frappe
|
||||
from frappe.tests.utils import FrappeTestCase
|
||||
from frappe.tests.utils import FrappeTestCase, change_settings
|
||||
from frappe.utils import add_days, add_to_date, flt, nowdate, nowtime, today
|
||||
|
||||
from erpnext.stock.doctype.item.test_item import make_item
|
||||
@ -521,6 +521,24 @@ class TestSerialandBatchBundle(FrappeTestCase):
|
||||
make_serial_nos(item_code, serial_nos)
|
||||
self.assertTrue(frappe.db.exists("Serial No", serial_no_id))
|
||||
|
||||
@change_settings("Stock Settings", {"auto_create_serial_and_batch_bundle_for_outward": 1})
|
||||
def test_duplicate_serial_and_batch_bundle(self):
|
||||
from erpnext.stock.doctype.purchase_receipt.test_purchase_receipt import make_purchase_receipt
|
||||
|
||||
item_code = make_item(properties={"is_stock_item": 1, "has_serial_no": 1}).name
|
||||
|
||||
serial_no = f"{item_code}-001"
|
||||
serial_nos = [{"serial_no": serial_no, "qty": 1}]
|
||||
make_serial_nos(item_code, serial_nos)
|
||||
|
||||
pr1 = make_purchase_receipt(item=item_code, qty=1, rate=500, serial_no=[serial_no])
|
||||
pr2 = make_purchase_receipt(item=item_code, qty=1, rate=500, do_not_save=True)
|
||||
|
||||
pr1.reload()
|
||||
pr2.items[0].serial_and_batch_bundle = pr1.items[0].serial_and_batch_bundle
|
||||
|
||||
self.assertRaises(frappe.exceptions.ValidationError, pr2.save)
|
||||
|
||||
|
||||
def get_batch_from_bundle(bundle):
|
||||
from erpnext.stock.serial_batch_bundle import get_batch_nos
|
||||
|
@ -1025,6 +1025,9 @@ class StockEntry(StockController):
|
||||
already_picked_serial_nos = []
|
||||
|
||||
for row in self.items:
|
||||
if row.use_serial_batch_fields and (row.serial_no or row.batch_no):
|
||||
continue
|
||||
|
||||
if not row.s_warehouse:
|
||||
continue
|
||||
|
||||
@ -1032,7 +1035,7 @@ class StockEntry(StockController):
|
||||
continue
|
||||
|
||||
bundle_doc = None
|
||||
if row.serial_and_batch_bundle and abs(row.qty) != abs(
|
||||
if row.serial_and_batch_bundle and abs(row.transfer_qty) != abs(
|
||||
frappe.get_cached_value("Serial and Batch Bundle", row.serial_and_batch_bundle, "total_qty")
|
||||
):
|
||||
bundle_doc = SerialBatchCreation(
|
||||
@ -1042,7 +1045,7 @@ class StockEntry(StockController):
|
||||
"serial_and_batch_bundle": row.serial_and_batch_bundle,
|
||||
"type_of_transaction": "Outward",
|
||||
"ignore_serial_nos": already_picked_serial_nos,
|
||||
"qty": row.qty * -1,
|
||||
"qty": row.transfer_qty * -1,
|
||||
}
|
||||
).update_serial_and_batch_entries()
|
||||
elif not row.serial_and_batch_bundle:
|
||||
@ -1054,7 +1057,7 @@ class StockEntry(StockController):
|
||||
"posting_time": self.posting_time,
|
||||
"voucher_type": self.doctype,
|
||||
"voucher_detail_no": row.name,
|
||||
"qty": row.qty * -1,
|
||||
"qty": row.transfer_qty * -1,
|
||||
"ignore_serial_nos": already_picked_serial_nos,
|
||||
"type_of_transaction": "Outward",
|
||||
"company": self.company,
|
||||
|
@ -92,9 +92,6 @@ def make_stock_entry(**args):
|
||||
else:
|
||||
args.qty = cint(args.qty)
|
||||
|
||||
if args.serial_no or args.batch_no:
|
||||
args.use_serial_batch_fields = True
|
||||
|
||||
# purpose
|
||||
if not args.purpose:
|
||||
if args.source and args.target:
|
||||
@ -136,7 +133,7 @@ def make_stock_entry(**args):
|
||||
serial_number = args.serial_no
|
||||
|
||||
bundle_id = None
|
||||
if args.serial_no or args.batch_no or args.batches:
|
||||
if not args.use_serial_batch_fields and (args.serial_no or args.batch_no or args.batches):
|
||||
batches = frappe._dict({})
|
||||
if args.batch_no:
|
||||
batches = frappe._dict({args.batch_no: args.qty})
|
||||
@ -164,7 +161,11 @@ def make_stock_entry(**args):
|
||||
.name
|
||||
)
|
||||
|
||||
args.serial_no = serial_number
|
||||
args["serial_no"] = ""
|
||||
args["batch_no"] = ""
|
||||
|
||||
else:
|
||||
args.serial_no = serial_number
|
||||
|
||||
s.append(
|
||||
"items",
|
||||
|
@ -1587,6 +1587,7 @@ class TestStockEntry(FrappeTestCase):
|
||||
qty=4,
|
||||
to_warehouse="_Test Warehouse - _TC",
|
||||
batch_no=batch.name,
|
||||
use_serial_batch_fields=1,
|
||||
do_not_save=True,
|
||||
)
|
||||
|
||||
@ -1745,6 +1746,51 @@ class TestStockEntry(FrappeTestCase):
|
||||
mr.cancel()
|
||||
mr.delete()
|
||||
|
||||
def test_use_serial_and_batch_fields(self):
|
||||
item = make_item(
|
||||
"Test Use Serial and Batch Item SN Item",
|
||||
{"has_serial_no": 1, "is_stock_item": 1},
|
||||
)
|
||||
|
||||
serial_nos = [
|
||||
"Test Use Serial and Batch Item SN Item - SN 001",
|
||||
"Test Use Serial and Batch Item SN Item - SN 002",
|
||||
]
|
||||
|
||||
se = make_stock_entry(
|
||||
item_code=item.name,
|
||||
qty=2,
|
||||
to_warehouse="_Test Warehouse - _TC",
|
||||
use_serial_batch_fields=1,
|
||||
serial_no="\n".join(serial_nos),
|
||||
)
|
||||
|
||||
self.assertTrue(se.items[0].use_serial_batch_fields)
|
||||
self.assertFalse(se.items[0].serial_no)
|
||||
self.assertTrue(se.items[0].serial_and_batch_bundle)
|
||||
|
||||
for serial_no in serial_nos:
|
||||
self.assertTrue(frappe.db.exists("Serial No", serial_no))
|
||||
self.assertEqual(frappe.db.get_value("Serial No", serial_no, "status"), "Active")
|
||||
|
||||
se1 = make_stock_entry(
|
||||
item_code=item.name,
|
||||
qty=2,
|
||||
from_warehouse="_Test Warehouse - _TC",
|
||||
use_serial_batch_fields=1,
|
||||
serial_no="\n".join(serial_nos),
|
||||
)
|
||||
|
||||
se1.reload()
|
||||
|
||||
self.assertTrue(se1.items[0].use_serial_batch_fields)
|
||||
self.assertFalse(se1.items[0].serial_no)
|
||||
self.assertTrue(se1.items[0].serial_and_batch_bundle)
|
||||
|
||||
for serial_no in serial_nos:
|
||||
self.assertTrue(frappe.db.exists("Serial No", serial_no))
|
||||
self.assertEqual(frappe.db.get_value("Serial No", serial_no, "status"), "Delivered")
|
||||
|
||||
|
||||
def make_serialized_item(**args):
|
||||
args = frappe._dict(args)
|
||||
|
@ -315,7 +315,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2023-10-19 16:41:16.545416",
|
||||
"modified": "2024-02-07 16:05:17.772098",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Stock Reservation Entry",
|
||||
@ -335,6 +335,90 @@
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Sales Manager",
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Purchase Manager",
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Stock Manager",
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Sales User",
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Purchase User",
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
},
|
||||
{
|
||||
"cancel": 1,
|
||||
"create": 1,
|
||||
"delete": 1,
|
||||
"email": 1,
|
||||
"export": 1,
|
||||
"print": 1,
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Stock User",
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
}
|
||||
],
|
||||
"sort_field": "modified",
|
||||
|
@ -13,6 +13,7 @@
|
||||
"column_break_3",
|
||||
"is_group",
|
||||
"parent_warehouse",
|
||||
"is_rejected_warehouse",
|
||||
"column_break_4",
|
||||
"account",
|
||||
"company",
|
||||
@ -249,13 +250,20 @@
|
||||
{
|
||||
"fieldname": "column_break_qajx",
|
||||
"fieldtype": "Column Break"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"description": "If yes, then this warehouse will be used to store rejected materials",
|
||||
"fieldname": "is_rejected_warehouse",
|
||||
"fieldtype": "Check",
|
||||
"label": "Is Rejected Warehouse"
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-building",
|
||||
"idx": 1,
|
||||
"is_tree": 1,
|
||||
"links": [],
|
||||
"modified": "2023-05-29 13:10:43.333160",
|
||||
"modified": "2024-01-24 16:27:28.299520",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Stock",
|
||||
"name": "Warehouse",
|
||||
|
Loading…
x
Reference in New Issue
Block a user