mirror of
https://github.com/frappe/erpnext.git
synced 2026-06-06 05:39:12 +00:00
Merge branch 'develop' into bank-trans-party-automatch
This commit is contained in:
@@ -288,7 +288,7 @@ def _make_sales_order(source_name, target_doc=None, ignore_permissions=False):
|
||||
)
|
||||
|
||||
# sales team
|
||||
for d in customer.get("sales_team", []):
|
||||
for d in customer.get("sales_team") or []:
|
||||
target.append(
|
||||
"sales_team",
|
||||
{
|
||||
|
||||
@@ -47,21 +47,50 @@ frappe.ui.form.on("Sales Order", {
|
||||
frm.set_df_property('packed_items', 'cannot_add_rows', true);
|
||||
frm.set_df_property('packed_items', 'cannot_delete_rows', true);
|
||||
},
|
||||
|
||||
refresh: function(frm) {
|
||||
if(frm.doc.docstatus === 1 && frm.doc.status !== 'Closed'
|
||||
&& flt(frm.doc.per_delivered, 6) < 100 && flt(frm.doc.per_billed, 6) < 100) {
|
||||
frm.add_custom_button(__('Update Items'), () => {
|
||||
erpnext.utils.update_child_items({
|
||||
frm: frm,
|
||||
child_docname: "items",
|
||||
child_doctype: "Sales Order Detail",
|
||||
cannot_add_row: false,
|
||||
})
|
||||
});
|
||||
if(frm.doc.docstatus === 1) {
|
||||
if (frm.doc.status !== 'Closed' && flt(frm.doc.per_delivered, 6) < 100 && flt(frm.doc.per_billed, 6) < 100) {
|
||||
frm.add_custom_button(__('Update Items'), () => {
|
||||
erpnext.utils.update_child_items({
|
||||
frm: frm,
|
||||
child_docname: "items",
|
||||
child_doctype: "Sales Order Detail",
|
||||
cannot_add_row: false,
|
||||
})
|
||||
});
|
||||
|
||||
// Stock Reservation > Reserve button will be only visible if the SO has unreserved stock.
|
||||
if (frm.doc.__onload && frm.doc.__onload.has_unreserved_stock) {
|
||||
frm.add_custom_button(__('Reserve'), () => frm.events.create_stock_reservation_entries(frm), __('Stock Reservation'));
|
||||
}
|
||||
}
|
||||
|
||||
// Stock Reservation > Unreserve button will be only visible if the SO has reserved stock.
|
||||
if (frm.doc.__onload && frm.doc.__onload.has_reserved_stock) {
|
||||
frm.add_custom_button(__('Unreserve'), () => frm.events.cancel_stock_reservation_entries(frm), __('Stock Reservation'));
|
||||
}
|
||||
}
|
||||
|
||||
if (frm.doc.docstatus === 0 && frm.doc.is_internal_customer) {
|
||||
frm.events.get_items_from_internal_purchase_order(frm);
|
||||
if (frm.doc.docstatus === 0) {
|
||||
if (frm.doc.is_internal_customer) {
|
||||
frm.events.get_items_from_internal_purchase_order(frm);
|
||||
}
|
||||
|
||||
if (frm.is_new()) {
|
||||
frappe.db.get_single_value("Stock Settings", "enable_stock_reservation").then((value) => {
|
||||
if (value) {
|
||||
frappe.db.get_single_value("Stock Settings", "reserve_stock_on_sales_order_submission").then((value) => {
|
||||
// If `Reserve Stock on Sales Order Submission` is enabled in Stock Settings, set Reserve Stock to 1 else 0.
|
||||
frm.set_value("reserve_stock", value ? 1 : 0);
|
||||
})
|
||||
} else {
|
||||
// If `Stock Reservation` is disabled in Stock Settings, set Reserve Stock to 0 and read only.
|
||||
frm.set_value("reserve_stock", 0);
|
||||
frm.set_df_property("reserve_stock", "read_only", 1);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
@@ -137,6 +166,108 @@ frappe.ui.form.on("Sales Order", {
|
||||
if(!d.delivery_date) d.delivery_date = frm.doc.delivery_date;
|
||||
});
|
||||
refresh_field("items");
|
||||
},
|
||||
|
||||
create_stock_reservation_entries(frm) {
|
||||
let items_data = [];
|
||||
|
||||
const dialog = frappe.prompt({fieldname: 'items', fieldtype: 'Table', label: __('Items to Reserve'),
|
||||
fields: [
|
||||
{
|
||||
fieldtype: 'Data',
|
||||
fieldname: 'name',
|
||||
label: __('Name'),
|
||||
reqd: 1,
|
||||
read_only: 1,
|
||||
},
|
||||
{
|
||||
fieldtype: 'Link',
|
||||
fieldname: 'item_code',
|
||||
label: __('Item Code'),
|
||||
options: 'Item',
|
||||
reqd: 1,
|
||||
read_only: 1,
|
||||
in_list_view: 1,
|
||||
},
|
||||
{
|
||||
fieldtype: 'Link',
|
||||
fieldname: 'warehouse',
|
||||
label: __('Warehouse'),
|
||||
options: 'Warehouse',
|
||||
reqd: 1,
|
||||
in_list_view: 1,
|
||||
get_query: function () {
|
||||
return {
|
||||
filters: [
|
||||
["Warehouse", "is_group", "!=", 1]
|
||||
]
|
||||
};
|
||||
},
|
||||
},
|
||||
{
|
||||
fieldtype: 'Float',
|
||||
fieldname: 'qty_to_reserve',
|
||||
label: __('Qty'),
|
||||
reqd: 1,
|
||||
in_list_view: 1
|
||||
}
|
||||
],
|
||||
data: items_data,
|
||||
in_place_edit: true,
|
||||
get_data: function() {
|
||||
return items_data;
|
||||
}
|
||||
}, function(data) {
|
||||
if (data.items.length > 0) {
|
||||
frappe.call({
|
||||
doc: frm.doc,
|
||||
method: 'create_stock_reservation_entries',
|
||||
args: {
|
||||
items_details: data.items,
|
||||
notify: true
|
||||
},
|
||||
freeze: true,
|
||||
freeze_message: __('Reserving Stock...'),
|
||||
callback: (r) => {
|
||||
frm.doc.__onload.has_unreserved_stock = false;
|
||||
frm.reload_doc();
|
||||
}
|
||||
});
|
||||
}
|
||||
}, __("Stock Reservation"), __("Reserve Stock"));
|
||||
|
||||
frm.doc.items.forEach(item => {
|
||||
if (item.reserve_stock) {
|
||||
let unreserved_qty = (flt(item.stock_qty) - (flt(item.delivered_qty) * flt(item.conversion_factor)) - flt(item.stock_reserved_qty))
|
||||
|
||||
if (unreserved_qty > 0) {
|
||||
dialog.fields_dict.items.df.data.push({
|
||||
'name': item.name,
|
||||
'item_code': item.item_code,
|
||||
'warehouse': item.warehouse,
|
||||
'qty_to_reserve': (unreserved_qty / flt(item.conversion_factor))
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
dialog.fields_dict.items.grid.refresh();
|
||||
},
|
||||
|
||||
cancel_stock_reservation_entries(frm) {
|
||||
frappe.call({
|
||||
method: 'erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry.cancel_stock_reservation_entries',
|
||||
args: {
|
||||
voucher_type: frm.doctype,
|
||||
voucher_no: frm.docname
|
||||
},
|
||||
freeze: true,
|
||||
freeze_message: __('Unreserving Stock...'),
|
||||
callback: (r) => {
|
||||
frm.doc.__onload.has_reserved_stock = false;
|
||||
frm.reload_doc();
|
||||
}
|
||||
})
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@
|
||||
"scan_barcode",
|
||||
"column_break_28",
|
||||
"set_warehouse",
|
||||
"reserve_stock",
|
||||
"items_section",
|
||||
"items",
|
||||
"section_break_31",
|
||||
@@ -1625,13 +1626,24 @@
|
||||
"fieldname": "named_place",
|
||||
"fieldtype": "Data",
|
||||
"label": "Named Place"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval: (doc.docstatus == 0 || doc.reserve_stock)",
|
||||
"description": "If checked, Stock Reservation Entries will be created on <b>Submit</b>",
|
||||
"fieldname": "reserve_stock",
|
||||
"fieldtype": "Check",
|
||||
"label": "Reserve Stock",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"report_hide": 1
|
||||
}
|
||||
],
|
||||
"icon": "fa fa-file-text",
|
||||
"idx": 105,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2023-04-20 11:14:01.036202",
|
||||
"modified": "2023-04-22 09:55:37.008190",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Sales Order",
|
||||
@@ -1664,7 +1676,6 @@
|
||||
"read": 1,
|
||||
"report": 1,
|
||||
"role": "Sales Manager",
|
||||
"set_user_permissions": 1,
|
||||
"share": 1,
|
||||
"submit": 1,
|
||||
"write": 1
|
||||
|
||||
@@ -30,6 +30,11 @@ from erpnext.manufacturing.doctype.production_plan.production_plan import (
|
||||
from erpnext.selling.doctype.customer.customer import check_credit_limit
|
||||
from erpnext.setup.doctype.item_group.item_group import get_item_group_defaults
|
||||
from erpnext.stock.doctype.item.item import get_item_defaults
|
||||
from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import (
|
||||
cancel_stock_reservation_entries,
|
||||
get_sre_reserved_qty_details_for_voucher,
|
||||
has_reserved_stock,
|
||||
)
|
||||
from erpnext.stock.get_item_details import get_default_bom, get_price_list_rate
|
||||
from erpnext.stock.stock_balance import get_reserved_qty, update_bin_qty
|
||||
|
||||
@@ -44,6 +49,14 @@ class SalesOrder(SellingController):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(SalesOrder, self).__init__(*args, **kwargs)
|
||||
|
||||
def onload(self) -> None:
|
||||
if frappe.get_cached_value("Stock Settings", None, "enable_stock_reservation"):
|
||||
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 validate(self):
|
||||
super(SalesOrder, self).validate()
|
||||
self.validate_delivery_date()
|
||||
@@ -241,6 +254,9 @@ class SalesOrder(SellingController):
|
||||
|
||||
update_coupon_code_count(self.coupon_code, "used")
|
||||
|
||||
if self.get("reserve_stock"):
|
||||
self.create_stock_reservation_entries()
|
||||
|
||||
def on_cancel(self):
|
||||
self.ignore_linked_doctypes = ("GL Entry", "Stock Ledger Entry", "Payment Ledger Entry")
|
||||
super(SalesOrder, self).on_cancel()
|
||||
@@ -257,6 +273,7 @@ class SalesOrder(SellingController):
|
||||
self.db_set("status", "Cancelled")
|
||||
|
||||
self.update_blanket_order()
|
||||
cancel_stock_reservation_entries("Sales Order", self.name)
|
||||
|
||||
unlink_inter_company_doc(self.doctype, self.name, self.inter_company_order_reference)
|
||||
if self.coupon_code:
|
||||
@@ -485,6 +502,166 @@ class SalesOrder(SellingController):
|
||||
).format(item.item_code)
|
||||
)
|
||||
|
||||
def has_unreserved_stock(self) -> bool:
|
||||
"""Returns True if there is any unreserved item in the Sales Order."""
|
||||
|
||||
reserved_qty_details = get_sre_reserved_qty_details_for_voucher("Sales Order", self.name)
|
||||
|
||||
for item in self.get("items"):
|
||||
if not item.get("reserve_stock"):
|
||||
continue
|
||||
|
||||
unreserved_qty = get_unreserved_qty(item, reserved_qty_details)
|
||||
if unreserved_qty > 0:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
@frappe.whitelist()
|
||||
def create_stock_reservation_entries(self, items_details=None, notify=True):
|
||||
"""Creates Stock Reservation Entries for Sales Order Items."""
|
||||
|
||||
from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import (
|
||||
get_available_qty_to_reserve,
|
||||
validate_stock_reservation_settings,
|
||||
)
|
||||
|
||||
validate_stock_reservation_settings(self)
|
||||
|
||||
allow_partial_reservation = frappe.db.get_single_value(
|
||||
"Stock Settings", "allow_partial_reservation"
|
||||
)
|
||||
|
||||
items = []
|
||||
if items_details:
|
||||
for item in items_details:
|
||||
so_item = frappe.get_doc("Sales Order Item", item["name"])
|
||||
so_item.reserve_stock = 1
|
||||
so_item.warehouse = item["warehouse"]
|
||||
so_item.qty_to_reserve = flt(item["qty_to_reserve"]) * flt(so_item.conversion_factor)
|
||||
items.append(so_item)
|
||||
|
||||
sre_count = 0
|
||||
reserved_qty_details = get_sre_reserved_qty_details_for_voucher("Sales Order", self.name)
|
||||
for item in items or self.get("items"):
|
||||
# Skip if `Reserved Stock` is not checked for the item.
|
||||
if not item.get("reserve_stock"):
|
||||
continue
|
||||
|
||||
# Skip if Non-Stock Item.
|
||||
if not frappe.get_cached_value("Item", item.item_code, "is_stock_item"):
|
||||
frappe.msgprint(
|
||||
_("Row #{0}: Stock cannot be reserved for a non-stock Item {1}").format(
|
||||
item.idx, frappe.bold(item.item_code)
|
||||
),
|
||||
title=_("Stock Reservation"),
|
||||
indicator="yellow",
|
||||
)
|
||||
item.db_set("reserve_stock", 0)
|
||||
continue
|
||||
|
||||
# Skip if Group Warehouse.
|
||||
if frappe.get_cached_value("Warehouse", item.warehouse, "is_group"):
|
||||
frappe.msgprint(
|
||||
_("Row #{0}: Stock cannot be reserved in group warehouse {1}.").format(
|
||||
item.idx, frappe.bold(item.warehouse)
|
||||
),
|
||||
title=_("Stock Reservation"),
|
||||
indicator="yellow",
|
||||
)
|
||||
continue
|
||||
|
||||
unreserved_qty = get_unreserved_qty(item, reserved_qty_details)
|
||||
|
||||
# Stock is already reserved for the item, notify the user and skip the item.
|
||||
if unreserved_qty <= 0:
|
||||
frappe.msgprint(
|
||||
_("Row #{0}: Stock is already reserved for the Item {1}.").format(
|
||||
item.idx, frappe.bold(item.item_code)
|
||||
),
|
||||
title=_("Stock Reservation"),
|
||||
indicator="yellow",
|
||||
)
|
||||
continue
|
||||
|
||||
available_qty_to_reserve = get_available_qty_to_reserve(item.item_code, item.warehouse)
|
||||
|
||||
# No stock available to reserve, notify the user and skip the item.
|
||||
if available_qty_to_reserve <= 0:
|
||||
frappe.msgprint(
|
||||
_("Row #{0}: No available stock to reserve for the Item {1} in Warehouse {2}.").format(
|
||||
item.idx, frappe.bold(item.item_code), frappe.bold(item.warehouse)
|
||||
),
|
||||
title=_("Stock Reservation"),
|
||||
indicator="orange",
|
||||
)
|
||||
continue
|
||||
|
||||
# The quantity which can be reserved.
|
||||
qty_to_be_reserved = min(unreserved_qty, available_qty_to_reserve)
|
||||
|
||||
if hasattr(item, "qty_to_reserve"):
|
||||
if item.qty_to_reserve <= 0:
|
||||
frappe.msgprint(
|
||||
_("Row #{0}: Quantity to reserve for the Item {1} should be greater than 0.").format(
|
||||
item.idx, frappe.bold(item.item_code)
|
||||
),
|
||||
title=_("Stock Reservation"),
|
||||
indicator="orange",
|
||||
)
|
||||
continue
|
||||
else:
|
||||
qty_to_be_reserved = min(qty_to_be_reserved, item.qty_to_reserve)
|
||||
|
||||
# Partial Reservation
|
||||
if qty_to_be_reserved < unreserved_qty:
|
||||
if not item.get("qty_to_reserve") or qty_to_be_reserved < flt(item.get("qty_to_reserve")):
|
||||
frappe.msgprint(
|
||||
_("Row #{0}: Only {1} available to reserve for the Item {2}").format(
|
||||
item.idx,
|
||||
frappe.bold(str(qty_to_be_reserved / item.conversion_factor) + " " + item.uom),
|
||||
frappe.bold(item.item_code),
|
||||
),
|
||||
title=_("Stock Reservation"),
|
||||
indicator="orange",
|
||||
)
|
||||
|
||||
# Skip the item if `Partial Reservation` is disabled in the Stock Settings.
|
||||
if not allow_partial_reservation:
|
||||
continue
|
||||
|
||||
# Create and Submit Stock Reservation Entry
|
||||
sre = frappe.new_doc("Stock Reservation Entry")
|
||||
sre.item_code = item.item_code
|
||||
sre.warehouse = item.warehouse
|
||||
sre.voucher_type = self.doctype
|
||||
sre.voucher_no = self.name
|
||||
sre.voucher_detail_no = item.name
|
||||
sre.available_qty = available_qty_to_reserve
|
||||
sre.voucher_qty = item.stock_qty
|
||||
sre.reserved_qty = qty_to_be_reserved
|
||||
sre.company = self.company
|
||||
sre.stock_uom = item.stock_uom
|
||||
sre.project = self.project
|
||||
sre.save()
|
||||
sre.submit()
|
||||
|
||||
sre_count += 1
|
||||
|
||||
if sre_count and notify:
|
||||
frappe.msgprint(_("Stock Reservation Entries Created"), alert=True, indicator="green")
|
||||
|
||||
|
||||
def get_unreserved_qty(item: object, reserved_qty_details: dict) -> float:
|
||||
"""Returns the unreserved quantity for the Sales Order Item."""
|
||||
|
||||
existing_reserved_qty = reserved_qty_details.get(item.name, 0)
|
||||
return (
|
||||
item.stock_qty
|
||||
- flt(item.delivered_qty) * item.get("conversion_factor", 1)
|
||||
- existing_reserved_qty
|
||||
)
|
||||
|
||||
|
||||
def get_list_context(context=None):
|
||||
from erpnext.controllers.website_list_for_contact import get_list_context
|
||||
@@ -680,7 +857,6 @@ def make_delivery_note(source_name, target_doc=None, skip_item_mapping=False):
|
||||
}
|
||||
|
||||
target_doc = get_mapped_doc("Sales Order", source_name, mapper, target_doc, set_missing_values)
|
||||
|
||||
target_doc.set_onload("ignore_price_list", True)
|
||||
|
||||
return target_doc
|
||||
|
||||
@@ -11,6 +11,7 @@ def get_data():
|
||||
"Payment Request": "reference_name",
|
||||
"Auto Repeat": "reference_document",
|
||||
"Maintenance Visit": "prevdoc_docname",
|
||||
"Stock Reservation Entry": "voucher_no",
|
||||
},
|
||||
"internal_links": {
|
||||
"Quotation": ["items", "prevdoc_docname"],
|
||||
@@ -23,7 +24,7 @@ def get_data():
|
||||
{"label": _("Purchasing"), "items": ["Material Request", "Purchase Order"]},
|
||||
{"label": _("Projects"), "items": ["Project"]},
|
||||
{"label": _("Manufacturing"), "items": ["Work Order"]},
|
||||
{"label": _("Reference"), "items": ["Quotation", "Auto Repeat"]},
|
||||
{"label": _("Reference"), "items": ["Quotation", "Auto Repeat", "Stock Reservation Entry"]},
|
||||
{"label": _("Payment"), "items": ["Payment Entry", "Payment Request", "Journal Entry"]},
|
||||
],
|
||||
}
|
||||
|
||||
@@ -1878,6 +1878,141 @@ class TestSalesOrder(FrappeTestCase):
|
||||
self.assertEqual(pe.references[1].reference_name, so.name)
|
||||
self.assertEqual(pe.references[1].allocated_amount, 300)
|
||||
|
||||
@change_settings("Stock Settings", {"enable_stock_reservation": 1})
|
||||
def test_stock_reservation_against_sales_order(self) -> None:
|
||||
from random import randint, uniform
|
||||
|
||||
from erpnext.stock.doctype.stock_reservation_entry.stock_reservation_entry import (
|
||||
cancel_stock_reservation_entries,
|
||||
get_sre_reserved_qty_details_for_voucher,
|
||||
get_stock_reservation_entries_for_voucher,
|
||||
has_reserved_stock,
|
||||
)
|
||||
from erpnext.stock.doctype.stock_reservation_entry.test_stock_reservation_entry import (
|
||||
create_items,
|
||||
create_material_receipt,
|
||||
)
|
||||
|
||||
items_details, warehouse = create_items(), "_Test Warehouse - _TC"
|
||||
se = create_material_receipt(items_details, warehouse, qty=10)
|
||||
|
||||
item_list = []
|
||||
for item_code, properties in items_details.items():
|
||||
stock_uom = properties.stock_uom
|
||||
item_list.append(
|
||||
{
|
||||
"item_code": item_code,
|
||||
"warehouse": warehouse,
|
||||
"qty": flt(uniform(11, 100), 0 if stock_uom == "Nos" else 3),
|
||||
"uom": stock_uom,
|
||||
"rate": randint(10, 200),
|
||||
}
|
||||
)
|
||||
|
||||
so = make_sales_order(
|
||||
item_list=item_list,
|
||||
warehouse="_Test Warehouse - _TC",
|
||||
)
|
||||
|
||||
# Test - 1: Stock should not be reserved if the Available Qty to Reserve is less than the Ordered Qty and Partial Reservation is disabled in Stock Settings.
|
||||
with change_settings("Stock Settings", {"allow_partial_reservation": 0}):
|
||||
so.create_stock_reservation_entries()
|
||||
self.assertFalse(has_reserved_stock("Sales Order", so.name))
|
||||
|
||||
# Test - 2: Stock should be Partially Reserved if the Partial Reservation is enabled in Stock Settings.
|
||||
with change_settings("Stock Settings", {"allow_partial_reservation": 1}):
|
||||
so.create_stock_reservation_entries()
|
||||
so.load_from_db()
|
||||
self.assertTrue(has_reserved_stock("Sales Order", so.name))
|
||||
|
||||
for item in so.items:
|
||||
sre_details = get_stock_reservation_entries_for_voucher(
|
||||
"Sales Order", so.name, item.name, fields=["reserved_qty", "status"]
|
||||
)
|
||||
self.assertEqual(item.stock_reserved_qty, sre_details[0].reserved_qty)
|
||||
self.assertEqual(sre_details[0].status, "Partially Reserved")
|
||||
|
||||
se.cancel()
|
||||
|
||||
# Test - 3: Stock should be fully Reserved if the Available Qty to Reserve is greater than the Un-reserved Qty.
|
||||
create_material_receipt(items_details, warehouse, qty=110)
|
||||
so.create_stock_reservation_entries()
|
||||
so.load_from_db()
|
||||
|
||||
reserved_qty_details = get_sre_reserved_qty_details_for_voucher("Sales Order", so.name)
|
||||
for item in so.items:
|
||||
reserved_qty = reserved_qty_details[item.name]
|
||||
self.assertEqual(item.stock_reserved_qty, reserved_qty)
|
||||
self.assertEqual(item.stock_qty, item.stock_reserved_qty)
|
||||
|
||||
# Test - 4: Stock should get unreserved on cancellation of Stock Reservation Entries.
|
||||
cancel_stock_reservation_entries("Sales Order", so.name)
|
||||
so.load_from_db()
|
||||
self.assertFalse(has_reserved_stock("Sales Order", so.name))
|
||||
|
||||
for item in so.items:
|
||||
self.assertEqual(item.stock_reserved_qty, 0)
|
||||
|
||||
# Test - 5: Re-reserve the stock.
|
||||
so.create_stock_reservation_entries()
|
||||
self.assertTrue(has_reserved_stock("Sales Order", so.name))
|
||||
|
||||
# Test - 6: Stock should get unreserved on cancellation of Sales Order.
|
||||
so.cancel()
|
||||
so.load_from_db()
|
||||
self.assertFalse(has_reserved_stock("Sales Order", so.name))
|
||||
|
||||
for item in so.items:
|
||||
self.assertEqual(item.stock_reserved_qty, 0)
|
||||
|
||||
# Create Sales Order and Reserve Stock.
|
||||
so = make_sales_order(
|
||||
item_list=item_list,
|
||||
warehouse="_Test Warehouse - _TC",
|
||||
)
|
||||
so.create_stock_reservation_entries()
|
||||
|
||||
# Test - 7: Partial Delivery against Sales Order.
|
||||
dn1 = make_delivery_note(so.name)
|
||||
|
||||
for item in dn1.items:
|
||||
item.qty = flt(uniform(1, 10), 0 if item.stock_uom == "Nos" else 3)
|
||||
|
||||
dn1.save()
|
||||
dn1.submit()
|
||||
|
||||
for item in so.items:
|
||||
sre_details = get_stock_reservation_entries_for_voucher(
|
||||
"Sales Order", so.name, item.name, fields=["delivered_qty", "status"]
|
||||
)
|
||||
self.assertGreater(sre_details[0].delivered_qty, 0)
|
||||
self.assertEqual(sre_details[0].status, "Partially Delivered")
|
||||
|
||||
# Test - 8: Over Delivery against Sales Order, SRE Delivered Qty should not be greater than the SRE Reserved Qty.
|
||||
with change_settings("Stock Settings", {"over_delivery_receipt_allowance": 100}):
|
||||
dn2 = make_delivery_note(so.name)
|
||||
|
||||
for item in dn2.items:
|
||||
item.qty += flt(uniform(1, 10), 0 if item.stock_uom == "Nos" else 3)
|
||||
|
||||
dn2.save()
|
||||
dn2.submit()
|
||||
|
||||
for item in so.items:
|
||||
sre_details = frappe.db.get_all(
|
||||
"Stock Reservation Entry",
|
||||
filters={
|
||||
"voucher_type": "Sales Order",
|
||||
"voucher_no": so.name,
|
||||
"voucher_detail_no": item.name,
|
||||
},
|
||||
fields=["status", "reserved_qty", "delivered_qty"],
|
||||
)
|
||||
|
||||
for sre_detail in sre_details:
|
||||
self.assertEqual(sre_detail.reserved_qty, sre_detail.delivered_qty)
|
||||
self.assertEqual(sre_detail.status, "Delivered")
|
||||
|
||||
def test_delivered_item_material_request(self):
|
||||
"SO -> MR (Manufacture) -> WO. Test if WO Qty is updated in SO."
|
||||
from erpnext.manufacturing.doctype.work_order.work_order import (
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
"item_code",
|
||||
"customer_item_code",
|
||||
"ensure_delivery_based_on_produced_serial_no",
|
||||
"reserve_stock",
|
||||
"col_break1",
|
||||
"delivery_date",
|
||||
"item_name",
|
||||
@@ -27,6 +28,7 @@
|
||||
"uom",
|
||||
"conversion_factor",
|
||||
"stock_qty",
|
||||
"stock_reserved_qty",
|
||||
"section_break_16",
|
||||
"price_list_rate",
|
||||
"base_price_list_rate",
|
||||
@@ -859,12 +861,33 @@
|
||||
"fieldname": "material_request_item",
|
||||
"fieldtype": "Data",
|
||||
"label": "Material Request Item"
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"default": "1",
|
||||
"fieldname": "reserve_stock",
|
||||
"fieldtype": "Check",
|
||||
"label": "Reserve Stock",
|
||||
"print_hide": 1,
|
||||
"report_hide": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval: doc.stock_reserved_qty",
|
||||
"fieldname": "stock_reserved_qty",
|
||||
"fieldtype": "Float",
|
||||
"label": "Stock Reserved Qty (in Stock UOM)",
|
||||
"no_copy": 1,
|
||||
"non_negative": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1,
|
||||
"report_hide": 1
|
||||
}
|
||||
],
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2022-12-25 02:51:10.247569",
|
||||
"modified": "2023-04-04 10:44:05.707488",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Sales Order Item",
|
||||
|
||||
@@ -2,9 +2,11 @@
|
||||
"creation": "2021-11-23 12:00:36.138824",
|
||||
"docstatus": 0,
|
||||
"doctype": "Form Tour",
|
||||
"first_document": 0,
|
||||
"idx": 0,
|
||||
"include_name_field": 0,
|
||||
"is_standard": 1,
|
||||
"modified": "2021-11-23 12:02:48.010298",
|
||||
"modified": "2023-05-23 12:51:48.684517",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Quotation",
|
||||
@@ -14,51 +16,43 @@
|
||||
"steps": [
|
||||
{
|
||||
"description": "Select a customer or lead for whom this quotation is being prepared. Let's select a Customer.",
|
||||
"field": "",
|
||||
"fieldname": "quotation_to",
|
||||
"fieldtype": "Link",
|
||||
"has_next_condition": 0,
|
||||
"is_table_field": 0,
|
||||
"label": "Quotation To",
|
||||
"parent_field": "",
|
||||
"position": "Right",
|
||||
"title": "Quotation To"
|
||||
},
|
||||
{
|
||||
"description": "Select a specific Customer to whom this quotation will be sent.",
|
||||
"field": "",
|
||||
"fieldname": "party_name",
|
||||
"fieldtype": "Dynamic Link",
|
||||
"has_next_condition": 0,
|
||||
"is_table_field": 0,
|
||||
"label": "Party",
|
||||
"parent_field": "",
|
||||
"position": "Right",
|
||||
"title": "Party"
|
||||
},
|
||||
{
|
||||
"child_doctype": "Quotation Item",
|
||||
"description": "Select an item for which you will be quoting a price.",
|
||||
"field": "",
|
||||
"fieldname": "items",
|
||||
"fieldtype": "Table",
|
||||
"has_next_condition": 0,
|
||||
"is_table_field": 0,
|
||||
"label": "Items",
|
||||
"parent_field": "",
|
||||
"parent_fieldname": "items",
|
||||
"position": "Bottom",
|
||||
"title": "Items"
|
||||
},
|
||||
{
|
||||
"description": "You can select pre-populated Sales Taxes and Charges from here.",
|
||||
"field": "",
|
||||
"fieldname": "taxes",
|
||||
"fieldtype": "Table",
|
||||
"has_next_condition": 0,
|
||||
"is_table_field": 0,
|
||||
"label": "Sales Taxes and Charges",
|
||||
"parent_field": "",
|
||||
"position": "Bottom",
|
||||
"title": "Sales Taxes and Charges"
|
||||
}
|
||||
|
||||
@@ -1,123 +0,0 @@
|
||||
{
|
||||
"charts": [],
|
||||
"content": "[{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Your Shortcuts</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Point Of Sale\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Settings & Configurations\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Loyalty Program\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Opening & Closing\",\"col\":4}}]",
|
||||
"creation": "2020-03-02 17:18:32.505616",
|
||||
"docstatus": 0,
|
||||
"doctype": "Workspace",
|
||||
"for_user": "",
|
||||
"hide_custom": 0,
|
||||
"icon": "retail",
|
||||
"idx": 0,
|
||||
"label": "Retail",
|
||||
"links": [
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Settings & Configurations",
|
||||
"link_count": 2,
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Point-of-Sale Profile",
|
||||
"link_count": 0,
|
||||
"link_to": "POS Profile",
|
||||
"link_type": "DocType",
|
||||
"onboard": 1,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "POS Settings",
|
||||
"link_count": 0,
|
||||
"link_to": "POS Settings",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Loyalty Program",
|
||||
"link_count": 2,
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Loyalty Program",
|
||||
"link_count": 0,
|
||||
"link_to": "Loyalty Program",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Loyalty Point Entry",
|
||||
"link_count": 0,
|
||||
"link_to": "Loyalty Point Entry",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Opening & Closing",
|
||||
"link_count": 2,
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "POS Opening Entry",
|
||||
"link_count": 0,
|
||||
"link_to": "POS Opening Entry",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "",
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "POS Closing Entry",
|
||||
"link_count": 0,
|
||||
"link_to": "POS Closing Entry",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2022-01-13 18:07:56.711095",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Retail",
|
||||
"owner": "Administrator",
|
||||
"parent_page": "",
|
||||
"public": 1,
|
||||
"restrict_to_domain": "Retail",
|
||||
"roles": [],
|
||||
"sequence_id": 22.0,
|
||||
"shortcuts": [
|
||||
{
|
||||
"doc_view": "",
|
||||
"label": "Point Of Sale",
|
||||
"link_to": "point-of-sale",
|
||||
"type": "Page"
|
||||
}
|
||||
],
|
||||
"title": "Retail"
|
||||
}
|
||||
@@ -5,14 +5,16 @@
|
||||
"label": "Sales Order Trends"
|
||||
}
|
||||
],
|
||||
"content": "[{\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Selling\",\"col\":12}},{\"type\":\"chart\",\"data\":{\"chart_name\":\"Sales Order Trends\",\"col\":12}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Quick Access</b></span>\",\"col\":12}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Item\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Order\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Analytics\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Order Analysis\",\"col\":3}},{\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"type\":\"spacer\",\"data\":{\"col\":12}},{\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"col\":12}},{\"type\":\"card\",\"data\":{\"card_name\":\"Selling\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Items and Pricing\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Key Reports\",\"col\":4}},{\"type\":\"card\",\"data\":{\"card_name\":\"Other Reports\",\"col\":4}}]",
|
||||
"content": "[{\"id\":\"ow595dYDrI\",\"type\":\"onboarding\",\"data\":{\"onboarding_name\":\"Selling\",\"col\":12}},{\"id\":\"vBSf8Vi9U8\",\"type\":\"chart\",\"data\":{\"chart_name\":\"Sales Order Trends\",\"col\":12}},{\"id\":\"aW2i5R5GRP\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"1it3dCOnm6\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Quick Access</b></span>\",\"col\":12}},{\"id\":\"x7pLl-spS4\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Item\",\"col\":3}},{\"id\":\"SSGrXWmY-H\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Order\",\"col\":3}},{\"id\":\"-5J_yLxDaS\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Sales Analytics\",\"col\":3}},{\"id\":\"6YEYpnIBKV\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Point of Sale\",\"col\":3}},{\"id\":\"c_GjZuZ2oN\",\"type\":\"shortcut\",\"data\":{\"shortcut_name\":\"Dashboard\",\"col\":3}},{\"id\":\"oNjjNbnUHp\",\"type\":\"spacer\",\"data\":{\"col\":12}},{\"id\":\"0BcePLg0g1\",\"type\":\"header\",\"data\":{\"text\":\"<span class=\\\"h4\\\"><b>Reports & Masters</b></span>\",\"col\":12}},{\"id\":\"uze5dJ1ipL\",\"type\":\"card\",\"data\":{\"card_name\":\"Selling\",\"col\":4}},{\"id\":\"3j2fYwMAkq\",\"type\":\"card\",\"data\":{\"card_name\":\"Point of Sale\",\"col\":4}},{\"id\":\"xImm8NepFt\",\"type\":\"card\",\"data\":{\"card_name\":\"Items and Pricing\",\"col\":4}},{\"id\":\"6MjIe7KCQo\",\"type\":\"card\",\"data\":{\"card_name\":\"Settings\",\"col\":4}},{\"id\":\"lBu2EKgmJF\",\"type\":\"card\",\"data\":{\"card_name\":\"Key Reports\",\"col\":4}},{\"id\":\"1ARHrjg4kI\",\"type\":\"card\",\"data\":{\"card_name\":\"Other Reports\",\"col\":4}}]",
|
||||
"creation": "2020-01-28 11:49:12.092882",
|
||||
"custom_blocks": [],
|
||||
"docstatus": 0,
|
||||
"doctype": "Workspace",
|
||||
"for_user": "",
|
||||
"hide_custom": 0,
|
||||
"icon": "sell",
|
||||
"idx": 0,
|
||||
"is_hidden": 0,
|
||||
"label": "Selling",
|
||||
"links": [
|
||||
{
|
||||
@@ -317,140 +319,68 @@
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Other Reports",
|
||||
"link_count": 12,
|
||||
"label": "Point of Sale",
|
||||
"link_count": 6,
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "Lead",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Lead Details",
|
||||
"is_query_report": 0,
|
||||
"label": "Point-of-Sale Profile",
|
||||
"link_count": 0,
|
||||
"link_to": "Lead Details",
|
||||
"link_type": "Report",
|
||||
"link_to": "POS Profile",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Address",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Customer Addresses And Contacts",
|
||||
"is_query_report": 0,
|
||||
"label": "POS Settings",
|
||||
"link_count": 0,
|
||||
"link_to": "Address And Contacts",
|
||||
"link_type": "Report",
|
||||
"link_to": "POS Settings",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Item",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Available Stock for Packing Items",
|
||||
"is_query_report": 0,
|
||||
"label": "POS Opening Entry",
|
||||
"link_count": 0,
|
||||
"link_to": "Available Stock for Packing Items",
|
||||
"link_type": "Report",
|
||||
"link_to": "POS Opening Entry",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Sales Order",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Pending SO Items For Purchase Request",
|
||||
"is_query_report": 0,
|
||||
"label": "POS Closing Entry",
|
||||
"link_count": 0,
|
||||
"link_to": "Pending SO Items For Purchase Request",
|
||||
"link_type": "Report",
|
||||
"link_to": "POS Closing Entry",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Delivery Note",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Delivery Note Trends",
|
||||
"is_query_report": 0,
|
||||
"label": "Loyalty Program",
|
||||
"link_count": 0,
|
||||
"link_to": "Delivery Note Trends",
|
||||
"link_type": "Report",
|
||||
"link_to": "Loyalty Program",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Sales Invoice",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Sales Invoice Trends",
|
||||
"is_query_report": 0,
|
||||
"label": "Loyalty Point Entry",
|
||||
"link_count": 0,
|
||||
"link_to": "Sales Invoice Trends",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Customer",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Customer Credit Balance",
|
||||
"link_count": 0,
|
||||
"link_to": "Customer Credit Balance",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Customer",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Customers Without Any Sales Transactions",
|
||||
"link_count": 0,
|
||||
"link_to": "Customers Without Any Sales Transactions",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Customer",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Sales Partners Commission",
|
||||
"link_count": 0,
|
||||
"link_to": "Sales Partners Commission",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Sales Order",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Territory Target Variance Based On Item Group",
|
||||
"link_count": 0,
|
||||
"link_to": "Territory Target Variance Based On Item Group",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Sales Order",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Sales Person Target Variance Based On Item Group",
|
||||
"link_count": 0,
|
||||
"link_to": "Sales Person Target Variance Based On Item Group",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Sales Order",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Sales Partner Target Variance Based On Item Group",
|
||||
"link_count": 0,
|
||||
"link_to": "Sales Partner Target Variance based on Item Group",
|
||||
"link_type": "Report",
|
||||
"link_to": "Loyalty Point Entry",
|
||||
"link_type": "DocType",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
@@ -458,7 +388,7 @@
|
||||
"hidden": 0,
|
||||
"is_query_report": 0,
|
||||
"label": "Key Reports",
|
||||
"link_count": 22,
|
||||
"link_count": 9,
|
||||
"onboard": 0,
|
||||
"type": "Card Break"
|
||||
},
|
||||
@@ -562,15 +492,12 @@
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"dependencies": "Lead",
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Lead Details",
|
||||
"link_count": 0,
|
||||
"link_to": "Lead Details",
|
||||
"link_type": "Report",
|
||||
"is_query_report": 0,
|
||||
"label": "Other Reports",
|
||||
"link_count": 11,
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
"type": "Card Break"
|
||||
},
|
||||
{
|
||||
"dependencies": "Address",
|
||||
@@ -692,29 +619,26 @@
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
},
|
||||
{
|
||||
"hidden": 0,
|
||||
"is_query_report": 1,
|
||||
"label": "Payment Terms Status for Sales Order",
|
||||
"link_count": 0,
|
||||
"link_to": "Payment Terms Status for Sales Order",
|
||||
"link_type": "Report",
|
||||
"onboard": 0,
|
||||
"type": "Link"
|
||||
}
|
||||
],
|
||||
"modified": "2023-04-16 13:29:55.087240",
|
||||
"modified": "2023-05-26 16:31:53.634851",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Selling",
|
||||
"number_cards": [],
|
||||
"owner": "Administrator",
|
||||
"parent_page": "",
|
||||
"public": 1,
|
||||
"quick_lists": [],
|
||||
"restrict_to_domain": "",
|
||||
"roles": [],
|
||||
"sequence_id": 23.0,
|
||||
"sequence_id": 6.0,
|
||||
"shortcuts": [
|
||||
{
|
||||
"label": "Point of Sale",
|
||||
"link_to": "point-of-sale",
|
||||
"type": "Page"
|
||||
},
|
||||
{
|
||||
"color": "Grey",
|
||||
"format": "{} Available",
|
||||
@@ -739,11 +663,6 @@
|
||||
"stats_filter": "{ \"Status\": \"Open\" }",
|
||||
"type": "Report"
|
||||
},
|
||||
{
|
||||
"label": "Sales Order Analysis",
|
||||
"link_to": "Sales Order Analysis",
|
||||
"type": "Report"
|
||||
},
|
||||
{
|
||||
"label": "Dashboard",
|
||||
"link_to": "Selling",
|
||||
|
||||
Reference in New Issue
Block a user