From 38a46424799bb4dc2481d8aa884c0e051874a93f Mon Sep 17 00:00:00 2001 From: l0gesh29 Date: Tue, 23 Dec 2025 20:10:38 +0530 Subject: [PATCH 01/11] feat: modify field properties --- erpnext/accounts/doctype/sales_invoice/sales_invoice.json | 8 +++----- .../sales_invoice_timesheet/sales_invoice_timesheet.json | 6 +++--- erpnext/projects/doctype/timesheet/timesheet.json | 7 ++++--- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index a889ca3c2ed..80bc226388c 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -772,8 +772,7 @@ }, { "collapsible": 1, - "collapsible_depends_on": "eval:doc.total_billing_amount > 0", - "depends_on": "eval:!doc.is_return", + "depends_on": "eval:doc.total_billing_amount > 0", "fieldname": "time_sheet_list", "fieldtype": "Section Break", "hide_border": 1, @@ -787,7 +786,6 @@ "hide_days": 1, "hide_seconds": 1, "label": "Time Sheets", - "no_copy": 1, "options": "Sales Invoice Timesheet", "print_hide": 1 }, @@ -2086,7 +2084,7 @@ "fieldtype": "Column Break" }, { - "depends_on": "eval:(!doc.is_return && doc.total_billing_amount > 0)", + "depends_on": "eval:doc.total_billing_amount > 0", "fieldname": "section_break_104", "fieldtype": "Section Break" }, @@ -2260,7 +2258,7 @@ "link_fieldname": "consolidated_invoice" } ], - "modified": "2025-10-09 14:48:59.472826", + "modified": "2025-12-23 20:09:14.424656", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", diff --git a/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json b/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json index 1302fd38b26..cf9a90062b2 100644 --- a/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json +++ b/erpnext/accounts/doctype/sales_invoice_timesheet/sales_invoice_timesheet.json @@ -52,7 +52,6 @@ "fieldtype": "Data", "hidden": 1, "label": "Timesheet Detail", - "no_copy": 1, "print_hide": 1, "read_only": 1 }, @@ -117,15 +116,16 @@ ], "istable": 1, "links": [], - "modified": "2024-03-27 13:10:36.562795", + "modified": "2025-12-23 13:54:17.677187", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice Timesheet", "owner": "Administrator", "permissions": [], "quick_entry": 1, + "row_format": "Dynamic", "sort_field": "creation", "sort_order": "DESC", "states": [], "track_changes": 1 -} \ No newline at end of file +} diff --git a/erpnext/projects/doctype/timesheet/timesheet.json b/erpnext/projects/doctype/timesheet/timesheet.json index 255f7e8ed97..0022ef9193b 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.json +++ b/erpnext/projects/doctype/timesheet/timesheet.json @@ -91,7 +91,7 @@ "in_standard_filter": 1, "label": "Status", "no_copy": 1, - "options": "Draft\nSubmitted\nBilled\nPayslip\nCompleted\nCancelled", + "options": "Draft\nSubmitted\nPartially Billed\nBilled\nPayslip\nCompleted\nCancelled", "print_hide": 1, "read_only": 1 }, @@ -310,7 +310,7 @@ "idx": 1, "is_submittable": 1, "links": [], - "modified": "2024-03-27 13:10:53.551907", + "modified": "2025-12-19 13:48:23.453636", "modified_by": "Administrator", "module": "Projects", "name": "Timesheet", @@ -386,8 +386,9 @@ "write": 1 } ], + "row_format": "Dynamic", "sort_field": "creation", "sort_order": "ASC", "states": [], "title_field": "title" -} \ No newline at end of file +} From c87b5d31323ab7018f8080b8e1dafac3e470c7c6 Mon Sep 17 00:00:00 2001 From: l0gesh29 Date: Tue, 23 Dec 2025 20:13:54 +0530 Subject: [PATCH 02/11] feat(timesheet): handle partial billing in sales invoice --- .../doctype/sales_invoice/sales_invoice.py | 39 +++++++++++++++---- .../projects/doctype/timesheet/timesheet.py | 9 ++++- 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 46797291d92..fbfb5a4c09a 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -340,10 +340,17 @@ class SalesInvoice(SellingController): self.is_opening = "No" self.set_against_income_account() - self.validate_time_sheets_are_submitted() + + if not self.is_return: + self.validate_time_sheets_are_submitted() + self.validate_multiple_billing("Delivery Note", "dn_detail", "amount") - if self.is_return: - self.timesheets = [] + + if self.is_return and self.return_against: + for row in self.timesheets: + row.billing_hours *= -1 + row.billing_amount *= -1 + self.update_packing_list() self.set_billing_hours_and_amount() self.update_timesheet_billing_for_project() @@ -502,7 +509,7 @@ class SalesInvoice(SellingController): if cint(self.is_pos) != 1 and not self.is_return: self.update_against_document_in_jv() - self.update_time_sheet(self.name) + self.update_time_sheet(None if (self.is_return and self.return_against) else self.name) if frappe.get_single_value("Selling Settings", "sales_update_frequency") == "Each Transaction": update_company_current_month_sales(self.company) @@ -820,8 +827,19 @@ class SalesInvoice(SellingController): for data in timesheet.time_logs: if ( (self.project and args.timesheet_detail == data.name) - or (not self.project and not data.sales_invoice) - or (not sales_invoice and data.sales_invoice == self.name) + or (not self.project and not data.sales_invoice and args.timesheet_detail == data.name) + or ( + not sales_invoice + and data.sales_invoice == self.name + and args.timesheet_detail == data.name + ) + or ( + self.is_return + and self.return_against + and data.sales_invoice + and not sales_invoice + and args.timesheet_detail == data.name + ) ): data.sales_invoice = sales_invoice @@ -864,7 +882,7 @@ class SalesInvoice(SellingController): for data in self.timesheets: if data.time_sheet: status = frappe.db.get_value("Timesheet", data.time_sheet, "status") - if status not in ["Submitted", "Payslip"]: + if status not in ["Submitted", "Payslip", "Partially Billed"]: frappe.throw(_("Timesheet {0} is already completed or cancelled").format(data.time_sheet)) def set_pos_fields(self, for_validate=False): @@ -1299,7 +1317,12 @@ class SalesInvoice(SellingController): timesheet.billing_amount = ts_doc.total_billable_amount def update_timesheet_billing_for_project(self): - if not self.timesheets and self.project and self.is_auto_fetch_timesheet_enabled(): + if ( + not self.is_return + and not self.timesheets + and self.project + and self.is_auto_fetch_timesheet_enabled() + ): self.add_timesheet_data() else: self.calculate_billing_amount_for_timesheet() diff --git a/erpnext/projects/doctype/timesheet/timesheet.py b/erpnext/projects/doctype/timesheet/timesheet.py index 5ef50218a3f..347b09b4656 100644 --- a/erpnext/projects/doctype/timesheet/timesheet.py +++ b/erpnext/projects/doctype/timesheet/timesheet.py @@ -51,7 +51,9 @@ class Timesheet(Document): per_billed: DF.Percent sales_invoice: DF.Link | None start_date: DF.Date | None - status: DF.Literal["Draft", "Submitted", "Billed", "Payslip", "Completed", "Cancelled"] + status: DF.Literal[ + "Draft", "Submitted", "Partially Billed", "Billed", "Payslip", "Completed", "Cancelled" + ] time_logs: DF.Table[TimesheetDetail] title: DF.Data | None total_billable_amount: DF.Currency @@ -120,6 +122,9 @@ class Timesheet(Document): if flt(self.per_billed, self.precision("per_billed")) >= 100.0: self.status = "Billed" + if 0.0 < flt(self.per_billed, self.precision("per_billed")) < 100.0: + self.status = "Partially Billed" + if self.sales_invoice: self.status = "Completed" @@ -425,7 +430,7 @@ def make_sales_invoice(source_name, item_code=None, customer=None, currency=None target.append("items", {"item_code": item_code, "qty": hours, "rate": billing_rate}) for time_log in timesheet.time_logs: - if time_log.is_billable: + if time_log.is_billable and not time_log.sales_invoice: target.append( "timesheets", { From ff0b37055b9890ab368fc60553103dd2eca46a35 Mon Sep 17 00:00:00 2001 From: l0gesh29 Date: Tue, 23 Dec 2025 20:14:45 +0530 Subject: [PATCH 03/11] feat: add list_view status for partial billing --- erpnext/projects/doctype/timesheet/timesheet_list.js | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/erpnext/projects/doctype/timesheet/timesheet_list.js b/erpnext/projects/doctype/timesheet/timesheet_list.js index 0de568ce589..b733cccc787 100644 --- a/erpnext/projects/doctype/timesheet/timesheet_list.js +++ b/erpnext/projects/doctype/timesheet/timesheet_list.js @@ -1,6 +1,10 @@ frappe.listview_settings["Timesheet"] = { add_fields: ["status", "total_hours", "start_date", "end_date"], get_indicator: function (doc) { + if (doc.status == "Partially Billed") { + return [__("Partially Billed"), "orange", "status,=," + "Partially Billed"]; + } + if (doc.status == "Billed") { return [__("Billed"), "green", "status,=," + "Billed"]; } From 57d34ab146868f50884a52dfdcbf76ec0d267925 Mon Sep 17 00:00:00 2001 From: l0gesh29 Date: Wed, 24 Dec 2025 18:35:48 +0530 Subject: [PATCH 04/11] fix: include total hours validation in depends on --- erpnext/accounts/doctype/sales_invoice/sales_invoice.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json index 80bc226388c..afc19b02adc 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.json +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.json @@ -772,7 +772,7 @@ }, { "collapsible": 1, - "depends_on": "eval:doc.total_billing_amount > 0", + "collapsible_depends_on": "eval:doc.total_billing_amount > 0 || doc.total_billing_hours > 0", "fieldname": "time_sheet_list", "fieldtype": "Section Break", "hide_border": 1, @@ -2084,7 +2084,7 @@ "fieldtype": "Column Break" }, { - "depends_on": "eval:doc.total_billing_amount > 0", + "depends_on": "eval:doc.total_billing_amount > 0 || doc.total_billing_hours > 0", "fieldname": "section_break_104", "fieldtype": "Section Break" }, @@ -2258,7 +2258,7 @@ "link_fieldname": "consolidated_invoice" } ], - "modified": "2025-12-23 20:09:14.424656", + "modified": "2025-12-24 18:29:50.242618", "modified_by": "Administrator", "module": "Accounts", "name": "Sales Invoice", From ae594e81f98a6edd904755d70404427c7bb1e942 Mon Sep 17 00:00:00 2001 From: l0gesh29 Date: Wed, 24 Dec 2025 22:08:28 +0530 Subject: [PATCH 05/11] test: add test for partial billing and return --- .../doctype/timesheet/test_timesheet.py | 59 ++++++++++++++++++- 1 file changed, 58 insertions(+), 1 deletion(-) diff --git a/erpnext/projects/doctype/timesheet/test_timesheet.py b/erpnext/projects/doctype/timesheet/test_timesheet.py index 046db983dc0..4735b3df870 100644 --- a/erpnext/projects/doctype/timesheet/test_timesheet.py +++ b/erpnext/projects/doctype/timesheet/test_timesheet.py @@ -7,6 +7,7 @@ import frappe from frappe.tests import IntegrationTestCase from frappe.utils import add_to_date, now_datetime, nowdate +from erpnext.accounts.doctype.sales_invoice.sales_invoice import make_sales_return from erpnext.accounts.doctype.sales_invoice.test_sales_invoice import create_sales_invoice from erpnext.projects.doctype.timesheet.timesheet import OverlapError, make_sales_invoice from erpnext.setup.doctype.employee.test_employee import make_employee @@ -207,6 +208,60 @@ class TestTimesheet(ERPNextTestSuite): ts.calculate_percentage_billed() self.assertEqual(ts.per_billed, 100) + def test_partial_billing_and_return(self): + """ + Test Timesheet status transitions during partial billing, full billing, + sales return, and return cancellation. + + Scenario: + 1. Create a Timesheet with two billable time logs. + 2. Create a Sales Invoice billing only one time log → Timesheet becomes Partially Billed. + 3. Create another Sales Invoice billing the remaining time log → Timesheet becomes Billed. + 4. Create a Sales Return against the second invoice → Timesheet reverts to Partially Billed. + 5. Cancel the Sales Return → Timesheet returns to Billed status. + + This test ensures Timesheet status is recalculated correctly + across billing and return lifecycle events. + """ + emp = make_employee("test_employee_6@salary.com") + + timesheet = make_timesheet(emp, simulate=True, is_billable=1, do_not_submit=True) + timesheet_detail = timesheet.append("time_logs", {}) + timesheet_detail.is_billable = 1 + timesheet_detail.activity_type = "_Test Activity Type" + timesheet_detail.from_time = timesheet.time_logs[0].to_time + datetime.timedelta(minutes=1) + timesheet_detail.hours = 2 + timesheet_detail.to_time = timesheet_detail.from_time + datetime.timedelta( + hours=timesheet_detail.hours + ) + timesheet.save().submit() + + sales_invoice = make_sales_invoice(timesheet.name, "_Test Item", "_Test Customer", currency="INR") + sales_invoice.due_date = nowdate() + sales_invoice.timesheets.pop() + sales_invoice.submit() + + timesheet_status = frappe.get_value("Timesheet", timesheet.name, "status") + self.assertEqual(timesheet_status, "Partially Billed") + + sales_invoice2 = make_sales_invoice(timesheet.name, "_Test Item", "_Test Customer", currency="INR") + sales_invoice2.due_date = nowdate() + sales_invoice2.submit() + + timesheet_status = frappe.get_value("Timesheet", timesheet.name, "status") + self.assertEqual(timesheet_status, "Billed") + + sales_return = make_sales_return(sales_invoice2.name).submit() + timesheet_status = frappe.get_value("Timesheet", timesheet.name, "status") + self.assertEqual(timesheet_status, "Partially Billed") + + sales_return.load_from_db() + sales_return.cancel() + + timesheet.load_from_db() + self.assertEqual(timesheet.time_logs[1].sales_invoice, sales_invoice2.name) + self.assertEqual(timesheet.status, "Billed") + def make_timesheet( employee, @@ -218,6 +273,7 @@ def make_timesheet( company=None, currency=None, exchange_rate=None, + do_not_submit=False, ): update_activity_type(activity_type) timesheet = frappe.new_doc("Timesheet") @@ -246,7 +302,8 @@ def make_timesheet( else: timesheet.save(ignore_permissions=True) - timesheet.submit() + if not do_not_submit: + timesheet.submit() return timesheet From 50f73a5072b471e347decf2819879a5a957dbe0a Mon Sep 17 00:00:00 2001 From: l0gesh29 Date: Wed, 24 Dec 2025 22:09:03 +0530 Subject: [PATCH 06/11] fix: handle return cancellation --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index fbfb5a4c09a..000d1f602c5 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -589,7 +589,7 @@ class SalesInvoice(SellingController): self.check_if_consolidated_invoice() super().before_cancel() - self.update_time_sheet(None) + self.update_time_sheet(self.return_against if (self.is_return and self.return_against) else None) def on_cancel(self): check_if_return_invoice_linked_with_payment_entry(self) From cda8a97f4a0344f7e8de04dc52305709fffc14fc Mon Sep 17 00:00:00 2001 From: l0gesh29 Date: Fri, 9 Jan 2026 17:43:55 +0530 Subject: [PATCH 07/11] fix: add validation for duplication --- .../accounts/doctype/sales_invoice/sales_invoice.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 000d1f602c5..2e292fc79d7 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -885,6 +885,16 @@ class SalesInvoice(SellingController): if status not in ["Submitted", "Payslip", "Partially Billed"]: frappe.throw(_("Timesheet {0} is already completed or cancelled").format(data.time_sheet)) + if data.time_sheet and data.timesheet_detail: + if sales_invoice := frappe.db.get_value( + "Timesheet Detail", data.timesheet_detail, "sales_invoice" + ): + frappe.throw( + _("Row {0}: Sales Invoice {1} is already created for {2}").format( + data.idx, frappe.bold(sales_invoice), frappe.bold(data.time_sheet) + ) + ) + def set_pos_fields(self, for_validate=False): """Set retail related fields from POS Profiles""" if cint(self.is_pos) != 1: From 43d1d685c63ec8129fe02133cc2f19821f4cd093 Mon Sep 17 00:00:00 2001 From: Logesh Periyasamy Date: Fri, 9 Jan 2026 18:11:42 +0530 Subject: [PATCH 08/11] fix: add validation for amount and hours Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com> --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index 2e292fc79d7..a61717040a7 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -348,8 +348,10 @@ class SalesInvoice(SellingController): if self.is_return and self.return_against: for row in self.timesheets: - row.billing_hours *= -1 - row.billing_amount *= -1 + if row.billing_hours > 0: + row.billing_hours *= -1 + if row.billing_amount > 0: + row.billing_amount *= -1 self.update_packing_list() self.set_billing_hours_and_amount() From ff9b936634fbd3d17677dbe124cab3521c0458f7 Mon Sep 17 00:00:00 2001 From: l0gesh29 Date: Fri, 9 Jan 2026 19:08:23 +0530 Subject: [PATCH 09/11] fix: add validation for return against --- .../doctype/sales_invoice/sales_invoice.py | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index a61717040a7..a7df2d38420 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -839,6 +839,7 @@ class SalesInvoice(SellingController): self.is_return and self.return_against and data.sales_invoice + and data.sales_invoice == self.return_against and not sales_invoice and args.timesheet_detail == data.name ) @@ -881,12 +882,10 @@ class SalesInvoice(SellingController): payment.account = get_bank_cash_account(payment.mode_of_payment, self.company).get("account") def validate_time_sheets_are_submitted(self): + # Note: This validation is skipped for return invoices + # to allow returns to reference already-billed timesheet details for data in self.timesheets: - if data.time_sheet: - status = frappe.db.get_value("Timesheet", data.time_sheet, "status") - if status not in ["Submitted", "Payslip", "Partially Billed"]: - frappe.throw(_("Timesheet {0} is already completed or cancelled").format(data.time_sheet)) - + # Handle invoice duplication if data.time_sheet and data.timesheet_detail: if sales_invoice := frappe.db.get_value( "Timesheet Detail", data.timesheet_detail, "sales_invoice" @@ -897,6 +896,11 @@ class SalesInvoice(SellingController): ) ) + if data.time_sheet: + status = frappe.db.get_value("Timesheet", data.time_sheet, "status") + if status not in ["Submitted", "Payslip", "Partially Billed"]: + frappe.throw(_("Timesheet {0} is already completed or cancelled").format(data.time_sheet)) + def set_pos_fields(self, for_validate=False): """Set retail related fields from POS Profiles""" if cint(self.is_pos) != 1: From 8379b39aaf7a40ec416f64ee74f618d44cdadb05 Mon Sep 17 00:00:00 2001 From: l0gesh29 Date: Sun, 11 Jan 2026 13:52:46 +0530 Subject: [PATCH 10/11] fix: add validation for direct return --- .../accounts/doctype/sales_invoice/sales_invoice.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index a7df2d38420..aec346f6783 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -341,6 +341,9 @@ class SalesInvoice(SellingController): self.set_against_income_account() + if self.is_return and not self.return_against and self.timesheets: + frappe.throw(_("Direct return is not allowed for Timesheet.")) + if not self.is_return: self.validate_time_sheets_are_submitted() @@ -348,10 +351,10 @@ class SalesInvoice(SellingController): if self.is_return and self.return_against: for row in self.timesheets: - if row.billing_hours > 0: - row.billing_hours *= -1 - if row.billing_amount > 0: - row.billing_amount *= -1 + if row.billing_hours: + row.billing_hours = -abs(row.billing_hours) + if row.billing_amount: + row.billing_amount = -abs(row.billing_amount) self.update_packing_list() self.set_billing_hours_and_amount() From f7004aa8c31fc845aa1a8f774c490d50de601b21 Mon Sep 17 00:00:00 2001 From: l0gesh29 Date: Sun, 11 Jan 2026 14:32:15 +0530 Subject: [PATCH 11/11] chore: modify error msg --- erpnext/accounts/doctype/sales_invoice/sales_invoice.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py index aec346f6783..b9f3bb8e2c5 100644 --- a/erpnext/accounts/doctype/sales_invoice/sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/sales_invoice.py @@ -902,7 +902,9 @@ class SalesInvoice(SellingController): if data.time_sheet: status = frappe.db.get_value("Timesheet", data.time_sheet, "status") if status not in ["Submitted", "Payslip", "Partially Billed"]: - frappe.throw(_("Timesheet {0} is already completed or cancelled").format(data.time_sheet)) + frappe.throw( + _("Timesheet {0} cannot be invoiced in its current state").format(data.time_sheet) + ) def set_pos_fields(self, for_validate=False): """Set retail related fields from POS Profiles"""