Merge pull request #37513 from s-aga-r/FIX-4388
fix: GL Entries not getting created for PR Return
diff --git a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
index 6857ba3..061bab3 100644
--- a/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
+++ b/erpnext/accounts/doctype/accounts_settings/accounts_settings.json
@@ -32,6 +32,7 @@
"column_break_19",
"add_taxes_from_item_tax_template",
"book_tax_discount_loss",
+ "round_row_wise_tax",
"print_settings",
"show_inclusive_tax_in_print",
"show_taxes_as_table_in_print",
@@ -414,6 +415,13 @@
"fieldname": "ignore_account_closing_balance",
"fieldtype": "Check",
"label": "Ignore Account Closing Balance"
+ },
+ {
+ "default": "0",
+ "description": "Tax Amount will be rounded on a row(items) level",
+ "fieldname": "round_row_wise_tax",
+ "fieldtype": "Check",
+ "label": "Round Tax Amount Row-wise"
}
],
"icon": "icon-cog",
@@ -421,7 +429,7 @@
"index_web_pages_for_search": 1,
"issingle": 1,
"links": [],
- "modified": "2023-07-27 15:05:34.000264",
+ "modified": "2023-08-28 00:12:02.740633",
"modified_by": "Administrator",
"module": "Accounts",
"name": "Accounts Settings",
diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
index aa3d1b3..e365d60 100644
--- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
+++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py
@@ -5,7 +5,7 @@
import unittest
import frappe
-from frappe.tests.utils import change_settings
+from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import add_days, cint, flt, getdate, nowdate, today
import erpnext
@@ -38,7 +38,7 @@
test_ignore = ["Serial No"]
-class TestPurchaseInvoice(unittest.TestCase, StockTestMixin):
+class TestPurchaseInvoice(FrappeTestCase, StockTestMixin):
@classmethod
def setUpClass(self):
unlink_payment_on_cancel_of_invoice()
@@ -48,6 +48,9 @@
def tearDownClass(self):
unlink_payment_on_cancel_of_invoice(0)
+ def tearDown(self):
+ frappe.db.rollback()
+
def test_purchase_invoice_received_qty(self):
"""
1. Test if received qty is validated against accepted + rejected
@@ -422,6 +425,7 @@
self.assertEqual(tax.tax_amount, expected_values[i][1])
self.assertEqual(tax.total, expected_values[i][2])
+ @change_settings("Accounts Settings", {"unlink_payment_on_cancellation_of_invoice": 1})
def test_purchase_invoice_with_advance(self):
from erpnext.accounts.doctype.journal_entry.test_journal_entry import (
test_records as jv_test_records,
@@ -476,6 +480,7 @@
)
)
+ @change_settings("Accounts Settings", {"unlink_payment_on_cancellation_of_invoice": 1})
def test_invoice_with_advance_and_multi_payment_terms(self):
from erpnext.accounts.doctype.journal_entry.test_journal_entry import (
test_records as jv_test_records,
@@ -1220,6 +1225,7 @@
acc_settings.submit_journal_entriessubmit_journal_entries = 0
acc_settings.save()
+ @change_settings("Accounts Settings", {"unlink_payment_on_cancellation_of_invoice": 1})
def test_gain_loss_with_advance_entry(self):
unlink_enabled = frappe.db.get_value(
"Accounts Settings", "Accounts Settings", "unlink_payment_on_cancel_of_invoice"
@@ -1420,6 +1426,7 @@
)
frappe.db.set_value("Company", "_Test Company", "exchange_gain_loss_account", original_account)
+ @change_settings("Accounts Settings", {"unlink_payment_on_cancellation_of_invoice": 1})
def test_purchase_invoice_advance_taxes(self):
from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry
diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
index 8aa1f4c..c1adffd 100644
--- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
+++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py
@@ -6,7 +6,7 @@
import frappe
from frappe.model.dynamic_links import get_dynamic_link_map
-from frappe.tests.utils import change_settings
+from frappe.tests.utils import FrappeTestCase, change_settings
from frappe.utils import add_days, flt, getdate, nowdate, today
import erpnext
@@ -45,13 +45,17 @@
from erpnext.stock.utils import get_incoming_rate, get_stock_balance
-class TestSalesInvoice(unittest.TestCase):
+class TestSalesInvoice(FrappeTestCase):
def setUp(self):
from erpnext.stock.doctype.stock_ledger_entry.test_stock_ledger_entry import create_items
create_items(["_Test Internal Transfer Item"], uoms=[{"uom": "Box", "conversion_factor": 10}])
create_internal_parties()
setup_accounts()
+ frappe.db.set_single_value("Accounts Settings", "acc_frozen_upto", None)
+
+ def tearDown(self):
+ frappe.db.rollback()
def make(self):
w = frappe.copy_doc(test_records[0])
@@ -179,6 +183,7 @@
self.assertRaises(frappe.LinkExistsError, si.cancel)
unlink_payment_on_cancel_of_invoice()
+ @change_settings("Accounts Settings", {"unlink_payment_on_cancellation_of_invoice": 1})
def test_payment_entry_unlink_against_standalone_credit_note(self):
from erpnext.accounts.doctype.payment_entry.test_payment_entry import get_payment_entry
@@ -1300,6 +1305,7 @@
dn.submit()
return dn
+ @change_settings("Accounts Settings", {"unlink_payment_on_cancellation_of_invoice": 1})
def test_sales_invoice_with_advance(self):
from erpnext.accounts.doctype.journal_entry.test_journal_entry import (
test_records as jv_test_records,
@@ -2775,6 +2781,13 @@
company="_Test Company",
)
+ tds_payable_account = create_account(
+ account_name="TDS Payable",
+ account_type="Tax",
+ parent_account="Duties and Taxes - _TC",
+ company="_Test Company",
+ )
+
si = create_sales_invoice(parent_cost_center="Main - _TC", do_not_save=1)
si.apply_discount_on = "Grand Total"
si.additional_discount_account = additional_discount_account
@@ -3073,8 +3086,8 @@
si.commission_rate = commission_rate
self.assertRaises(frappe.ValidationError, si.save)
+ @change_settings("Accounts Settings", {"acc_frozen_upto": add_days(getdate(), 1)})
def test_sales_invoice_submission_post_account_freezing_date(self):
- frappe.db.set_single_value("Accounts Settings", "acc_frozen_upto", add_days(getdate(), 1))
si = create_sales_invoice(do_not_save=True)
si.posting_date = add_days(getdate(), 1)
si.save()
@@ -3083,8 +3096,6 @@
si.posting_date = getdate()
si.submit()
- frappe.db.set_single_value("Accounts Settings", "acc_frozen_upto", None)
-
def test_over_billing_case_against_delivery_note(self):
"""
Test a case where duplicating the item with qty = 1 in the invoice
@@ -3113,6 +3124,13 @@
frappe.db.set_single_value("Accounts Settings", "over_billing_allowance", over_billing_allowance)
+ @change_settings(
+ "Accounts Settings",
+ {
+ "book_deferred_entries_via_journal_entry": 1,
+ "submit_journal_entries": 1,
+ },
+ )
def test_multi_currency_deferred_revenue_via_journal_entry(self):
deferred_account = create_account(
account_name="Deferred Revenue",
@@ -3120,11 +3138,6 @@
company="_Test Company",
)
- acc_settings = frappe.get_single("Accounts Settings")
- acc_settings.book_deferred_entries_via_journal_entry = 1
- acc_settings.submit_journal_entries = 1
- acc_settings.save()
-
item = create_item("_Test Item for Deferred Accounting")
item.enable_deferred_expense = 1
item.item_defaults[0].deferred_revenue_account = deferred_account
@@ -3190,13 +3203,6 @@
self.assertEqual(expected_gle[i][2], gle.debit)
self.assertEqual(getdate(expected_gle[i][3]), gle.posting_date)
- acc_settings = frappe.get_single("Accounts Settings")
- acc_settings.book_deferred_entries_via_journal_entry = 0
- acc_settings.submit_journal_entries = 0
- acc_settings.save()
-
- frappe.db.set_single_value("Accounts Settings", "acc_frozen_upto", None)
-
def test_standalone_serial_no_return(self):
si = create_sales_invoice(
item_code="_Test Serialized Item With Series", update_stock=True, is_return=True, qty=-1
diff --git a/erpnext/accounts/doctype/subscription/test_subscription.py b/erpnext/accounts/doctype/subscription/test_subscription.py
index 803e879..785fd04 100644
--- a/erpnext/accounts/doctype/subscription/test_subscription.py
+++ b/erpnext/accounts/doctype/subscription/test_subscription.py
@@ -4,6 +4,7 @@
import unittest
import frappe
+from frappe.tests.utils import FrappeTestCase
from frappe.utils.data import (
add_days,
add_months,
@@ -21,11 +22,15 @@
test_dependencies = ("UOM", "Item Group", "Item")
-class TestSubscription(unittest.TestCase):
+class TestSubscription(FrappeTestCase):
def setUp(self):
make_plans()
create_parties()
reset_settings()
+ frappe.db.set_single_value("Accounts Settings", "acc_frozen_upto", None)
+
+ def tearDown(self):
+ frappe.db.rollback()
def test_create_subscription_with_trial_with_correct_period(self):
subscription = create_subscription(
diff --git a/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py b/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py
index 91ad3d6..f2ec31c 100644
--- a/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py
+++ b/erpnext/accounts/report/tax_withholding_details/tax_withholding_details.py
@@ -68,7 +68,11 @@
tax_amount += entry.credit - entry.debit
if net_total_map.get(name):
- total_amount, grand_total, base_total = net_total_map.get(name)
+ if voucher_type == "Journal Entry":
+ # back calcalute total amount from rate and tax_amount
+ total_amount = grand_total = base_total = tax_amount / (rate / 100)
+ else:
+ total_amount, grand_total, base_total = net_total_map.get(name)
else:
total_amount += entry.credit
diff --git a/erpnext/controllers/taxes_and_totals.py b/erpnext/controllers/taxes_and_totals.py
index 95bf0e4..96284d6 100644
--- a/erpnext/controllers/taxes_and_totals.py
+++ b/erpnext/controllers/taxes_and_totals.py
@@ -25,6 +25,9 @@
def __init__(self, doc: Document):
self.doc = doc
frappe.flags.round_off_applicable_accounts = []
+ frappe.flags.round_row_wise_tax = frappe.db.get_single_value(
+ "Accounts Settings", "round_row_wise_tax"
+ )
self._items = self.filter_rows() if self.doc.doctype == "Quotation" else self.doc.get("items")
@@ -370,6 +373,8 @@
for i, tax in enumerate(self.doc.get("taxes")):
# tax_amount represents the amount of tax for the current step
current_tax_amount = self.get_current_tax_amount(item, tax, item_tax_map)
+ if frappe.flags.round_row_wise_tax:
+ current_tax_amount = flt(current_tax_amount, tax.precision("tax_amount"))
# Adjust divisional loss to the last item
if tax.charge_type == "Actual":
@@ -480,10 +485,19 @@
# store tax breakup for each item
key = item.item_code or item.item_name
item_wise_tax_amount = current_tax_amount * self.doc.conversion_rate
- if tax.item_wise_tax_detail.get(key):
- item_wise_tax_amount += tax.item_wise_tax_detail[key][1]
+ if frappe.flags.round_row_wise_tax:
+ item_wise_tax_amount = flt(item_wise_tax_amount, tax.precision("tax_amount"))
+ if tax.item_wise_tax_detail.get(key):
+ item_wise_tax_amount += flt(tax.item_wise_tax_detail[key][1], tax.precision("tax_amount"))
+ tax.item_wise_tax_detail[key] = [
+ tax_rate,
+ flt(item_wise_tax_amount, tax.precision("tax_amount")),
+ ]
+ else:
+ if tax.item_wise_tax_detail.get(key):
+ item_wise_tax_amount += tax.item_wise_tax_detail[key][1]
- tax.item_wise_tax_detail[key] = [tax_rate, flt(item_wise_tax_amount)]
+ tax.item_wise_tax_detail[key] = [tax_rate, flt(item_wise_tax_amount)]
def round_off_totals(self, tax):
if tax.account_head in frappe.flags.round_off_applicable_accounts:
diff --git a/erpnext/public/js/controllers/taxes_and_totals.js b/erpnext/public/js/controllers/taxes_and_totals.js
index 70b70c3..6b613ce 100644
--- a/erpnext/public/js/controllers/taxes_and_totals.js
+++ b/erpnext/public/js/controllers/taxes_and_totals.js
@@ -193,7 +193,7 @@
frappe.flags.round_off_applicable_accounts = [];
if (me.frm.doc.company) {
- return frappe.call({
+ frappe.call({
"method": "erpnext.controllers.taxes_and_totals.get_round_off_applicable_accounts",
"args": {
"company": me.frm.doc.company,
@@ -206,6 +206,11 @@
}
});
}
+
+ frappe.db.get_single_value("Accounts Settings", "round_row_wise_tax")
+ .then((round_row_wise_tax) => {
+ frappe.flags.round_row_wise_tax = round_row_wise_tax;
+ })
}
determine_exclusive_rate() {
@@ -346,6 +351,9 @@
$.each(me.frm.doc["taxes"] || [], function(i, tax) {
// tax_amount represents the amount of tax for the current step
var current_tax_amount = me.get_current_tax_amount(item, tax, item_tax_map);
+ if (frappe.flags.round_row_wise_tax) {
+ current_tax_amount = flt(current_tax_amount, precision("tax_amount", tax));
+ }
// Adjust divisional loss to the last item
if (tax.charge_type == "Actual") {
@@ -480,8 +488,15 @@
}
let item_wise_tax_amount = current_tax_amount * this.frm.doc.conversion_rate;
- if (tax_detail && tax_detail[key])
- item_wise_tax_amount += tax_detail[key][1];
+ if (frappe.flags.round_row_wise_tax) {
+ item_wise_tax_amount = flt(item_wise_tax_amount, precision("tax_amount", tax));
+ if (tax_detail && tax_detail[key]) {
+ item_wise_tax_amount += flt(tax_detail[key][1], precision("tax_amount", tax));
+ }
+ } else {
+ if (tax_detail && tax_detail[key])
+ item_wise_tax_amount += tax_detail[key][1];
+ }
tax_detail[key] = [tax_rate, flt(item_wise_tax_amount, precision("base_tax_amount", tax))];
}
diff --git a/erpnext/regional/united_arab_emirates/utils.py b/erpnext/regional/united_arab_emirates/utils.py
index a910af6..efeaeed 100644
--- a/erpnext/regional/united_arab_emirates/utils.py
+++ b/erpnext/regional/united_arab_emirates/utils.py
@@ -7,32 +7,32 @@
def update_itemised_tax_data(doc):
+ # maybe this should be a standard function rather than a regional one
if not doc.taxes:
return
+ if not doc.items:
+ return
+
+ meta = frappe.get_meta(doc.items[0].doctype)
+ if not meta.has_field("tax_rate"):
+ return
+
itemised_tax = get_itemised_tax(doc.taxes)
for row in doc.items:
- tax_rate = 0.0
- item_tax_rate = 0.0
+ tax_rate, tax_amount = 0.0, 0.0
+ # dont even bother checking in item tax template as it contains both input and output accounts - double the tax rate
+ item_code = row.item_code or row.item_name
+ if itemised_tax.get(item_code):
+ for tax in itemised_tax.get(row.item_code).values():
+ _tax_rate = flt(tax.get("tax_rate", 0), row.precision("tax_rate"))
+ tax_amount += flt((row.net_amount * _tax_rate) / 100, row.precision("tax_amount"))
+ tax_rate += _tax_rate
- if row.item_tax_rate:
- item_tax_rate = frappe.parse_json(row.item_tax_rate)
-
- # First check if tax rate is present
- # If not then look up in item_wise_tax_detail
- if item_tax_rate:
- for account, rate in item_tax_rate.items():
- tax_rate += rate
- elif row.item_code and itemised_tax.get(row.item_code):
- tax_rate = sum([tax.get("tax_rate", 0) for d, tax in itemised_tax.get(row.item_code).items()])
-
- meta = frappe.get_meta(row.doctype)
-
- if meta.has_field("tax_rate"):
- row.tax_rate = flt(tax_rate, row.precision("tax_rate"))
- row.tax_amount = flt((row.net_amount * tax_rate) / 100, row.precision("net_amount"))
- row.total_amount = flt((row.net_amount + row.tax_amount), row.precision("total_amount"))
+ row.tax_rate = flt(tax_rate, row.precision("tax_rate"))
+ row.tax_amount = flt(tax_amount, row.precision("tax_amount"))
+ row.total_amount = flt((row.net_amount + row.tax_amount), row.precision("total_amount"))
def get_account_currency(account):
diff --git a/erpnext/selling/doctype/sales_order/sales_order.py b/erpnext/selling/doctype/sales_order/sales_order.py
index aae0fee..b91002e 100755
--- a/erpnext/selling/doctype/sales_order/sales_order.py
+++ b/erpnext/selling/doctype/sales_order/sales_order.py
@@ -606,29 +606,37 @@
def get_requested_item_qty(sales_order):
- return frappe._dict(
- frappe.db.sql(
- """
- select sales_order_item, sum(qty)
- from `tabMaterial Request Item`
- where docstatus = 1
- and sales_order = %s
- group by sales_order_item
- """,
- sales_order,
- )
- )
+ result = {}
+ for d in frappe.db.get_all(
+ "Material Request Item",
+ filters={"docstatus": 1, "sales_order": sales_order},
+ fields=["sales_order_item", "sum(qty) as qty", "sum(received_qty) as received_qty"],
+ group_by="sales_order_item",
+ ):
+ result[d.sales_order_item] = frappe._dict({"qty": d.qty, "received_qty": d.received_qty})
+
+ return result
@frappe.whitelist()
def make_material_request(source_name, target_doc=None):
requested_item_qty = get_requested_item_qty(source_name)
+ def get_remaining_qty(so_item):
+ return flt(
+ flt(so_item.qty)
+ - flt(requested_item_qty.get(so_item.name, {}).get("qty"))
+ - max(
+ flt(so_item.get("delivered_qty"))
+ - flt(requested_item_qty.get(so_item.name, {}).get("received_qty")),
+ 0,
+ )
+ )
+
def update_item(source, target, source_parent):
# qty is for packed items, because packed items don't have stock_qty field
- qty = source.get("qty")
target.project = source_parent.project
- target.qty = qty - requested_item_qty.get(source.name, 0) - flt(source.get("delivered_qty"))
+ target.qty = get_remaining_qty(source)
target.stock_qty = flt(target.qty) * flt(target.conversion_factor)
args = target.as_dict().copy()
@@ -661,8 +669,8 @@
"Sales Order Item": {
"doctype": "Material Request Item",
"field_map": {"name": "sales_order_item", "parent": "sales_order"},
- "condition": lambda doc: not frappe.db.exists("Product Bundle", doc.item_code)
- and (doc.stock_qty - flt(doc.get("delivered_qty"))) > requested_item_qty.get(doc.name, 0),
+ "condition": lambda item: not frappe.db.exists("Product Bundle", item.item_code)
+ and get_remaining_qty(item) > 0,
"postprocess": update_item,
},
},
diff --git a/erpnext/setup/doctype/driver/driver.json b/erpnext/setup/doctype/driver/driver.json
index 8d426cc..2e994b5 100644
--- a/erpnext/setup/doctype/driver/driver.json
+++ b/erpnext/setup/doctype/driver/driver.json
@@ -157,6 +157,22 @@
"role": "HR Manager",
"share": 1,
"write": 1
+ },
+ {
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Delivery User"
+ },
+ {
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Delivery Manager",
+ "share": 1,
+ "write": 1
}
],
"quick_entry": 1,
@@ -166,4 +182,4 @@
"sort_order": "DESC",
"title_field": "full_name",
"track_changes": 1
-}
\ No newline at end of file
+}
diff --git a/erpnext/setup/doctype/vehicle/vehicle.json b/erpnext/setup/doctype/vehicle/vehicle.json
index ed803a7..b19d459 100644
--- a/erpnext/setup/doctype/vehicle/vehicle.json
+++ b/erpnext/setup/doctype/vehicle/vehicle.json
@@ -860,6 +860,22 @@
"share": 1,
"submit": 0,
"write": 1
+ },
+ {
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Delivery User"
+ },
+ {
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Delivery Manager",
+ "share": 1,
+ "write": 1
}
],
"quick_entry": 1,
@@ -872,4 +888,4 @@
"title_field": "",
"track_changes": 1,
"track_seen": 0
-}
\ No newline at end of file
+}
diff --git a/erpnext/stock/doctype/delivery_note/delivery_note.json b/erpnext/stock/doctype/delivery_note/delivery_note.json
index e0d4919..b85f296 100644
--- a/erpnext/stock/doctype/delivery_note/delivery_note.json
+++ b/erpnext/stock/doctype/delivery_note/delivery_note.json
@@ -1460,6 +1460,36 @@
"read": 1,
"role": "Stock Manager",
"write": 1
+ },
+ {
+ "amend": 1,
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Delivery User",
+ "share": 1,
+ "submit": 1,
+ "write": 1
+ },
+ {
+ "amend": 1,
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Delivery Manager",
+ "share": 1,
+ "submit": 1,
+ "write": 1
}
],
"search_fields": "status,customer,customer_name, territory,base_grand_total",
diff --git a/erpnext/stock/doctype/delivery_settings/delivery_settings.json b/erpnext/stock/doctype/delivery_settings/delivery_settings.json
index 963403b..ad0ac45 100644
--- a/erpnext/stock/doctype/delivery_settings/delivery_settings.json
+++ b/erpnext/stock/doctype/delivery_settings/delivery_settings.json
@@ -239,7 +239,7 @@
"print": 1,
"read": 1,
"report": 0,
- "role": "System Manager",
+ "role": "Delivery Manager",
"set_user_permissions": 0,
"share": 1,
"submit": 0,
@@ -255,4 +255,4 @@
"track_changes": 1,
"track_seen": 0,
"track_views": 0
-}
\ No newline at end of file
+}
diff --git a/erpnext/stock/doctype/delivery_trip/delivery_trip.json b/erpnext/stock/doctype/delivery_trip/delivery_trip.json
index 9d8fe46..ec72af8 100644
--- a/erpnext/stock/doctype/delivery_trip/delivery_trip.json
+++ b/erpnext/stock/doctype/delivery_trip/delivery_trip.json
@@ -188,7 +188,7 @@
],
"is_submittable": 1,
"links": [],
- "modified": "2023-06-27 11:22:27.927637",
+ "modified": "2023-10-01 07:06:06.314503",
"modified_by": "Administrator",
"module": "Stock",
"name": "Delivery Trip",
@@ -224,10 +224,40 @@
"share": 1,
"submit": 1,
"write": 1
+ },
+ {
+ "amend": 1,
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Delivery User",
+ "share": 1,
+ "submit": 1,
+ "write": 1
+ },
+ {
+ "amend": 1,
+ "cancel": 1,
+ "create": 1,
+ "delete": 1,
+ "email": 1,
+ "export": 1,
+ "print": 1,
+ "read": 1,
+ "report": 1,
+ "role": "Delivery Manager",
+ "share": 1,
+ "submit": 1,
+ "write": 1
}
],
"sort_field": "modified",
"sort_order": "DESC",
"states": [],
"title_field": "driver_name"
-}
\ No newline at end of file
+}