mirror of
https://github.com/frappe/erpnext.git
synced 2026-08-27 05:45:19 +00:00
feat: option to skip delivery note for service items in sales order (#58297)
* feat: option to skip delivery note for service items in sales order * fix: reset stale skip delivery flags when setting is disabled * fix: clear stale skip delivery note flag for non-sales order types * fix: reset auto skip delivery flags on switch to maintenance order * refactor: replace sales order skip_delivery_note with item level skip_delivery * chore: drop skip delivery migration patch * fix: honor legacy skip_delivery_note flag instead of data migration
This commit is contained in:
@@ -614,6 +614,7 @@ class SalesInvoice(SellingController):
|
||||
"percent_join_field": "sales_order",
|
||||
"status_field": "delivery_status",
|
||||
"keyword": "Delivered",
|
||||
"exclude_field": "skip_delivery",
|
||||
"second_source_dt": "Delivery Note Item",
|
||||
"second_source_field": "qty",
|
||||
"second_join_field": "so_detail",
|
||||
|
||||
@@ -144,6 +144,7 @@ class ChildItemUpdater:
|
||||
if parent.is_against_so():
|
||||
parent.update_status_updater()
|
||||
elif self.parent_doctype == "Sales Order":
|
||||
parent.set_skip_delivery()
|
||||
parent.check_credit_limit()
|
||||
|
||||
for idx, row in enumerate(parent.get(self.child_docname), start=1):
|
||||
|
||||
@@ -605,14 +605,21 @@ class StatusUpdater(Document):
|
||||
|
||||
@staticmethod
|
||||
def _calculate_target_parent_percentage(
|
||||
name, target_parent_dt, target_dt, target_ref_field, target_field
|
||||
name, target_parent_dt, target_dt, target_ref_field, target_field, exclude_field=None
|
||||
):
|
||||
filters = {"parent": name, "parenttype": target_parent_dt}
|
||||
if exclude_field:
|
||||
filters[exclude_field] = 0
|
||||
|
||||
child_records = frappe.get_all(
|
||||
target_dt,
|
||||
filters={"parent": name, "parenttype": target_parent_dt},
|
||||
filters=filters,
|
||||
fields=[target_ref_field, target_field],
|
||||
)
|
||||
|
||||
if exclude_field and not child_records:
|
||||
return 100
|
||||
|
||||
# For operator dicts, the alias is in the "as" key; for strings, use the field name directly
|
||||
ref_key = target_ref_field.get("as") if isinstance(target_ref_field, dict) else target_ref_field
|
||||
|
||||
@@ -671,6 +678,7 @@ class StatusUpdater(Document):
|
||||
args["target_dt"],
|
||||
args["target_ref_field"],
|
||||
args["target_field"],
|
||||
args.get("exclude_field"),
|
||||
)
|
||||
# update field
|
||||
if args.get("status_field"):
|
||||
|
||||
@@ -524,7 +524,8 @@ class WorkOrder(Document):
|
||||
.on(ProductBundleItem.parent == SalesOrderItem.item_code)
|
||||
.select(SalesOrder.name, SalesOrder.project, SalesOrderItem.delivery_date)
|
||||
.where(
|
||||
(SalesOrder.skip_delivery_note == 0)
|
||||
(SalesOrderItem.skip_delivery == 0)
|
||||
& (SalesOrder.skip_delivery_note == 0)
|
||||
& (SalesOrder.docstatus == 1)
|
||||
& (SalesOrder.name == self.sales_order)
|
||||
& (
|
||||
@@ -545,6 +546,7 @@ class WorkOrder(Document):
|
||||
.select(SalesOrder.name, SalesOrder.project, SalesOrderItem.delivery_date)
|
||||
.where(
|
||||
(SalesOrder.name == self.sales_order)
|
||||
& (SalesOrderItem.skip_delivery == 0)
|
||||
& (SalesOrder.skip_delivery_note == 0)
|
||||
& (SalesOrderItem.item_code == PackedItem.parent_item)
|
||||
& (SalesOrder.docstatus == 1)
|
||||
|
||||
@@ -307,8 +307,10 @@ def make_delivery_note(
|
||||
return False
|
||||
|
||||
return (
|
||||
(abs(doc.delivered_qty) < abs(doc.qty)) or is_unit_price_row(doc)
|
||||
) and doc.delivered_by_supplier != 1
|
||||
((abs(doc.delivered_qty) < abs(doc.qty)) or is_unit_price_row(doc))
|
||||
and doc.delivered_by_supplier != 1
|
||||
and not cint(doc.skip_delivery)
|
||||
)
|
||||
|
||||
def update_item(source, target, source_parent):
|
||||
target.base_amount = (flt(source.qty) - flt(source.delivered_qty)) * flt(source.base_rate)
|
||||
|
||||
@@ -1020,11 +1020,14 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
|
||||
if (doc.status !== "Closed") {
|
||||
if (doc.status !== "On Hold") {
|
||||
const items_are_deliverable = this.frm.doc.items.some(
|
||||
(item) => item.delivered_by_supplier === 0 && item.qty > flt(item.delivered_qty)
|
||||
(item) =>
|
||||
!item.skip_delivery &&
|
||||
item.delivered_by_supplier === 0 &&
|
||||
item.qty > flt(item.delivered_qty)
|
||||
);
|
||||
allow_delivery =
|
||||
(this.frm.doc.has_unit_price_items || items_are_deliverable) &&
|
||||
!this.frm.doc.skip_delivery_note;
|
||||
!this.frm.doc.skip_delivery_note &&
|
||||
(this.frm.doc.has_unit_price_items || items_are_deliverable);
|
||||
|
||||
if (this.frm.has_perm("submit")) {
|
||||
if (flt(doc.per_delivered) < 100 || flt(doc.per_billed) < 100) {
|
||||
@@ -1426,14 +1429,12 @@ erpnext.selling.SalesOrderController = class SalesOrderController extends erpnex
|
||||
});
|
||||
}
|
||||
|
||||
skip_delivery_note() {
|
||||
this.toggle_delivery_date();
|
||||
}
|
||||
|
||||
toggle_delivery_date() {
|
||||
const items = this.frm.doc.items || [];
|
||||
const all_skipped = items.length && items.every((item) => item.skip_delivery);
|
||||
this.frm.fields_dict.items.grid.toggle_reqd(
|
||||
"delivery_date",
|
||||
this.frm.doc.order_type == "Sales" && !this.frm.doc.skip_delivery_note
|
||||
this.frm.doc.order_type == "Sales" && !all_skipped
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -312,7 +312,6 @@
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"depends_on": "eval:!doc.skip_delivery_note",
|
||||
"fieldname": "delivery_date",
|
||||
"fieldtype": "Date",
|
||||
"hide_days": 1,
|
||||
@@ -1478,16 +1477,6 @@
|
||||
"options": "Phone",
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"depends_on": "eval:doc.order_type == 'Maintenance';",
|
||||
"fieldname": "skip_delivery_note",
|
||||
"fieldtype": "Check",
|
||||
"hide_days": 1,
|
||||
"hide_seconds": 1,
|
||||
"label": "Skip Delivery Note",
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fetch_from": "customer.is_internal_customer",
|
||||
@@ -1708,6 +1697,16 @@
|
||||
"options": "Contact",
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "skip_delivery_note",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"label": "Skip Delivery Note",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "has_unit_price_items",
|
||||
@@ -1826,7 +1825,7 @@
|
||||
"idx": 105,
|
||||
"is_submittable": 1,
|
||||
"links": [],
|
||||
"modified": "2026-08-21 23:11:48.053347",
|
||||
"modified": "2026-08-26 12:00:00.000000",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Sales Order",
|
||||
|
||||
@@ -222,6 +222,7 @@ class SalesOrder(SellingController):
|
||||
|
||||
def validate(self):
|
||||
super().validate()
|
||||
self.set_skip_delivery()
|
||||
self.validate_delivery_date()
|
||||
self.validate_proj_cust()
|
||||
self.validate_po()
|
||||
@@ -268,7 +269,7 @@ class SalesOrder(SellingController):
|
||||
|
||||
def validate_po(self):
|
||||
# validate p.o date v/s delivery date
|
||||
if self.po_date and not self.skip_delivery_note:
|
||||
if self.po_date and not self.delivery_not_required():
|
||||
for d in self.get("items"):
|
||||
if d.delivery_date and getdate(self.po_date) > getdate(d.delivery_date):
|
||||
frappe.throw(
|
||||
@@ -277,7 +278,7 @@ class SalesOrder(SellingController):
|
||||
)
|
||||
)
|
||||
|
||||
if self.po_no and self.customer and not self.skip_delivery_note:
|
||||
if self.po_no and self.customer and not self.delivery_not_required():
|
||||
so = frappe.db.get_value(
|
||||
"Sales Order",
|
||||
filters={
|
||||
@@ -347,6 +348,44 @@ class SalesOrder(SellingController):
|
||||
|
||||
return frappe.db.exists("Item", {"name": ["in", bundle_items], "is_stock_item": 1}) is not None
|
||||
|
||||
def set_skip_delivery(self):
|
||||
enabled = cint(frappe.get_single_value("Selling Settings", "skip_delivery_note_for_service_items"))
|
||||
for d in self.get("items"):
|
||||
d.skip_delivery = cint(
|
||||
bool(enabled) and not cint(d.delivered_by_supplier) and not self.requires_delivery(d)
|
||||
)
|
||||
|
||||
self.set_delivery_progress()
|
||||
|
||||
def delivery_not_required(self):
|
||||
if cint(self.get("skip_delivery_note")):
|
||||
return True
|
||||
|
||||
return bool(self.get("items")) and all(cint(d.skip_delivery) for d in self.get("items"))
|
||||
|
||||
def set_delivery_progress(self):
|
||||
if self.delivery_not_required():
|
||||
self.per_delivered = 100
|
||||
self.delivery_status = "Not Applicable"
|
||||
return
|
||||
|
||||
deliverable = [d for d in self.get("items") if not cint(d.skip_delivery)]
|
||||
total_qty = sum(abs(flt(d.qty)) for d in deliverable)
|
||||
delivered_qty = sum(min(abs(flt(d.delivered_qty)), abs(flt(d.qty))) for d in deliverable)
|
||||
self.per_delivered = round(delivered_qty / total_qty * 100, 6) if total_qty else 0
|
||||
|
||||
if self.delivery_status == "Not Applicable":
|
||||
self.delivery_status = self._determine_status(self.per_delivered, "Delivered")
|
||||
|
||||
def requires_delivery(self, row):
|
||||
is_stock_item, is_fixed_asset = frappe.get_cached_value(
|
||||
"Item", row.item_code, ["is_stock_item", "is_fixed_asset"]
|
||||
)
|
||||
if is_stock_item or is_fixed_asset:
|
||||
return True
|
||||
|
||||
return self.has_product_bundle(row.item_code) and self.product_bundle_has_stock_item(row.item_code)
|
||||
|
||||
def validate_sales_mntc_quotation(self):
|
||||
quotation_names = [d.prevdoc_docname for d in self.get("items") if d.prevdoc_docname]
|
||||
|
||||
@@ -364,7 +403,7 @@ class SalesOrder(SellingController):
|
||||
frappe.msgprint(_("Quotation {0} not of type {1}").format(d.prevdoc_docname, self.order_type))
|
||||
|
||||
def validate_delivery_date(self):
|
||||
if self.order_type == "Sales" and not self.skip_delivery_note:
|
||||
if self.order_type == "Sales" and not self.delivery_not_required():
|
||||
delivery_date_list = [d.delivery_date for d in self.get("items") if d.delivery_date]
|
||||
max_delivery_date = max(delivery_date_list) if delivery_date_list else None
|
||||
if (max_delivery_date and not self.delivery_date) or (
|
||||
@@ -762,6 +801,7 @@ def get_events(start: str, end: str, filters: str | dict | None = None):
|
||||
SalesOrderItem.delivery_date,
|
||||
)
|
||||
.distinct()
|
||||
.where(SalesOrderItem.skip_delivery == 0)
|
||||
.where(SalesOrder.skip_delivery_note == 0)
|
||||
.where(SalesOrder.docstatus < 2)
|
||||
.where(SalesOrderItem.delivery_date.between(start, end))
|
||||
|
||||
@@ -9,8 +9,8 @@ frappe.listview_settings["Sales Order"] = {
|
||||
"status",
|
||||
"advance_payment_status",
|
||||
"order_type",
|
||||
"name",
|
||||
"skip_delivery_note",
|
||||
"name",
|
||||
],
|
||||
get_indicator: function (doc) {
|
||||
if (doc.status === "Closed") {
|
||||
@@ -23,7 +23,7 @@ frappe.listview_settings["Sales Order"] = {
|
||||
return [__("Completed"), "green", "status,=,Completed"];
|
||||
} else if (doc.advance_payment_status === "Requested") {
|
||||
return [__("To Pay"), "gray", "advance_payment_status,=,Requested"];
|
||||
} else if (!doc.skip_delivery_note && flt(doc.per_delivered) < 100) {
|
||||
} else if (flt(doc.per_delivered) < 100 && !doc.skip_delivery_note) {
|
||||
if (frappe.datetime.get_diff(doc.delivery_date) < 0) {
|
||||
// not delivered & overdue
|
||||
return [
|
||||
@@ -50,14 +50,12 @@ frappe.listview_settings["Sales Order"] = {
|
||||
return [__("To Deliver"), "orange", "per_delivered,<,100|per_billed,=,100|status,!=,Closed"];
|
||||
}
|
||||
} else if (
|
||||
flt(doc.per_delivered) === 100 &&
|
||||
(flt(doc.per_delivered) === 100 || doc.skip_delivery_note) &&
|
||||
flt(doc.grand_total) !== 0 &&
|
||||
flt(doc.per_billed) < 100
|
||||
) {
|
||||
// to bill
|
||||
return [__("To Bill"), "orange", "per_delivered,=,100|per_billed,<,100|status,!=,Closed"];
|
||||
} else if (doc.skip_delivery_note && flt(doc.per_billed) < 100) {
|
||||
return [__("To Bill"), "orange", "per_billed,<,100|status,!=,Closed"];
|
||||
}
|
||||
},
|
||||
onload: function (listview) {
|
||||
|
||||
@@ -50,6 +50,9 @@ class StatusService:
|
||||
tot_qty, delivered_qty = 0.0, 0.0
|
||||
|
||||
for item in doc.items:
|
||||
if item.skip_delivery:
|
||||
continue
|
||||
|
||||
if item.delivered_by_supplier:
|
||||
item_delivered_qty = frappe.get_all(
|
||||
"Purchase Order Item",
|
||||
|
||||
@@ -107,28 +107,119 @@ class TestSalesOrder(ERPNextTestSuite):
|
||||
mr.reload()
|
||||
self.assertRaises(frappe.ValidationError, make_material_request, so.name)
|
||||
|
||||
def test_sales_order_skip_delivery_note(self):
|
||||
so = make_sales_order(do_not_submit=True)
|
||||
@ERPNextTestSuite.change_settings("Selling Settings", {"skip_delivery_note_for_service_items": 1})
|
||||
def test_maintenance_order_completes_with_service_items(self):
|
||||
service_item = make_item("_Test Service Item For Skip DN", {"is_stock_item": 0}).name
|
||||
so = make_sales_order(item_code=service_item, qty=2, rate=100, do_not_submit=True)
|
||||
so.order_type = "Maintenance"
|
||||
so.skip_delivery_note = 1
|
||||
so.append(
|
||||
"items",
|
||||
{
|
||||
"item_code": "_Test Item 2",
|
||||
"qty": 2,
|
||||
"rate": 100,
|
||||
},
|
||||
)
|
||||
so.save()
|
||||
so.submit()
|
||||
|
||||
so.reload()
|
||||
self.assertEqual(so.items[0].skip_delivery, 1)
|
||||
self.assertEqual(flt(so.per_delivered), 100)
|
||||
self.assertEqual(so.delivery_status, "Not Applicable")
|
||||
|
||||
si = make_sales_invoice(so.name)
|
||||
si.insert()
|
||||
si.submit()
|
||||
|
||||
so.reload()
|
||||
self.assertEqual(so.status, "Completed")
|
||||
|
||||
@ERPNextTestSuite.change_settings("Selling Settings", {"skip_delivery_note_for_service_items": 1})
|
||||
def test_auto_skip_delivery_note_for_service_items(self):
|
||||
service_item = make_item("_Test Service Item For Skip DN", {"is_stock_item": 0}).name
|
||||
so = make_sales_order(item_code=service_item, qty=2, rate=100)
|
||||
so.reload()
|
||||
|
||||
self.assertEqual(so.items[0].skip_delivery, 1)
|
||||
self.assertEqual(flt(so.per_delivered), 100)
|
||||
self.assertEqual(so.delivery_status, "Not Applicable")
|
||||
self.assertEqual(so.status, "To Bill")
|
||||
|
||||
si = make_sales_invoice(so.name)
|
||||
si.insert()
|
||||
si.submit()
|
||||
|
||||
so.reload()
|
||||
self.assertEqual(so.status, "Completed")
|
||||
|
||||
@ERPNextTestSuite.change_settings("Selling Settings", {"skip_delivery_note_for_service_items": 1})
|
||||
def test_mixed_sales_order_with_service_items(self):
|
||||
service_item = make_item("_Test Service Item For Skip DN", {"is_stock_item": 0}).name
|
||||
make_stock_entry(item_code="_Test Item", target="_Test Warehouse - _TC", qty=10, rate=100)
|
||||
|
||||
so = make_sales_order(
|
||||
item_list=[
|
||||
{
|
||||
"item_code": "_Test Item",
|
||||
"qty": 2,
|
||||
"rate": 100,
|
||||
"warehouse": "_Test Warehouse - _TC",
|
||||
},
|
||||
{"item_code": service_item, "qty": 1, "rate": 50},
|
||||
]
|
||||
)
|
||||
|
||||
self.assertEqual(so.items[0].skip_delivery, 0)
|
||||
self.assertEqual(so.items[1].skip_delivery, 1)
|
||||
|
||||
dn = make_delivery_note(so.name)
|
||||
self.assertEqual(len(dn.items), 1)
|
||||
self.assertEqual(dn.items[0].item_code, "_Test Item")
|
||||
dn.insert()
|
||||
dn.submit()
|
||||
|
||||
so.reload()
|
||||
self.assertEqual(flt(so.per_delivered), 100)
|
||||
|
||||
si = make_sales_invoice(so.name)
|
||||
si.insert()
|
||||
si.submit()
|
||||
|
||||
so.reload()
|
||||
self.assertEqual(so.status, "Completed")
|
||||
|
||||
@ERPNextTestSuite.change_settings("Selling Settings", {"skip_delivery_note_for_service_items": 1})
|
||||
def test_no_skip_delivery_for_bundle_with_stock_items(self):
|
||||
make_item("_Test Bundle Parent For Skip DN", {"is_stock_item": 0})
|
||||
make_item("_Test Bundle Child For Skip DN", {"is_stock_item": 1})
|
||||
make_product_bundle("_Test Bundle Parent For Skip DN", ["_Test Bundle Child For Skip DN"], 1)
|
||||
|
||||
so = make_sales_order(item_code="_Test Bundle Parent For Skip DN", qty=1, rate=100)
|
||||
|
||||
self.assertEqual(so.items[0].skip_delivery, 0)
|
||||
|
||||
def test_service_item_needs_delivery_when_setting_disabled(self):
|
||||
service_item = make_item("_Test Service Item For Skip DN", {"is_stock_item": 0}).name
|
||||
so = make_sales_order(item_code=service_item, qty=1, rate=100)
|
||||
|
||||
self.assertEqual(so.items[0].skip_delivery, 0)
|
||||
self.assertEqual(flt(so.per_delivered), 0)
|
||||
|
||||
si = make_sales_invoice(so.name)
|
||||
si.insert()
|
||||
si.submit()
|
||||
|
||||
so.reload()
|
||||
self.assertEqual(so.status, "To Deliver")
|
||||
|
||||
@ERPNextTestSuite.change_settings("Selling Settings", {"skip_delivery_note_for_service_items": 1})
|
||||
def test_stale_skip_delivery_cleared_after_setting_disabled(self):
|
||||
service_item = make_item("_Test Service Item For Skip DN", {"is_stock_item": 0}).name
|
||||
so = make_sales_order(item_code=service_item, qty=1, rate=100, do_not_submit=True)
|
||||
|
||||
self.assertEqual(so.items[0].skip_delivery, 1)
|
||||
self.assertEqual(flt(so.per_delivered), 100)
|
||||
self.assertEqual(so.delivery_status, "Not Applicable")
|
||||
|
||||
with self.change_settings("Selling Settings", {"skip_delivery_note_for_service_items": 0}):
|
||||
so.save()
|
||||
|
||||
self.assertEqual(so.items[0].skip_delivery, 0)
|
||||
self.assertEqual(flt(so.per_delivered), 0)
|
||||
self.assertEqual(so.delivery_status, "Not Delivered")
|
||||
|
||||
@ERPNextTestSuite.change_settings(
|
||||
"Selling Settings", {"allow_multiple_items": 1, "allow_negative_rates_for_items": 1}
|
||||
)
|
||||
|
||||
@@ -70,6 +70,7 @@
|
||||
"gross_profit",
|
||||
"drop_ship_section",
|
||||
"delivered_by_supplier",
|
||||
"skip_delivery",
|
||||
"supplier",
|
||||
"item_weight_details",
|
||||
"weight_per_unit",
|
||||
@@ -203,7 +204,6 @@
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"columns": 2,
|
||||
"depends_on": "eval: !parent.skip_delivery_note",
|
||||
"fieldname": "delivery_date",
|
||||
"fieldtype": "Date",
|
||||
"in_list_view": 1,
|
||||
@@ -507,6 +507,16 @@
|
||||
"label": "Supplier delivers to Customer",
|
||||
"print_hide": 1
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"fieldname": "skip_delivery",
|
||||
"fieldtype": "Check",
|
||||
"hidden": 1,
|
||||
"label": "Skip Delivery",
|
||||
"no_copy": 1,
|
||||
"print_hide": 1,
|
||||
"read_only": 1
|
||||
},
|
||||
{
|
||||
"allow_on_submit": 1,
|
||||
"fieldname": "supplier",
|
||||
@@ -1056,7 +1066,7 @@
|
||||
"idx": 1,
|
||||
"istable": 1,
|
||||
"links": [],
|
||||
"modified": "2026-08-07 17:31:31.732720",
|
||||
"modified": "2026-08-25 10:00:00.000000",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Sales Order Item",
|
||||
|
||||
@@ -83,6 +83,7 @@ class SalesOrderItem(Document):
|
||||
requested_qty: DF.Float
|
||||
reserve_stock: DF.Check
|
||||
returned_qty: DF.Float
|
||||
skip_delivery: DF.Check
|
||||
stock_qty: DF.Float
|
||||
stock_reserved_qty: DF.Float
|
||||
stock_uom: DF.Link | None
|
||||
|
||||
@@ -32,6 +32,7 @@
|
||||
"sales_transactions_settings_section",
|
||||
"so_required",
|
||||
"dn_required",
|
||||
"skip_delivery_note_for_service_items",
|
||||
"sales_update_frequency",
|
||||
"column_break_5",
|
||||
"allow_multiple_items",
|
||||
@@ -116,6 +117,13 @@
|
||||
"label": "Is Delivery Note required to create Sales Invoice?",
|
||||
"options": "No\nYes"
|
||||
},
|
||||
{
|
||||
"default": "0",
|
||||
"description": "Non-stock items will not require a Delivery Note. Sales Orders will be marked as Completed once all stock items are delivered and the order is fully billed",
|
||||
"fieldname": "skip_delivery_note_for_service_items",
|
||||
"fieldtype": "Check",
|
||||
"label": "Skip Delivery Note Creation for Service Items"
|
||||
},
|
||||
{
|
||||
"default": "Daily",
|
||||
"description": "The frequency at which project progress and company transaction details will be updated. Set it to daily or monthly if you post a lot of transactions.",
|
||||
@@ -435,7 +443,7 @@
|
||||
"index_web_pages_for_search": 1,
|
||||
"issingle": 1,
|
||||
"links": [],
|
||||
"modified": "2026-04-29 11:05:48.836362",
|
||||
"modified": "2026-08-19 11:00:00.000000",
|
||||
"modified_by": "Administrator",
|
||||
"module": "Selling",
|
||||
"name": "Selling Settings",
|
||||
|
||||
@@ -172,6 +172,7 @@ class DeliveryNote(SellingController):
|
||||
"percent_join_field": "against_sales_order",
|
||||
"status_field": "delivery_status",
|
||||
"keyword": "Delivered",
|
||||
"exclude_field": "skip_delivery",
|
||||
"second_source_dt": "Sales Invoice Item",
|
||||
"second_source_field": "qty",
|
||||
"second_join_field": "so_detail",
|
||||
|
||||
Reference in New Issue
Block a user