diff --git a/posawesome/fixtures/custom_field.json b/posawesome/fixtures/custom_field.json index 2e00aea31..33909f1ca 100644 --- a/posawesome/fixtures/custom_field.json +++ b/posawesome/fixtures/custom_field.json @@ -1,4 +1,58 @@ [ + { + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "collapsible_depends_on": null, + "columns": 0, + "default": null, + "depends_on": null, + "description": null, + "docstatus": 0, + "doctype": "Custom Field", + "dt": "POS Profile", + "fetch_from": null, + "fetch_if_empty": 0, + "fieldname": "custom_pezesha_channel_id", + "fieldtype": "Data", + "hidden": 0, + "hide_border": 0, + "hide_days": 0, + "hide_seconds": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_preview": 0, + "in_standard_filter": 0, + "insert_after": "customer", + "is_system_generated": 0, + "is_virtual": 0, + "label": "Pezesha Channel ID", + "length": 0, + "mandatory_depends_on": null, + "modified": "2024-03-26 13:01:54.453466", + "module": null, + "name": "POS Profile-custom_pezesha_channel_id", + "no_copy": 0, + "non_negative": 0, + "options": null, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": null, + "read_only": 0, + "read_only_depends_on": null, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "sort_options": 0, + "translatable": 1, + "unique": 0, + "width": null + }, { "allow_in_quick_entry": 0, "allow_on_submit": 0, @@ -350,7 +404,7 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "company_address", + "insert_after": "custom_closing_shift_time", "is_system_generated": 0, "is_virtual": 0, "label": "POS Awesome Settings", @@ -384,16 +438,16 @@ "collapsible": 0, "collapsible_depends_on": null, "columns": 0, - "default": null, + "default": "0", "depends_on": null, - "description": "For POS Closing Shift Payment Reconciliation", + "description": null, "docstatus": 0, "doctype": "Custom Field", - "dt": "POS Profile", + "dt": "Customer", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_cash_mode_of_payment", - "fieldtype": "Link", + "fieldname": "posa_discount", + "fieldtype": "Float", "hidden": 0, "hide_border": 0, "hide_days": 0, @@ -404,18 +458,18 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "posa_pos_awesome_settings", + "insert_after": "lead_name", "is_system_generated": 0, "is_virtual": 0, - "label": "Cash Mode of Payment", + "label": "Discount %", "length": 0, "mandatory_depends_on": null, - "modified": "2021-03-06 00:29:24.240940", + "modified": "2021-06-04 21:02:31.784347", "module": null, - "name": "POS Profile-posa_cash_mode_of_payment", + "name": "Customer-posa_discount", "no_copy": 0, - "non_negative": 0, - "options": "Mode of Payment", + "non_negative": 1, + "options": null, "permlevel": 0, "precision": "", "print_hide": 0, @@ -443,10 +497,10 @@ "description": null, "docstatus": 0, "doctype": "Custom Field", - "dt": "Customer", + "dt": "Batch", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_discount", + "fieldname": "posa_batch_price", "fieldtype": "Float", "hidden": 0, "hide_border": 0, @@ -458,17 +512,17 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "lead_name", + "insert_after": "manufacturing_date", "is_system_generated": 0, "is_virtual": 0, - "label": "Discount %", + "label": "Price", "length": 0, "mandatory_depends_on": null, - "modified": "2021-06-04 21:02:31.784347", + "modified": "2020-10-26 02:31:58.913688", "module": null, - "name": "Customer-posa_discount", + "name": "Batch-posa_batch_price", "no_copy": 0, - "non_negative": 1, + "non_negative": 0, "options": null, "permlevel": 0, "precision": "", @@ -492,16 +546,16 @@ "collapsible": 0, "collapsible_depends_on": null, "columns": 0, - "default": "0", + "default": null, "depends_on": null, - "description": null, + "description": "For POS Closing Shift Payment Reconciliation", "docstatus": 0, "doctype": "Custom Field", "dt": "POS Profile", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_allow_delete", - "fieldtype": "Check", + "fieldname": "posa_cash_mode_of_payment", + "fieldtype": "Link", "hidden": 0, "hide_border": 0, "hide_days": 0, @@ -512,18 +566,18 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "posa_cash_mode_of_payment", + "insert_after": "posa_pos_awesome_settings", "is_system_generated": 0, "is_virtual": 0, - "label": "Auto Delete Draft Invoice", + "label": "Cash Mode of Payment", "length": 0, "mandatory_depends_on": null, - "modified": "2020-10-09 16:01:30.649938", + "modified": "2021-03-06 00:29:24.240940", "module": null, - "name": "POS Profile-posa_allow_delete", + "name": "POS Profile-posa_cash_mode_of_payment", "no_copy": 0, "non_negative": 0, - "options": null, + "options": "Mode of Payment", "permlevel": 0, "precision": "", "print_hide": 0, @@ -551,11 +605,11 @@ "description": null, "docstatus": 0, "doctype": "Custom Field", - "dt": "Batch", + "dt": "POS Profile", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_batch_price", - "fieldtype": "Float", + "fieldname": "posa_allow_delete", + "fieldtype": "Check", "hidden": 0, "hide_border": 0, "hide_days": 0, @@ -566,15 +620,15 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "manufacturing_date", + "insert_after": "posa_cash_mode_of_payment", "is_system_generated": 0, "is_virtual": 0, - "label": "Price", + "label": "Auto Delete Draft Invoice", "length": 0, "mandatory_depends_on": null, - "modified": "2020-10-26 02:31:58.913688", + "modified": "2020-10-09 16:01:30.649938", "module": null, - "name": "Batch-posa_batch_price", + "name": "POS Profile-posa_allow_delete", "no_copy": 0, "non_negative": 0, "options": null, @@ -816,16 +870,16 @@ "collapsible": 0, "collapsible_depends_on": null, "columns": 0, - "default": "221", + "default": null, "depends_on": null, - "description": "It is best not to use more than four numbers", + "description": null, "docstatus": 0, "doctype": "Custom Field", - "dt": "POS Profile", + "dt": "Sales Invoice", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_scale_barcode_start", - "fieldtype": "Int", + "fieldname": "posa_pos_opening_shift", + "fieldtype": "Data", "hidden": 0, "hide_border": 0, "hide_days": 0, @@ -835,31 +889,31 @@ "in_global_search": 0, "in_list_view": 0, "in_preview": 0, - "in_standard_filter": 0, - "insert_after": "posa_max_discount_allowed", + "in_standard_filter": 1, + "insert_after": "pos_profile", "is_system_generated": 0, "is_virtual": 0, - "label": "Scale Barcode Start With", + "label": "POS Shift", "length": 0, "mandatory_depends_on": null, - "modified": "2020-10-30 03:54:32.270370", + "modified": "2020-09-27 03:15:11.844405", "module": null, - "name": "POS Profile-posa_scale_barcode_start", - "no_copy": 0, + "name": "Sales Invoice-posa_pos_opening_shift", + "no_copy": 1, "non_negative": 0, "options": null, "permlevel": 0, "precision": "", - "print_hide": 0, + "print_hide": 1, "print_hide_if_no_value": 0, "print_width": null, - "read_only": 0, + "read_only": 1, "read_only_depends_on": null, "report_hide": 0, "reqd": 0, "search_index": 0, "sort_options": 0, - "translatable": 0, + "translatable": 1, "unique": 0, "width": null }, @@ -870,16 +924,16 @@ "collapsible": 0, "collapsible_depends_on": null, "columns": 0, - "default": null, + "default": "", "depends_on": null, "description": null, "docstatus": 0, "doctype": "Custom Field", - "dt": "POS Profile", + "dt": "Address", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_allow_change_posting_date", - "fieldtype": "Check", + "fieldname": "posa_delivery_charges", + "fieldtype": "Link", "hidden": 0, "hide_border": 0, "hide_days": 0, @@ -890,18 +944,18 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "posa_scale_barcode_start", + "insert_after": "is_shipping_address", "is_system_generated": 0, "is_virtual": 0, - "label": "Allow Change Posting Date", + "label": "Delivery Charges", "length": 0, "mandatory_depends_on": null, - "modified": "2022-12-16 11:20:05.134781", + "modified": "2022-07-24 17:20:00.246026", "module": null, - "name": "POS Profile-posa_allow_change_posting_date", + "name": "Address-posa_delivery_charges", "no_copy": 0, "non_negative": 0, - "options": null, + "options": "Delivery Charges", "permlevel": 0, "precision": "", "print_hide": 0, @@ -924,16 +978,16 @@ "collapsible": 0, "collapsible_depends_on": null, "columns": 0, - "default": null, + "default": "221", "depends_on": null, - "description": null, + "description": "It is best not to use more than four numbers", "docstatus": 0, "doctype": "Custom Field", - "dt": "Sales Invoice", + "dt": "POS Profile", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_pos_opening_shift", - "fieldtype": "Data", + "fieldname": "posa_scale_barcode_start", + "fieldtype": "Int", "hidden": 0, "hide_border": 0, "hide_days": 0, @@ -943,31 +997,31 @@ "in_global_search": 0, "in_list_view": 0, "in_preview": 0, - "in_standard_filter": 1, - "insert_after": "pos_profile", + "in_standard_filter": 0, + "insert_after": "posa_max_discount_allowed", "is_system_generated": 0, "is_virtual": 0, - "label": "POS Shift", + "label": "Scale Barcode Start With", "length": 0, "mandatory_depends_on": null, - "modified": "2020-09-27 03:15:11.844405", + "modified": "2020-10-30 03:54:32.270370", "module": null, - "name": "Sales Invoice-posa_pos_opening_shift", - "no_copy": 1, + "name": "POS Profile-posa_scale_barcode_start", + "no_copy": 0, "non_negative": 0, "options": null, "permlevel": 0, "precision": "", - "print_hide": 1, + "print_hide": 0, "print_hide_if_no_value": 0, "print_width": null, - "read_only": 1, + "read_only": 0, "read_only_depends_on": null, "report_hide": 0, "reqd": 0, "search_index": 0, "sort_options": 0, - "translatable": 1, + "translatable": 0, "unique": 0, "width": null }, @@ -978,16 +1032,16 @@ "collapsible": 0, "collapsible_depends_on": null, "columns": 0, - "default": "", + "default": null, "depends_on": null, "description": null, "docstatus": 0, "doctype": "Custom Field", - "dt": "Address", + "dt": "Sales Invoice", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_delivery_charges", - "fieldtype": "Link", + "fieldname": "posa_is_printed", + "fieldtype": "Check", "hidden": 0, "hide_border": 0, "hide_days": 0, @@ -997,25 +1051,25 @@ "in_global_search": 0, "in_list_view": 0, "in_preview": 0, - "in_standard_filter": 0, - "insert_after": "is_shipping_address", + "in_standard_filter": 1, + "insert_after": "posa_pos_opening_shift", "is_system_generated": 0, "is_virtual": 0, - "label": "Delivery Charges", + "label": "Printed", "length": 0, "mandatory_depends_on": null, - "modified": "2022-07-24 17:20:00.246026", + "modified": "2020-11-02 02:48:23.877227", "module": null, - "name": "Address-posa_delivery_charges", + "name": "Sales Invoice-posa_is_printed", "no_copy": 0, "non_negative": 0, - "options": "Delivery Charges", + "options": null, "permlevel": 0, "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, "print_width": null, - "read_only": 0, + "read_only": 1, "read_only_depends_on": null, "report_hide": 0, "reqd": 0, @@ -1040,7 +1094,7 @@ "dt": "POS Profile", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_default_card_view", + "fieldname": "posa_allow_change_posting_date", "fieldtype": "Check", "hidden": 0, "hide_border": 0, @@ -1052,15 +1106,15 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "posa_allow_change_posting_date", + "insert_after": "posa_scale_barcode_start", "is_system_generated": 0, "is_virtual": 0, - "label": "Default Card View", + "label": "Allow Change Posting Date", "length": 0, "mandatory_depends_on": null, - "modified": "2023-03-12 14:03:44.088542", + "modified": "2022-12-16 11:20:05.134781", "module": null, - "name": "POS Profile-posa_default_card_view", + "name": "POS Profile-posa_allow_change_posting_date", "no_copy": 0, "non_negative": 0, "options": null, @@ -1091,10 +1145,10 @@ "description": null, "docstatus": 0, "doctype": "Custom Field", - "dt": "Sales Invoice", + "dt": "POS Profile", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_is_printed", + "fieldname": "posa_default_card_view", "fieldtype": "Check", "hidden": 0, "hide_border": 0, @@ -1105,16 +1159,16 @@ "in_global_search": 0, "in_list_view": 0, "in_preview": 0, - "in_standard_filter": 1, - "insert_after": "posa_pos_opening_shift", + "in_standard_filter": 0, + "insert_after": "posa_allow_change_posting_date", "is_system_generated": 0, "is_virtual": 0, - "label": "Printed", + "label": "Default Card View", "length": 0, "mandatory_depends_on": null, - "modified": "2020-11-02 02:48:23.877227", + "modified": "2023-03-12 14:03:44.088542", "module": null, - "name": "Sales Invoice-posa_is_printed", + "name": "POS Profile-posa_default_card_view", "no_copy": 0, "non_negative": 0, "options": null, @@ -1123,7 +1177,7 @@ "print_hide": 0, "print_hide_if_no_value": 0, "print_width": null, - "read_only": 1, + "read_only": 0, "read_only_depends_on": null, "report_hide": 0, "reqd": 0, @@ -1947,7 +2001,7 @@ "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, - "collapsible": 0, + "collapsible": 1, "collapsible_depends_on": null, "columns": 0, "default": null, @@ -1955,11 +2009,11 @@ "description": null, "docstatus": 0, "doctype": "Custom Field", - "dt": "POS Profile", + "dt": "Company", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_column_break_112", - "fieldtype": "Column Break", + "fieldname": "posa_referral_section", + "fieldtype": "Section Break", "hidden": 0, "hide_border": 0, "hide_days": 0, @@ -1970,15 +2024,15 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "hide_expected_amount", + "insert_after": "total_monthly_sales", "is_system_generated": 0, "is_virtual": 0, - "label": "", + "label": "Referral Code", "length": 0, "mandatory_depends_on": null, - "modified": "2021-06-23 22:48:40.886230", + "modified": "2021-07-29 23:04:22.290849", "module": null, - "name": "POS Profile-posa_column_break_112", + "name": "Company-posa_referral_section", "no_copy": 0, "non_negative": 0, "options": null, @@ -2012,8 +2066,8 @@ "dt": "POS Profile", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_allow_sales_order", - "fieldtype": "Check", + "fieldname": "posa_column_break_112", + "fieldtype": "Column Break", "hidden": 0, "hide_border": 0, "hide_days": 0, @@ -2024,15 +2078,15 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "posa_column_break_112", + "insert_after": "hide_expected_amount", "is_system_generated": 0, "is_virtual": 0, - "label": "Allow Create Sales Order", + "label": "", "length": 0, "mandatory_depends_on": null, - "modified": "2021-06-22 03:02:27.784096", + "modified": "2021-06-23 22:48:40.886230", "module": null, - "name": "POS Profile-posa_allow_sales_order", + "name": "POS Profile-posa_column_break_112", "no_copy": 0, "non_negative": 0, "options": null, @@ -2051,64 +2105,11 @@ "unique": 0, "width": null }, - { - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "collapsible_depends_on": null, - "columns": 0, - "default": null, - "depends_on": null, - "description": null, - "docstatus": 0, - "doctype": "Custom Field", - "dt": "POS Profile", - "fetch_from": null, - "fetch_if_empty": 0, - "fieldname": "custom_allow_select_sales_order", - "fieldtype": "Check", - "hidden": 0, - "hide_border": 0, - "hide_days": 0, - "hide_seconds": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_preview": 0, - "in_standard_filter": 0, - "insert_after": "posa_allow_sales_order", - "is_system_generated": 0, - "is_virtual": 0, - "label": "Allow Select Sales Order", - "length": 0, - "mandatory_depends_on": null, - "modified": "2023-11-13 12:16:27.784096", - "module": null, - "name": "POS Profile-custom_allow_select_sales_order", - "no_copy": 0, - "non_negative": 0, - "options": null, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": null, - "read_only": 0, - "read_only_depends_on": null, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "translatable": 0, - "unique": 0, - "width": null -}, { "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, - "collapsible": 1, + "collapsible": 0, "collapsible_depends_on": null, "columns": 0, "default": null, @@ -2119,8 +2120,8 @@ "dt": "Company", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_referral_section", - "fieldtype": "Section Break", + "fieldname": "posa_auto_referral", + "fieldtype": "Check", "hidden": 0, "hide_border": 0, "hide_days": 0, @@ -2131,15 +2132,15 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "total_monthly_sales", + "insert_after": "posa_referral_section", "is_system_generated": 0, "is_virtual": 0, - "label": "Referral Code", + "label": "Auto Create Referral For New Customers", "length": 0, "mandatory_depends_on": null, - "modified": "2021-07-29 23:04:22.290849", + "modified": "2021-07-29 23:07:08.681215", "module": null, - "name": "Company-posa_referral_section", + "name": "Company-posa_auto_referral", "no_copy": 0, "non_negative": 0, "options": null, @@ -2173,7 +2174,7 @@ "dt": "POS Profile", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_show_template_items", + "fieldname": "posa_allow_sales_order", "fieldtype": "Check", "hidden": 0, "hide_border": 0, @@ -2185,15 +2186,69 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "posa_allow_sales_order", + "insert_after": "posa_column_break_112", "is_system_generated": 0, "is_virtual": 0, - "label": "Show Template Items", + "label": "Allow Create Sales Order", "length": 0, "mandatory_depends_on": null, - "modified": "2021-06-23 22:48:41.288938", + "modified": "2021-06-22 03:02:27.784096", "module": null, - "name": "POS Profile-posa_show_template_items", + "name": "POS Profile-posa_allow_sales_order", + "no_copy": 0, + "non_negative": 0, + "options": null, + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": null, + "read_only": 0, + "read_only_depends_on": null, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "sort_options": 0, + "translatable": 0, + "unique": 0, + "width": null + }, + { + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 1, + "collapsible_depends_on": null, + "columns": 0, + "default": null, + "depends_on": null, + "description": null, + "docstatus": 0, + "doctype": "Custom Field", + "dt": "Sales Order", + "fetch_from": null, + "fetch_if_empty": 0, + "fieldname": "posa_additional_notes_section", + "fieldtype": "Section Break", + "hidden": 0, + "hide_border": 0, + "hide_days": 0, + "hide_seconds": 0, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_preview": 0, + "in_standard_filter": 0, + "insert_after": "items", + "is_system_generated": 0, + "is_virtual": 0, + "label": "Additional Notes", + "length": 0, + "mandatory_depends_on": null, + "modified": "2021-06-21 16:11:59.366893", + "module": null, + "name": "Sales Order-posa_additional_notes_section", "no_copy": 0, "non_negative": 0, "options": null, @@ -2227,8 +2282,8 @@ "dt": "Company", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_auto_referral", - "fieldtype": "Check", + "fieldname": "posa_column_break_22", + "fieldtype": "Column Break", "hidden": 0, "hide_border": 0, "hide_days": 0, @@ -2239,15 +2294,15 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "posa_referral_section", + "insert_after": "posa_auto_referral", "is_system_generated": 0, "is_virtual": 0, - "label": "Auto Create Referral For New Customers", + "label": "", "length": 0, "mandatory_depends_on": null, - "modified": "2021-07-29 23:07:08.681215", + "modified": "2021-07-29 23:11:04.558635", "module": null, - "name": "Company-posa_auto_referral", + "name": "Company-posa_column_break_22", "no_copy": 0, "non_negative": 0, "options": null, @@ -2274,14 +2329,14 @@ "collapsible_depends_on": null, "columns": 0, "default": null, - "depends_on": "posa_show_template_items", + "depends_on": null, "description": null, "docstatus": 0, "doctype": "Custom Field", "dt": "POS Profile", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_hide_variants_items", + "fieldname": "custom_allow_select_sales_order", "fieldtype": "Check", "hidden": 0, "hide_border": 0, @@ -2293,15 +2348,15 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "posa_show_template_items", + "insert_after": "posa_allow_sales_order", "is_system_generated": 0, "is_virtual": 0, - "label": "Hide Variants Items", + "label": "Allow Select Sales Order", "length": 0, "mandatory_depends_on": null, - "modified": "2021-06-24 03:24:52.591942", + "modified": "2023-11-13 12:16:27.784096", "module": null, - "name": "POS Profile-posa_hide_variants_items", + "name": "POS Profile-custom_allow_select_sales_order", "no_copy": 0, "non_negative": 0, "options": null, @@ -2324,7 +2379,7 @@ "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, - "collapsible": 1, + "collapsible": 0, "collapsible_depends_on": null, "columns": 0, "default": null, @@ -2335,8 +2390,8 @@ "dt": "Sales Order", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_additional_notes_section", - "fieldtype": "Section Break", + "fieldname": "posa_notes", + "fieldtype": "Small Text", "hidden": 0, "hide_border": 0, "hide_days": 0, @@ -2347,15 +2402,15 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "items", + "insert_after": "posa_additional_notes_section", "is_system_generated": 0, "is_virtual": 0, "label": "Additional Notes", "length": 0, "mandatory_depends_on": null, - "modified": "2021-06-21 16:11:59.366893", + "modified": "2021-06-21 16:11:59.829304", "module": null, - "name": "Sales Order-posa_additional_notes_section", + "name": "Sales Order-posa_notes", "no_copy": 0, "non_negative": 0, "options": null, @@ -2370,7 +2425,7 @@ "reqd": 0, "search_index": 0, "sort_options": 0, - "translatable": 0, + "translatable": 1, "unique": 0, "width": null }, @@ -2382,15 +2437,15 @@ "collapsible_depends_on": null, "columns": 0, "default": null, - "depends_on": null, + "depends_on": "posa_auto_referral", "description": null, "docstatus": 0, "doctype": "Custom Field", "dt": "Company", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_column_break_22", - "fieldtype": "Column Break", + "fieldname": "posa_customer_offer", + "fieldtype": "Link", "hidden": 0, "hide_border": 0, "hide_days": 0, @@ -2401,18 +2456,18 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "posa_auto_referral", + "insert_after": "posa_column_break_22", "is_system_generated": 0, "is_virtual": 0, - "label": "", + "label": "Final Customer Offer", "length": 0, - "mandatory_depends_on": null, - "modified": "2021-07-29 23:11:04.558635", + "mandatory_depends_on": "posa_auto_referral", + "modified": "2021-07-29 23:11:04.891539", "module": null, - "name": "Company-posa_column_break_22", + "name": "Company-posa_customer_offer", "no_copy": 0, "non_negative": 0, - "options": null, + "options": "POS Offer", "permlevel": 0, "precision": "", "print_hide": 0, @@ -2443,7 +2498,7 @@ "dt": "POS Profile", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_fetch_coupon", + "fieldname": "posa_show_template_items", "fieldtype": "Check", "hidden": 0, "hide_border": 0, @@ -2455,15 +2510,15 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "posa_hide_variants_items", + "insert_after": "custom_allow_select_sales_order", "is_system_generated": 0, "is_virtual": 0, - "label": "Auto Fetch Coupon Gifts", + "label": "Show Template Items", "length": 0, "mandatory_depends_on": null, - "modified": "2021-07-29 22:58:10.372543", + "modified": "2021-06-23 22:48:41.288938", "module": null, - "name": "POS Profile-posa_fetch_coupon", + "name": "POS Profile-posa_show_template_items", "no_copy": 0, "non_negative": 0, "options": null, @@ -2494,11 +2549,11 @@ "description": null, "docstatus": 0, "doctype": "Custom Field", - "dt": "Sales Order", + "dt": "Customer", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_notes", - "fieldtype": "Small Text", + "fieldname": "posa_birthday", + "fieldtype": "Date", "hidden": 0, "hide_border": 0, "hide_days": 0, @@ -2509,15 +2564,15 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "posa_additional_notes_section", + "insert_after": "contact_html", "is_system_generated": 0, "is_virtual": 0, - "label": "Additional Notes", + "label": "Birthday", "length": 0, "mandatory_depends_on": null, - "modified": "2021-06-21 16:11:59.829304", + "modified": "2021-07-31 00:12:09.417519", "module": null, - "name": "Sales Order-posa_notes", + "name": "Customer-posa_birthday", "no_copy": 0, "non_negative": 0, "options": null, @@ -2532,7 +2587,7 @@ "reqd": 0, "search_index": 0, "sort_options": 0, - "translatable": 1, + "translatable": 0, "unique": 0, "width": null }, @@ -2551,7 +2606,7 @@ "dt": "Company", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_customer_offer", + "fieldname": "posa_primary_offer", "fieldtype": "Link", "hidden": 0, "hide_border": 0, @@ -2563,15 +2618,15 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "posa_column_break_22", + "insert_after": "posa_customer_offer", "is_system_generated": 0, "is_virtual": 0, - "label": "Final Customer Offer", + "label": "Primary Customer Offer", "length": 0, - "mandatory_depends_on": "posa_auto_referral", - "modified": "2021-07-29 23:11:04.891539", + "mandatory_depends_on": null, + "modified": "2021-07-29 23:11:05.290809", "module": null, - "name": "Company-posa_customer_offer", + "name": "Company-posa_primary_offer", "no_copy": 0, "non_negative": 0, "options": "POS Offer", @@ -2598,15 +2653,15 @@ "collapsible_depends_on": null, "columns": 0, "default": null, - "depends_on": null, + "depends_on": "posa_show_template_items", "description": null, "docstatus": 0, "doctype": "Custom Field", - "dt": "Customer", + "dt": "POS Profile", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_birthday", - "fieldtype": "Date", + "fieldname": "posa_hide_variants_items", + "fieldtype": "Check", "hidden": 0, "hide_border": 0, "hide_days": 0, @@ -2617,15 +2672,15 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "contact_html", + "insert_after": "posa_show_template_items", "is_system_generated": 0, "is_virtual": 0, - "label": "Birthday", + "label": "Hide Variants Items", "length": 0, "mandatory_depends_on": null, - "modified": "2021-07-31 00:12:09.417519", + "modified": "2021-06-24 03:24:52.591942", "module": null, - "name": "Customer-posa_birthday", + "name": "POS Profile-posa_hide_variants_items", "no_copy": 0, "non_negative": 0, "options": null, @@ -2648,19 +2703,19 @@ "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, - "collapsible": 0, + "collapsible": 1, "collapsible_depends_on": null, "columns": 0, - "default": "0", + "default": null, "depends_on": null, "description": null, "docstatus": 0, "doctype": "Custom Field", - "dt": "POS Profile", + "dt": "Customer", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_allow_customer_purchase_order", - "fieldtype": "Check", + "fieldname": "posa_referral_section", + "fieldtype": "Section Break", "hidden": 0, "hide_border": 0, "hide_days": 0, @@ -2671,15 +2726,15 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "posa_fetch_coupon", + "insert_after": "posa_birthday", "is_system_generated": 0, "is_virtual": 0, - "label": "Allow Customer Purchase Order", + "label": "Referral Code", "length": 0, "mandatory_depends_on": null, - "modified": "2021-12-16 16:27:32.300240", + "modified": "2021-07-29 23:23:04.910503", "module": null, - "name": "POS Profile-posa_allow_customer_purchase_order", + "name": "Customer-posa_referral_section", "no_copy": 0, "non_negative": 0, "options": null, @@ -2705,7 +2760,7 @@ "collapsible": 0, "collapsible_depends_on": null, "columns": 0, - "default": null, + "default": "", "depends_on": "posa_auto_referral", "description": null, "docstatus": 0, @@ -2713,7 +2768,7 @@ "dt": "Company", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_primary_offer", + "fieldname": "posa_referral_campaign", "fieldtype": "Link", "hidden": 0, "hide_border": 0, @@ -2725,18 +2780,18 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "posa_customer_offer", + "insert_after": "posa_primary_offer", "is_system_generated": 0, "is_virtual": 0, - "label": "Primary Customer Offer", + "label": "Referral Campaign", "length": 0, "mandatory_depends_on": null, - "modified": "2021-07-29 23:11:05.290809", + "modified": "2021-07-29 23:11:05.723688", "module": null, - "name": "Company-posa_primary_offer", + "name": "Company-posa_referral_campaign", "no_copy": 0, "non_negative": 0, - "options": "POS Offer", + "options": "Campaign", "permlevel": 0, "precision": "", "print_hide": 0, @@ -2756,7 +2811,7 @@ "allow_in_quick_entry": 0, "allow_on_submit": 0, "bold": 0, - "collapsible": 1, + "collapsible": 0, "collapsible_depends_on": null, "columns": 0, "default": null, @@ -2764,11 +2819,11 @@ "description": null, "docstatus": 0, "doctype": "Custom Field", - "dt": "Customer", + "dt": "POS Profile", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_referral_section", - "fieldtype": "Section Break", + "fieldname": "posa_fetch_coupon", + "fieldtype": "Check", "hidden": 0, "hide_border": 0, "hide_days": 0, @@ -2779,15 +2834,15 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "posa_birthday", + "insert_after": "posa_hide_variants_items", "is_system_generated": 0, "is_virtual": 0, - "label": "Referral Code", + "label": "Auto Fetch Coupon Gifts", "length": 0, "mandatory_depends_on": null, - "modified": "2021-07-29 23:23:04.910503", + "modified": "2021-07-29 22:58:10.372543", "module": null, - "name": "Customer-posa_referral_section", + "name": "POS Profile-posa_fetch_coupon", "no_copy": 0, "non_negative": 0, "options": null, @@ -2807,22 +2862,22 @@ "width": null }, { - "allow_in_quick_entry": 0, + "allow_in_quick_entry": 1, "allow_on_submit": 0, "bold": 0, "collapsible": 0, "collapsible_depends_on": null, "columns": 0, - "default": "0", + "default": null, "depends_on": null, "description": null, "docstatus": 0, "doctype": "Custom Field", - "dt": "POS Profile", + "dt": "Customer", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_allow_print_last_invoice", - "fieldtype": "Check", + "fieldname": "posa_referral_code", + "fieldtype": "Data", "hidden": 0, "hide_border": 0, "hide_days": 0, @@ -2833,16 +2888,16 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "posa_allow_customer_purchase_order", + "insert_after": "posa_referral_section", "is_system_generated": 0, "is_virtual": 0, - "label": "Allow Print Last Invoice", + "label": "Referral Code", "length": 0, "mandatory_depends_on": null, - "modified": "2021-12-16 18:00:40.631156", + "modified": "2021-07-29 22:42:57.772021", "module": null, - "name": "POS Profile-posa_allow_print_last_invoice", - "no_copy": 0, + "name": "Customer-posa_referral_code", + "no_copy": 1, "non_negative": 0, "options": null, "permlevel": 0, @@ -2856,7 +2911,7 @@ "reqd": 0, "search_index": 0, "sort_options": 0, - "translatable": 0, + "translatable": 1, "unique": 0, "width": null }, @@ -2867,16 +2922,16 @@ "collapsible": 0, "collapsible_depends_on": null, "columns": 0, - "default": "", - "depends_on": "posa_auto_referral", + "default": "0", + "depends_on": null, "description": null, "docstatus": 0, "doctype": "Custom Field", - "dt": "Company", + "dt": "POS Profile", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_referral_campaign", - "fieldtype": "Link", + "fieldname": "posa_allow_customer_purchase_order", + "fieldtype": "Check", "hidden": 0, "hide_border": 0, "hide_days": 0, @@ -2887,18 +2942,18 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "posa_primary_offer", + "insert_after": "posa_fetch_coupon", "is_system_generated": 0, "is_virtual": 0, - "label": "Referral Campaign", + "label": "Allow Customer Purchase Order", "length": 0, "mandatory_depends_on": null, - "modified": "2021-07-29 23:11:05.723688", + "modified": "2021-12-16 16:27:32.300240", "module": null, - "name": "Company-posa_referral_campaign", + "name": "POS Profile-posa_allow_customer_purchase_order", "no_copy": 0, "non_negative": 0, - "options": "Campaign", + "options": null, "permlevel": 0, "precision": "", "print_hide": 0, @@ -2917,7 +2972,7 @@ { "allow_in_quick_entry": 1, "allow_on_submit": 0, - "bold": 0, + "bold": 1, "collapsible": 0, "collapsible_depends_on": null, "columns": 0, @@ -2929,8 +2984,8 @@ "dt": "Customer", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_referral_code", - "fieldtype": "Data", + "fieldname": "posa_referral_company", + "fieldtype": "Link", "hidden": 0, "hide_border": 0, "hide_days": 0, @@ -2941,18 +2996,18 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "posa_referral_section", + "insert_after": "posa_referral_code", "is_system_generated": 0, "is_virtual": 0, - "label": "Referral Code", + "label": "Referral Company", "length": 0, "mandatory_depends_on": null, - "modified": "2021-07-29 22:42:57.772021", + "modified": "2021-07-29 23:24:11.207034", "module": null, - "name": "Customer-posa_referral_code", + "name": "Customer-posa_referral_company", "no_copy": 1, "non_negative": 0, - "options": null, + "options": "Company", "permlevel": 0, "precision": "", "print_hide": 0, @@ -2964,7 +3019,7 @@ "reqd": 0, "search_index": 0, "sort_options": 0, - "translatable": 1, + "translatable": 0, "unique": 0, "width": null }, @@ -2980,11 +3035,11 @@ "description": null, "docstatus": 0, "doctype": "Custom Field", - "dt": "POS Profile", + "dt": "Sales Invoice Item", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_display_additional_notes", - "fieldtype": "Check", + "fieldname": "posa_offers", + "fieldtype": "Small Text", "hidden": 0, "hide_border": 0, "hide_days": 0, @@ -2995,15 +3050,15 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "posa_allow_print_last_invoice", + "insert_after": "pricing_rules", "is_system_generated": 0, "is_virtual": 0, - "label": "Display Additional Notes", + "label": "POS Offers", "length": 0, "mandatory_depends_on": null, - "modified": "2021-12-19 16:54:32.986600", + "modified": "2021-06-07 01:51:16.390447", "module": null, - "name": "POS Profile-posa_display_additional_notes", + "name": "Sales Invoice Item-posa_offers", "no_copy": 0, "non_negative": 0, "options": null, @@ -3012,33 +3067,33 @@ "print_hide": 0, "print_hide_if_no_value": 0, "print_width": null, - "read_only": 0, + "read_only": 1, "read_only_depends_on": null, "report_hide": 0, "reqd": 0, "search_index": 0, "sort_options": 0, - "translatable": 0, + "translatable": 1, "unique": 0, "width": null }, { - "allow_in_quick_entry": 1, + "allow_in_quick_entry": 0, "allow_on_submit": 0, - "bold": 1, + "bold": 0, "collapsible": 0, "collapsible_depends_on": null, "columns": 0, - "default": null, + "default": "0", "depends_on": null, "description": null, "docstatus": 0, "doctype": "Custom Field", - "dt": "Customer", + "dt": "POS Profile", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_referral_company", - "fieldtype": "Link", + "fieldname": "posa_allow_print_last_invoice", + "fieldtype": "Check", "hidden": 0, "hide_border": 0, "hide_days": 0, @@ -3049,18 +3104,18 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "posa_referral_code", + "insert_after": "posa_allow_customer_purchase_order", "is_system_generated": 0, "is_virtual": 0, - "label": "Referral Company", + "label": "Allow Print Last Invoice", "length": 0, "mandatory_depends_on": null, - "modified": "2021-07-29 23:24:11.207034", + "modified": "2021-12-16 18:00:40.631156", "module": null, - "name": "Customer-posa_referral_company", - "no_copy": 1, + "name": "POS Profile-posa_allow_print_last_invoice", + "no_copy": 0, "non_negative": 0, - "options": "Company", + "options": null, "permlevel": 0, "precision": "", "print_hide": 0, @@ -3088,10 +3143,10 @@ "description": null, "docstatus": 0, "doctype": "Custom Field", - "dt": "POS Profile", + "dt": "Sales Invoice Item", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_allow_write_off_change", + "fieldname": "posa_offer_applied", "fieldtype": "Check", "hidden": 0, "hide_border": 0, @@ -3103,15 +3158,15 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "posa_display_additional_notes", + "insert_after": "posa_offers", "is_system_generated": 0, "is_virtual": 0, - "label": "Allow Write Off Change", + "label": "Offer Applied", "length": 0, "mandatory_depends_on": null, - "modified": "2022-02-12 04:26:04.003374", + "modified": "2021-06-12 00:12:28.473489", "module": null, - "name": "POS Profile-posa_allow_write_off_change", + "name": "Sales Invoice Item-posa_offer_applied", "no_copy": 0, "non_negative": 0, "options": null, @@ -3120,7 +3175,7 @@ "print_hide": 0, "print_hide_if_no_value": 0, "print_width": null, - "read_only": 0, + "read_only": 1, "read_only_depends_on": null, "report_hide": 0, "reqd": 0, @@ -3142,11 +3197,11 @@ "description": null, "docstatus": 0, "doctype": "Custom Field", - "dt": "Sales Invoice Item", + "dt": "POS Profile", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_offers", - "fieldtype": "Small Text", + "fieldname": "posa_display_additional_notes", + "fieldtype": "Check", "hidden": 0, "hide_border": 0, "hide_days": 0, @@ -3157,15 +3212,15 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "pricing_rules", + "insert_after": "posa_allow_print_last_invoice", "is_system_generated": 0, "is_virtual": 0, - "label": "POS Offers", + "label": "Display Additional Notes", "length": 0, "mandatory_depends_on": null, - "modified": "2021-06-07 01:51:16.390447", + "modified": "2021-12-19 16:54:32.986600", "module": null, - "name": "Sales Invoice Item-posa_offers", + "name": "POS Profile-posa_display_additional_notes", "no_copy": 0, "non_negative": 0, "options": null, @@ -3174,13 +3229,13 @@ "print_hide": 0, "print_hide_if_no_value": 0, "print_width": null, - "read_only": 1, + "read_only": 0, "read_only_depends_on": null, "report_hide": 0, "reqd": 0, "search_index": 0, "sort_options": 0, - "translatable": 1, + "translatable": 0, "unique": 0, "width": null }, @@ -3191,15 +3246,15 @@ "collapsible": 0, "collapsible_depends_on": null, "columns": 0, - "default": "0", + "default": null, "depends_on": null, "description": null, "docstatus": 0, "doctype": "Custom Field", - "dt": "POS Profile", + "dt": "Sales Invoice Item", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_new_line", + "fieldname": "posa_is_offer", "fieldtype": "Check", "hidden": 0, "hide_border": 0, @@ -3208,18 +3263,18 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_global_search": 0, - "in_list_view": 0, + "in_list_view": 1, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "posa_allow_write_off_change", + "insert_after": "posa_offer_applied", "is_system_generated": 0, "is_virtual": 0, - "label": "Allow add New Items on New Line", + "label": "Is Offer", "length": 0, "mandatory_depends_on": null, - "modified": "2022-05-17 01:01:52.106645", + "modified": "2021-06-12 00:14:20.894553", "module": null, - "name": "POS Profile-posa_new_line", + "name": "Sales Invoice Item-posa_is_offer", "no_copy": 0, "non_negative": 0, "options": null, @@ -3228,7 +3283,7 @@ "print_hide": 0, "print_hide_if_no_value": 0, "print_width": null, - "read_only": 0, + "read_only": 1, "read_only_depends_on": null, "report_hide": 0, "reqd": 0, @@ -3250,10 +3305,10 @@ "description": null, "docstatus": 0, "doctype": "Custom Field", - "dt": "Sales Invoice Item", + "dt": "POS Profile", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_offer_applied", + "fieldname": "posa_allow_write_off_change", "fieldtype": "Check", "hidden": 0, "hide_border": 0, @@ -3265,15 +3320,15 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "posa_offers", + "insert_after": "posa_display_additional_notes", "is_system_generated": 0, "is_virtual": 0, - "label": "Offer Applied", + "label": "Allow Write Off Change", "length": 0, "mandatory_depends_on": null, - "modified": "2021-06-12 00:12:28.473489", + "modified": "2022-02-12 04:26:04.003374", "module": null, - "name": "Sales Invoice Item-posa_offer_applied", + "name": "POS Profile-posa_allow_write_off_change", "no_copy": 0, "non_negative": 0, "options": null, @@ -3282,7 +3337,7 @@ "print_hide": 0, "print_hide_if_no_value": 0, "print_width": null, - "read_only": 1, + "read_only": 0, "read_only_depends_on": null, "report_hide": 0, "reqd": 0, @@ -3299,16 +3354,16 @@ "collapsible": 0, "collapsible_depends_on": null, "columns": 0, - "default": "0", - "depends_on": null, + "default": null, + "depends_on": "", "description": null, "docstatus": 0, "doctype": "Custom Field", - "dt": "POS Profile", + "dt": "Sales Invoice Item", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_input_qty", - "fieldtype": "Check", + "fieldname": "posa_is_replace", + "fieldtype": "Data", "hidden": 0, "hide_border": 0, "hide_days": 0, @@ -3319,15 +3374,15 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "posa_new_line", + "insert_after": "posa_is_offer", "is_system_generated": 0, "is_virtual": 0, - "label": "Use QTY Input", + "label": "Is Offer Replace For item Row ID", "length": 0, "mandatory_depends_on": null, - "modified": "2022-05-17 02:17:13.591004", + "modified": "2021-06-17 18:10:36.233226", "module": null, - "name": "POS Profile-posa_input_qty", + "name": "Sales Invoice Item-posa_is_replace", "no_copy": 0, "non_negative": 0, "options": null, @@ -3336,13 +3391,13 @@ "print_hide": 0, "print_hide_if_no_value": 0, "print_width": null, - "read_only": 0, + "read_only": 1, "read_only_depends_on": null, "report_hide": 0, "reqd": 0, "search_index": 0, "sort_options": 0, - "translatable": 0, + "translatable": 1, "unique": 0, "width": null }, @@ -3353,15 +3408,15 @@ "collapsible": 0, "collapsible_depends_on": null, "columns": 0, - "default": null, + "default": "0", "depends_on": null, "description": null, "docstatus": 0, "doctype": "Custom Field", - "dt": "Sales Invoice Item", + "dt": "POS Profile", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_is_offer", + "fieldname": "posa_new_line", "fieldtype": "Check", "hidden": 0, "hide_border": 0, @@ -3370,18 +3425,18 @@ "ignore_user_permissions": 0, "ignore_xss_filter": 0, "in_global_search": 0, - "in_list_view": 1, + "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "posa_offer_applied", + "insert_after": "posa_allow_write_off_change", "is_system_generated": 0, "is_virtual": 0, - "label": "Is Offer", + "label": "Allow add New Items on New Line", "length": 0, "mandatory_depends_on": null, - "modified": "2021-06-12 00:14:20.894553", + "modified": "2022-05-17 01:01:52.106645", "module": null, - "name": "Sales Invoice Item-posa_is_offer", + "name": "POS Profile-posa_new_line", "no_copy": 0, "non_negative": 0, "options": null, @@ -3390,7 +3445,7 @@ "print_hide": 0, "print_hide_if_no_value": 0, "print_width": null, - "read_only": 1, + "read_only": 0, "read_only_depends_on": null, "report_hide": 0, "reqd": 0, @@ -3407,7 +3462,7 @@ "collapsible": 0, "collapsible_depends_on": null, "columns": 0, - "default": null, + "default": "0", "depends_on": null, "description": null, "docstatus": 0, @@ -3415,7 +3470,7 @@ "dt": "POS Profile", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_allow_print_draft_invoices", + "fieldname": "posa_input_qty", "fieldtype": "Check", "hidden": 0, "hide_border": 0, @@ -3427,15 +3482,15 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "posa_input_qty", + "insert_after": "posa_new_line", "is_system_generated": 0, "is_virtual": 0, - "label": "Allow Print Draft Invoices", + "label": "Use QTY Input", "length": 0, "mandatory_depends_on": null, - "modified": "2022-07-22 11:51:07.782265", + "modified": "2022-05-17 02:17:13.591004", "module": null, - "name": "POS Profile-posa_allow_print_draft_invoices", + "name": "POS Profile-posa_input_qty", "no_copy": 0, "non_negative": 0, "options": null, @@ -3462,15 +3517,15 @@ "collapsible_depends_on": null, "columns": 0, "default": null, - "depends_on": "", + "depends_on": null, "description": null, "docstatus": 0, "doctype": "Custom Field", - "dt": "Sales Invoice Item", + "dt": "POS Profile", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_is_replace", - "fieldtype": "Data", + "fieldname": "posa_allow_print_draft_invoices", + "fieldtype": "Check", "hidden": 0, "hide_border": 0, "hide_days": 0, @@ -3481,15 +3536,15 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "posa_is_offer", + "insert_after": "posa_input_qty", "is_system_generated": 0, "is_virtual": 0, - "label": "Is Offer Replace For item Row ID", + "label": "Allow Print Draft Invoices", "length": 0, "mandatory_depends_on": null, - "modified": "2021-06-17 18:10:36.233226", + "modified": "2022-07-22 11:51:07.782265", "module": null, - "name": "Sales Invoice Item-posa_is_replace", + "name": "POS Profile-posa_allow_print_draft_invoices", "no_copy": 0, "non_negative": 0, "options": null, @@ -3498,13 +3553,13 @@ "print_hide": 0, "print_hide_if_no_value": 0, "print_width": null, - "read_only": 1, + "read_only": 0, "read_only_depends_on": null, "report_hide": 0, "reqd": 0, "search_index": 0, "sort_options": 0, - "translatable": 1, + "translatable": 0, "unique": 0, "width": null }, @@ -4168,11 +4223,11 @@ "description": null, "docstatus": 0, "doctype": "Custom Field", - "dt": "POS Profile", - "fetch_from": null, - "fetch_if_empty": 0, - "fieldname": "posa_search_batch_no", - "fieldtype": "Check", + "dt": "Sales Invoice", + "fetch_from": "shipping_address_name.posa_delivery_charges", + "fetch_if_empty": 1, + "fieldname": "posa_delivery_charges", + "fieldtype": "Link", "hidden": 0, "hide_border": 0, "hide_days": 0, @@ -4183,18 +4238,18 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "posa_search_serial_no", + "insert_after": "taxes_and_charges", "is_system_generated": 0, "is_virtual": 0, - "label": "Search by Batch Number", + "label": "Delivery Charges", "length": 0, "mandatory_depends_on": null, - "modified": "2023-06-05 23:50:13.126933", + "modified": "2022-07-25 11:49:02.312879", "module": null, - "name": "POS Profile-posa_search_batch_no", + "name": "Sales Invoice-posa_delivery_charges", "no_copy": 0, "non_negative": 0, - "options": null, + "options": "Delivery Charges", "permlevel": 0, "precision": "", "print_hide": 0, @@ -4222,11 +4277,11 @@ "description": null, "docstatus": 0, "doctype": "Custom Field", - "dt": "Sales Invoice", - "fetch_from": "shipping_address_name.posa_delivery_charges", - "fetch_if_empty": 1, - "fieldname": "posa_delivery_charges", - "fieldtype": "Link", + "dt": "POS Profile", + "fetch_from": null, + "fetch_if_empty": 0, + "fieldname": "posa_search_batch_no", + "fieldtype": "Check", "hidden": 0, "hide_border": 0, "hide_days": 0, @@ -4237,18 +4292,18 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "taxes_and_charges", + "insert_after": "posa_search_serial_no", "is_system_generated": 0, "is_virtual": 0, - "label": "Delivery Charges", + "label": "Search by Batch Number", "length": 0, "mandatory_depends_on": null, - "modified": "2022-07-25 11:49:02.312879", + "modified": "2023-06-05 23:50:13.126933", "module": null, - "name": "Sales Invoice-posa_delivery_charges", + "name": "POS Profile-posa_search_batch_no", "no_copy": 0, "non_negative": 0, - "options": "Delivery Charges", + "options": null, "permlevel": 0, "precision": "", "print_hide": 0, @@ -4271,17 +4326,17 @@ "collapsible": 0, "collapsible_depends_on": null, "columns": 0, - "default": "1", + "default": null, "depends_on": null, "description": null, "docstatus": 0, "doctype": "Custom Field", - "dt": "POS Profile", + "dt": "Sales Invoice", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_tax_inclusive", - "fieldtype": "Check", - "hidden": 0, + "fieldname": "posa_delivery_charges_rate", + "fieldtype": "Currency", + "hidden": 1, "hide_border": 0, "hide_days": 0, "hide_seconds": 0, @@ -4291,24 +4346,24 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "posa_search_batch_no", + "insert_after": "posa_delivery_charges", "is_system_generated": 0, "is_virtual": 0, - "label": "Tax Inclusive", + "label": "Delivery Charges Rate", "length": 0, "mandatory_depends_on": null, - "modified": "2021-09-06 16:33:33.398280", + "modified": "2022-07-25 11:49:04.797031", "module": null, - "name": "POS Profile-posa_tax_inclusive", + "name": "Sales Invoice-posa_delivery_charges_rate", "no_copy": 0, "non_negative": 0, - "options": null, + "options": "currency", "permlevel": 0, "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, "print_width": null, - "read_only": 0, + "read_only": 1, "read_only_depends_on": null, "report_hide": 0, "reqd": 0, @@ -4325,17 +4380,17 @@ "collapsible": 0, "collapsible_depends_on": null, "columns": 0, - "default": null, + "default": "1", "depends_on": null, "description": null, "docstatus": 0, "doctype": "Custom Field", - "dt": "Sales Invoice", + "dt": "POS Profile", "fetch_from": null, "fetch_if_empty": 0, - "fieldname": "posa_delivery_charges_rate", - "fieldtype": "Currency", - "hidden": 1, + "fieldname": "posa_tax_inclusive", + "fieldtype": "Check", + "hidden": 0, "hide_border": 0, "hide_days": 0, "hide_seconds": 0, @@ -4345,24 +4400,24 @@ "in_list_view": 0, "in_preview": 0, "in_standard_filter": 0, - "insert_after": "posa_delivery_charges", + "insert_after": "posa_search_batch_no", "is_system_generated": 0, "is_virtual": 0, - "label": "Delivery Charges Rate", + "label": "Tax Inclusive", "length": 0, "mandatory_depends_on": null, - "modified": "2022-07-25 11:49:04.797031", + "modified": "2021-09-06 16:33:33.398280", "module": null, - "name": "Sales Invoice-posa_delivery_charges_rate", + "name": "POS Profile-posa_tax_inclusive", "no_copy": 0, "non_negative": 0, - "options": "currency", + "options": null, "permlevel": 0, "precision": "", "print_hide": 0, "print_hide_if_no_value": 0, "print_width": null, - "read_only": 1, + "read_only": 0, "read_only_depends_on": null, "report_hide": 0, "reqd": 0, diff --git a/posawesome/hooks.py b/posawesome/hooks.py index 75158395d..daeac3f0d 100644 --- a/posawesome/hooks.py +++ b/posawesome/hooks.py @@ -257,6 +257,7 @@ "POS Profile-posa_allow_reconcile_payments", "POS Profile-column_break_uolvm", "POS Profile-posa_allow_mpesa_reconcile_payments", + "POS Profile-custom_pezesha_channel_id", ), ] ], @@ -265,4 +266,10 @@ "doctype": "Property Setter", "filters": [["name", "in", ("Sales Invoice-posa_pos_opening_shift-no_copy")]], }, + ] +scheduler_events = { + "daily": [ + "posawesome.posawesome.doctype.pezesha_settings.pezesha_settings.corn" + ] +} diff --git a/posawesome/posawesome/api/payment_entry.py b/posawesome/posawesome/api/payment_entry.py index c7472615e..648bc149e 100644 --- a/posawesome/posawesome/api/payment_entry.py +++ b/posawesome/posawesome/api/payment_entry.py @@ -15,6 +15,75 @@ from erpnext.accounts.utils import QueryPaymentLedger, get_outstanding_invoices as _get_outstanding_invoices +# def create_payment_entry( +# company, +# customer, +# amount, +# currency, +# mode_of_payment, +# reference_date=None, +# reference_no=None, +# posting_date=None, +# cost_center=None, +# submit=0, +# ): +# # TODO : need to have a better way to handle currency +# date = nowdate() if not posting_date else posting_date +# party_type = "Customer" +# party_account = get_party_account(party_type, customer, company) +# party_account_currency = get_account_currency(party_account) +# if party_account_currency != currency: +# frappe.throw( +# _( +# "Currency is not correct, party account currency is {party_account_currency} and transaction currency is {currency}" +# ).format(party_account_currency=party_account_currency, currency=currency) +# ) +# payment_type = "Receive" + +# bank = get_bank_cash_account(company, mode_of_payment) +# company_currency = frappe.get_value("Company", company, "default_currency") +# conversion_rate = get_exchange_rate(currency, company_currency, date, "for_selling") +# paid_amount, received_amount = set_paid_amount_and_received_amount( +# party_account_currency, bank, amount, payment_type, None, conversion_rate +# ) + +# pe = frappe.new_doc("Payment Entry") +# pe.payment_type = payment_type +# pe.company = company +# pe.cost_center = cost_center or erpnext.get_default_cost_center(company) +# pe.posting_date = date +# pe.mode_of_payment = mode_of_payment +# pe.party_type = party_type +# pe.party = customer + +# pe.paid_from = party_account if payment_type == "Receive" else bank.account +# pe.paid_to = party_account if payment_type == "Pay" else bank.account +# pe.paid_from_account_currency = ( +# party_account_currency if payment_type == "Receive" else bank.account_currency +# ) +# pe.paid_to_account_currency = ( +# party_account_currency if payment_type == "Pay" else bank.account_currency +# ) +# pe.paid_amount = paid_amount +# pe.received_amount = received_amount +# pe.letter_head = frappe.get_value("Company", company, "default_letter_head") +# pe.reference_date = reference_date +# pe.reference_no = reference_no +# if pe.party_type in ["Customer", "Supplier"]: +# bank_account = get_party_bank_account(pe.party_type, pe.party) +# pe.set("bank_account", bank_account) +# pe.set_bank_account_data() + +# pe.setup_party_account_field() +# pe.set_missing_values() + +# if party_account and bank: +# pe.set_amounts() +# if submit: +# pe.docstatus = 1 +# pe.insert(ignore_permissions=True) +# return pe + def create_payment_entry( company, customer, @@ -27,22 +96,34 @@ def create_payment_entry( cost_center=None, submit=0, ): - # TODO : need to have a better way to handle currency - date = nowdate() if not posting_date else posting_date + # Use local variables to minimize repeated DB calls + date = posting_date or nowdate() party_type = "Customer" + + # Fetch party account and currency in one go party_account = get_party_account(party_type, customer, company) party_account_currency = get_account_currency(party_account) if party_account_currency != currency: frappe.throw( - _( - "Currency is not correct, party account currency is {party_account_currency} and transaction currency is {currency}" - ).format(party_account_currency=party_account_currency, currency=currency) + _("Currency is not correct, party account currency is {party_account_currency} and transaction currency is {currency}") + .format(party_account_currency=party_account_currency, currency=currency) ) + payment_type = "Receive" + # Get bank/cash account only once bank = get_bank_cash_account(company, mode_of_payment) - company_currency = frappe.get_value("Company", company, "default_currency") + if not bank: + frappe.throw(_("No Bank or Cash account found for mode of payment {0}").format(mode_of_payment)) + + # Fetch company values in a single call + company_doc = frappe.get_cached_doc("Company", company) + company_currency = company_doc.default_currency + letter_head = company_doc.default_letter_head + + # Use cached exchange rate if possible conversion_rate = get_exchange_rate(currency, company_currency, date, "for_selling") + paid_amount, received_amount = set_paid_amount_and_received_amount( party_account_currency, bank, amount, payment_type, None, conversion_rate ) @@ -66,14 +147,18 @@ def create_payment_entry( ) pe.paid_amount = paid_amount pe.received_amount = received_amount - pe.letter_head = frappe.get_value("Company", company, "default_letter_head") + pe.letter_head = letter_head pe.reference_date = reference_date pe.reference_no = reference_no - if pe.party_type in ["Customer", "Supplier"]: + + # Only fetch bank account if needed + if pe.party_type in ("Customer", "Supplier"): bank_account = get_party_bank_account(pe.party_type, pe.party) - pe.set("bank_account", bank_account) - pe.set_bank_account_data() + if bank_account: + pe.set("bank_account", bank_account) + pe.set_bank_account_data() + # Minimize method calls pe.setup_party_account_field() pe.set_missing_values() @@ -81,10 +166,11 @@ def create_payment_entry( pe.set_amounts() if submit: pe.docstatus = 1 + + # Use insert with minimal overhead pe.insert(ignore_permissions=True) return pe - def get_bank_cash_account(company, mode_of_payment, bank_account=None): bank = get_default_bank_cash_account( company, "Bank", mode_of_payment=mode_of_payment, account=bank_account @@ -222,6 +308,201 @@ def get_unallocated_payments(customer, company, currency, mode_of_payment=None): return unallocated_payment +# @frappe.whitelist() +# def process_pos_payment(payload): +# data = json.loads(payload) +# data = frappe._dict(data) +# if not data.pos_profile.get("posa_use_pos_awesome_payments"): +# frappe.throw(_("POS Awesome Payments is not enabled for this POS Profile")) + +# # validate data +# if not data.customer: +# frappe.throw(_("Customer is required")) +# if not data.company: +# frappe.throw(_("Company is required")) +# if not data.currency: +# frappe.throw(_("Currency is required")) +# if not data.pos_profile_name: +# frappe.throw(_("POS Profile is required")) +# if not data.pos_opening_shift_name: +# frappe.throw(_("POS Opening Shift is required")) + +# company = data.company +# currency = data.currency +# customer = data.customer +# pos_opening_shift_name = data.pos_opening_shift_name +# allow_make_new_payments = data.pos_profile.get("posa_allow_make_new_payments") +# allow_reconcile_payments = data.pos_profile.get("posa_allow_reconcile_payments") +# allow_mpesa_reconcile_payments = data.pos_profile.get( +# "posa_allow_mpesa_reconcile_payments" +# ) +# today = nowdate() + +# new_payments_entry = [] +# all_payments_entry = [] +# errors = [] +# reconcile_doc = None + +# # first process mpesa payments +# if ( +# allow_mpesa_reconcile_payments +# and len(data.selected_mpesa_payments) > 0 +# and data.total_selected_mpesa_payments > 0 +# ): +# for mpesa_payment in data.selected_mpesa_payments: +# try: +# new_mpesa_payment = submit_mpesa_payment( +# mpesa_payment.get("name"), customer +# ) +# new_payments_entry.append(new_mpesa_payment) +# all_payments_entry.append(new_mpesa_payment) +# except Exception as e: +# errors.append(e) + +# # then process the new payments +# if ( +# allow_make_new_payments +# and len(data.payment_methods) > 0 +# and data.total_payment_methods > 0 +# ): +# for payment_method in data.payment_methods: +# try: +# if not payment_method.get("amount"): +# continue +# new_payment_entry = create_payment_entry( +# company=company, +# customer=customer, +# currency=currency, +# amount=flt(payment_method.get("amount")), +# mode_of_payment=payment_method.get("mode_of_payment"), +# posting_date=today, +# reference_no=pos_opening_shift_name, +# reference_date=today, +# cost_center=data.pos_profile.get("cost_center"), +# submit=1, +# ) +# new_payments_entry.append(new_payment_entry) +# all_payments_entry.append(new_payment_entry) +# except Exception as e: +# errors.append(e) + +# # then then reconcile the new payments and the unallocated payments with the outstanding invoices +# if len(data.selected_invoices) > 0 and data.total_selected_invoices > 0: +# if ( +# allow_reconcile_payments +# and len(data.selected_payments) > 0 +# and data.total_selected_payments > 0 +# ): +# # add the unallocated payments to the all payments entry +# for selected_payment in data.selected_payments: +# all_payments_entry.append(selected_payment) + +# if len(all_payments_entry) > 0: +# # sort the all payments entry by posting date +# all_payments_entry = sorted( +# all_payments_entry, +# key=lambda k: getdate(str(k.get("posting_date"))), +# reverse=True, +# ) +# all_invoices_list = sorted( +# data.selected_invoices, +# key=lambda k: getdate(k.get("posting_date")), +# reverse=True, +# ) +# reconcile_doc = frappe.new_doc("Payment Reconciliation") +# reconcile_doc.party_type = "Customer" +# reconcile_doc.party = customer +# reconcile_doc.company = company +# reconcile_doc.receivable_payable_account = get_party_account( +# "Customer", customer, company +# ) +# reconcile_doc.get_unreconciled_entries() +# args = { +# "invoices": [], +# "payments": [], +# } +# for invoice in all_invoices_list: +# args["invoices"].append( +# { +# "invoice_type": "Sales Invoice", +# "invoice_number": invoice.get("name"), +# "invoice_date": invoice.get("posting_date"), +# "amount": invoice.get("grand_total"), +# "outstanding_amount": invoice.get("outstanding_amount"), +# "currency": invoice.get("currency"), +# "exchange_rate": 0, +# } +# ) +# for payment in all_payments_entry: +# args["payments"].append( +# { +# "reference_type": "Payment Entry", +# "reference_name": payment.get("name"), +# "posting_date": payment.get("posting_date"), +# "amount": payment.get("unallocated_amount"), +# "unallocated_amount": payment.get("unallocated_amount"), +# "difference_amount": 0, +# "currency": payment.get("currency"), +# "exchange_rate": 0, +# } +# ) +# reconcile_doc.allocate_entries(args) +# reconcile_doc.reconcile() + +# # then show the results +# msg = "" +# if len(new_payments_entry) > 0: +# msg += "

New Payments

" +# msg += "" +# msg += "" +# msg += "" +# for payment_entry in new_payments_entry: +# msg += "".format( +# payment_entry.get("name"), payment_entry.get("unallocated_amount") +# ) +# msg += "" +# msg += "
Payment EntryAmount
{0}{1}
" +# if len(all_payments_entry) > 0 and len(data.selected_invoices) > 0: +# msg += "

Reconciled Payments

" +# msg += "" +# msg += "" +# msg += "" +# for payment_entry in all_payments_entry: +# msg += "".format( +# payment_entry.get("name"), payment_entry.get("unallocated_amount") +# ) +# msg += "" +# msg += "
Payment EntryAmount
{0}{1}
" +# if len(data.selected_invoices) > 0 and data.total_selected_invoices > 0: +# msg += "

Reconciled Invoices

" +# msg += "" +# msg += "" +# msg += "" +# for invoice in data.selected_invoices: +# msg += "".format( +# invoice.get("name"), invoice.get("outstanding_amount") +# ) +# msg += "" +# msg += "
InvoiceAmount
{0}{1}
" +# if len(errors) > 0: +# msg += "

Errors

" +# msg += "" +# msg += "" +# msg += "" +# for error in errors: +# msg += "".format(error) +# msg += "" +# msg += "
Error
{0}
" +# if len(msg) > 0: +# frappe.msgprint(msg) + +# return { +# "new_payments_entry": new_payments_entry, +# "all_payments_entry": all_payments_entry, +# "errors": errors, +# "reconcile_doc": reconcile_doc, +# } + @frappe.whitelist() def process_pos_payment(payload): data = json.loads(payload) @@ -229,27 +510,26 @@ def process_pos_payment(payload): if not data.pos_profile.get("posa_use_pos_awesome_payments"): frappe.throw(_("POS Awesome Payments is not enabled for this POS Profile")) - # validate data - if not data.customer: - frappe.throw(_("Customer is required")) - if not data.company: - frappe.throw(_("Company is required")) - if not data.currency: - frappe.throw(_("Currency is required")) - if not data.pos_profile_name: - frappe.throw(_("POS Profile is required")) - if not data.pos_opening_shift_name: - frappe.throw(_("POS Opening Shift is required")) + # Validate required fields early + required_fields = [ + ("customer", _("Customer is required")), + ("company", _("Company is required")), + ("currency", _("Currency is required")), + ("pos_profile_name", _("POS Profile is required")), + ("pos_opening_shift_name", _("POS Opening Shift is required")), + ] + for field, error_msg in required_fields: + if not data.get(field): + frappe.throw(error_msg) company = data.company currency = data.currency customer = data.customer pos_opening_shift_name = data.pos_opening_shift_name - allow_make_new_payments = data.pos_profile.get("posa_allow_make_new_payments") - allow_reconcile_payments = data.pos_profile.get("posa_allow_reconcile_payments") - allow_mpesa_reconcile_payments = data.pos_profile.get( - "posa_allow_mpesa_reconcile_payments" - ) + pos_profile = data.pos_profile + allow_make_new_payments = pos_profile.get("posa_allow_make_new_payments") + allow_reconcile_payments = pos_profile.get("posa_allow_reconcile_payments") + allow_mpesa_reconcile_payments = pos_profile.get("posa_allow_mpesa_reconcile_payments") today = nowdate() new_payments_entry = [] @@ -257,86 +537,67 @@ def process_pos_payment(payload): errors = [] reconcile_doc = None - # first process mpesa payments - if ( - allow_mpesa_reconcile_payments - and len(data.selected_mpesa_payments) > 0 - and data.total_selected_mpesa_payments > 0 - ): - for mpesa_payment in data.selected_mpesa_payments: + # --- 1. Batch process MPESA payments --- + mpesa_payments = data.get("selected_mpesa_payments") or [] + if allow_mpesa_reconcile_payments and mpesa_payments and data.get("total_selected_mpesa_payments", 0) > 0: + for mpesa_payment in mpesa_payments: try: - new_mpesa_payment = submit_mpesa_payment( - mpesa_payment.get("name"), customer - ) + new_mpesa_payment = submit_mpesa_payment(mpesa_payment.get("name"), customer) new_payments_entry.append(new_mpesa_payment) all_payments_entry.append(new_mpesa_payment) except Exception as e: - errors.append(e) - - # then process the new payments - if ( - allow_make_new_payments - and len(data.payment_methods) > 0 - and data.total_payment_methods > 0 - ): - for payment_method in data.payment_methods: + errors.append(str(e)) + + # --- 2. Batch process new payments --- + payment_methods = data.get("payment_methods") or [] + if allow_make_new_payments and payment_methods and data.get("total_payment_methods", 0) > 0: + for payment_method in payment_methods: try: - if not payment_method.get("amount"): + amount = flt(payment_method.get("amount")) + if not amount: continue new_payment_entry = create_payment_entry( company=company, customer=customer, currency=currency, - amount=flt(payment_method.get("amount")), + amount=amount, mode_of_payment=payment_method.get("mode_of_payment"), posting_date=today, reference_no=pos_opening_shift_name, reference_date=today, - cost_center=data.pos_profile.get("cost_center"), + cost_center=pos_profile.get("cost_center"), submit=1, ) new_payments_entry.append(new_payment_entry) all_payments_entry.append(new_payment_entry) except Exception as e: - errors.append(e) - - # then then reconcile the new payments and the unallocated payments with the outstanding invoices - if len(data.selected_invoices) > 0 and data.total_selected_invoices > 0: - if ( - allow_reconcile_payments - and len(data.selected_payments) > 0 - and data.total_selected_payments > 0 - ): - # add the unallocated payments to the all payments entry - for selected_payment in data.selected_payments: - all_payments_entry.append(selected_payment) - - if len(all_payments_entry) > 0: - # sort the all payments entry by posting date - all_payments_entry = sorted( - all_payments_entry, - key=lambda k: getdate(str(k.get("posting_date"))), - reverse=True, - ) - all_invoices_list = sorted( - data.selected_invoices, - key=lambda k: getdate(k.get("posting_date")), - reverse=True, - ) + errors.append(str(e)) + + # --- 3. Reconcile payments and invoices --- + selected_invoices = data.get("selected_invoices") or [] + if selected_invoices and data.get("total_selected_invoices", 0) > 0: + # Add unallocated payments if allowed + if allow_reconcile_payments: + selected_payments = data.get("selected_payments") or [] + if selected_payments and data.get("total_selected_payments", 0) > 0: + all_payments_entry.extend(selected_payments) + + if all_payments_entry: + # Sort only if more than 1 entry (avoid unnecessary sort) + if len(all_payments_entry) > 1: + all_payments_entry.sort(key=lambda k: getdate(str(k.get("posting_date"))), reverse=True) + if len(selected_invoices) > 1: + selected_invoices.sort(key=lambda k: getdate(k.get("posting_date")), reverse=True) + reconcile_doc = frappe.new_doc("Payment Reconciliation") reconcile_doc.party_type = "Customer" reconcile_doc.party = customer reconcile_doc.company = company - reconcile_doc.receivable_payable_account = get_party_account( - "Customer", customer, company - ) + reconcile_doc.receivable_payable_account = get_party_account("Customer", customer, company) reconcile_doc.get_unreconciled_entries() + args = { - "invoices": [], - "payments": [], - } - for invoice in all_invoices_list: - args["invoices"].append( + "invoices": [ { "invoice_type": "Sales Invoice", "invoice_number": invoice.get("name"), @@ -346,9 +607,9 @@ def process_pos_payment(payload): "currency": invoice.get("currency"), "exchange_rate": 0, } - ) - for payment in all_payments_entry: - args["payments"].append( + for invoice in selected_invoices + ], + "payments": [ { "reference_type": "Payment Entry", "reference_name": payment.get("name"), @@ -359,56 +620,49 @@ def process_pos_payment(payload): "currency": payment.get("currency"), "exchange_rate": 0, } - ) + for payment in all_payments_entry + ], + } reconcile_doc.allocate_entries(args) reconcile_doc.reconcile() - # then show the results - msg = "" - if len(new_payments_entry) > 0: - msg += "

New Payments

" - msg += "" - msg += "" - msg += "" - for payment_entry in new_payments_entry: - msg += "".format( - payment_entry.get("name"), payment_entry.get("unallocated_amount") - ) - msg += "" - msg += "
Payment EntryAmount
{0}{1}
" - if len(all_payments_entry) > 0 and len(data.selected_invoices) > 0: - msg += "

Reconciled Payments

" - msg += "" - msg += "" - msg += "" - for payment_entry in all_payments_entry: - msg += "".format( - payment_entry.get("name"), payment_entry.get("unallocated_amount") - ) - msg += "" - msg += "
Payment EntryAmount
{0}{1}
" - if len(data.selected_invoices) > 0 and data.total_selected_invoices > 0: - msg += "

Reconciled Invoices

" - msg += "" - msg += "" - msg += "" - for invoice in data.selected_invoices: - msg += "".format( - invoice.get("name"), invoice.get("outstanding_amount") - ) - msg += "" - msg += "
InvoiceAmount
{0}{1}
" - if len(errors) > 0: - msg += "

Errors

" - msg += "" - msg += "" - msg += "" - for error in errors: - msg += "".format(error) - msg += "" - msg += "
Error
{0}
" - if len(msg) > 0: - frappe.msgprint(msg) + # --- 4. Build HTML message efficiently --- + def build_table(title, headers, rows): + html = f"

{title}

" + html += "".join(f"" for h in headers) + html += "" + for row in rows: + html += "" + "".join(f"" for c in row) + "" + html += "
{h}
{c}
" + return html + + msg_parts = [] + if new_payments_entry: + msg_parts.append(build_table( + "New Payments", + ["Payment Entry", "Amount"], + [(pe.get("name"), pe.get("unallocated_amount")) for pe in new_payments_entry] + )) + if all_payments_entry and selected_invoices: + msg_parts.append(build_table( + "Reconciled Payments", + ["Payment Entry", "Amount"], + [(pe.get("name"), pe.get("unallocated_amount")) for pe in all_payments_entry] + )) + if selected_invoices and data.get("total_selected_invoices", 0) > 0: + msg_parts.append(build_table( + "Reconciled Invoices", + ["Invoice", "Amount"], + [(inv.get("name"), inv.get("outstanding_amount")) for inv in selected_invoices] + )) + if errors: + msg_parts.append(build_table( + "Errors", + ["Error"], + [(e,) for e in errors] + )) + if msg_parts: + frappe.msgprint("".join(msg_parts)) return { "new_payments_entry": new_payments_entry, @@ -417,7 +671,6 @@ def process_pos_payment(payload): "reconcile_doc": reconcile_doc, } - @frappe.whitelist() def get_available_pos_profiles(company, currency): pos_profiles_list = frappe.get_list( diff --git a/posawesome/posawesome/api/posapp.py b/posawesome/posawesome/api/posapp.py index d92dacafc..a597f0430 100644 --- a/posawesome/posawesome/api/posapp.py +++ b/posawesome/posawesome/api/posapp.py @@ -126,6 +126,213 @@ def update_opening_shift_data(data, pos_profile): data["stock_settings"].update({"allow_negative_stock": allow_negative_stock}) +# @frappe.whitelist() +# def get_items( +# pos_profile, price_list=None, item_group="", search_value="", customer=None +# ): +# _pos_profile = json.loads(pos_profile) +# ttl = _pos_profile.get("posa_server_cache_duration") +# if ttl: +# ttl = int(ttl) * 30 + +# @redis_cache(ttl=ttl or 1800) +# def __get_items(pos_profile, price_list, item_group, search_value, customer=None): +# return _get_items(pos_profile, price_list, item_group, search_value, customer) + +# def _get_items(pos_profile, price_list, item_group, search_value, customer=None): +# pos_profile = json.loads(pos_profile) +# today = nowdate() +# data = dict() +# posa_display_items_in_stock = pos_profile.get("posa_display_items_in_stock") +# search_serial_no = pos_profile.get("posa_search_serial_no") +# search_batch_no = pos_profile.get("posa_search_batch_no") +# posa_show_template_items = pos_profile.get("posa_show_template_items") +# warehouse = pos_profile.get("warehouse") +# use_limit_search = pos_profile.get("pose_use_limit_search") +# search_limit = 0 + +# if not price_list: +# price_list = pos_profile.get("selling_price_list") + +# limit = "" + +# condition = "" +# condition += get_item_group_condition(pos_profile.get("name")) + +# if use_limit_search: +# search_limit = pos_profile.get("posa_search_limit") or 500 +# if search_value: +# data = search_serial_or_batch_or_barcode_number( +# search_value, search_serial_no +# ) + +# item_code = data.get("item_code") if data.get("item_code") else search_value +# serial_no = data.get("serial_no") if data.get("serial_no") else "" +# batch_no = data.get("batch_no") if data.get("batch_no") else "" +# barcode = data.get("barcode") if data.get("barcode") else "" + +# condition += get_seearch_items_conditions( +# item_code, serial_no, batch_no, barcode +# ) +# if item_group: +# condition += " AND item_group like '%{item_group}%'".format( +# item_group=item_group +# ) +# limit = " LIMIT {search_limit}".format(search_limit=search_limit) + +# if not posa_show_template_items: +# condition += " AND has_variants = 0" + +# result = [] + +# items_data = frappe.db.sql( +# """ +# SELECT +# name AS item_code, +# item_name, +# description, +# stock_uom, +# image, +# is_stock_item, +# has_variants, +# variant_of, +# item_group, +# idx as idx, +# has_batch_no, +# has_serial_no, +# max_discount, +# brand +# FROM +# `tabItem` +# WHERE +# disabled = 0 +# AND is_sales_item = 1 +# AND is_fixed_asset = 0 +# {condition} +# ORDER BY +# item_name asc +# {limit} +# """.format( +# condition=condition, limit=limit +# ), +# as_dict=1, +# ) + +# if items_data: +# items = [d.item_code for d in items_data] +# item_prices_data = frappe.get_all( +# "Item Price", +# fields=["item_code", "price_list_rate", "currency", "uom"], +# filters={ +# "price_list": price_list, +# "item_code": ["in", items], +# "currency": pos_profile.get("currency"), +# "selling": 1, +# "valid_from": ["<=", today], +# "customer": ["in", ["", None, customer]], +# }, +# or_filters=[ +# ["valid_upto", ">=", today], +# ["valid_upto", "in", ["", None]], +# ], +# order_by="valid_from ASC, valid_upto DESC", +# ) + +# item_prices = {} +# for d in item_prices_data: +# item_prices.setdefault(d.item_code, {}) +# item_prices[d.item_code][d.get("uom") or "None"] = d + +# for item in items_data: +# item_code = item.item_code +# item_price = {} +# if item_prices.get(item_code): +# item_price = ( +# item_prices.get(item_code).get(item.stock_uom) +# or item_prices.get(item_code).get("None") +# or {} +# ) +# item_barcode = frappe.get_all( +# "Item Barcode", +# filters={"parent": item_code}, +# fields=["barcode", "posa_uom"], +# ) +# batch_no_data = [] +# if search_batch_no: +# batch_list = get_batch_qty(warehouse=warehouse, item_code=item_code) +# if batch_list: +# for batch in batch_list: +# if batch.qty > 0 and batch.batch_no: +# batch_doc = frappe.get_cached_doc( +# "Batch", batch.batch_no +# ) +# if ( +# str(batch_doc.expiry_date) > str(today) +# or batch_doc.expiry_date in ["", None] +# ) and batch_doc.disabled == 0: +# batch_no_data.append( +# { +# "batch_no": batch.batch_no, +# "batch_qty": batch.qty, +# "expiry_date": batch_doc.expiry_date, +# "batch_price": batch_doc.posa_batch_price, +# "manufacturing_date": batch_doc.manufacturing_date, +# } +# ) +# serial_no_data = [] +# if search_serial_no: +# serial_no_data = frappe.get_all( +# "Serial No", +# filters={ +# "item_code": item_code, +# "status": "Active", +# "warehouse": warehouse, +# }, +# fields=["name as serial_no"], +# ) +# item_stock_qty = 0 +# if pos_profile.get("posa_display_items_in_stock") or use_limit_search: +# item_stock_qty = get_stock_availability( +# item_code, pos_profile.get("warehouse") +# ) +# attributes = "" +# if pos_profile.get("posa_show_template_items") and item.has_variants: +# attributes = get_item_attributes(item.item_code) +# item_attributes = "" +# if pos_profile.get("posa_show_template_items") and item.variant_of: +# item_attributes = frappe.get_all( +# "Item Variant Attribute", +# fields=["attribute", "attribute_value"], +# filters={"parent": item.item_code, "parentfield": "attributes"}, +# ) +# if posa_display_items_in_stock and ( +# not item_stock_qty or item_stock_qty < 0 +# ): +# pass +# else: +# row = {} +# row.update(item) +# row.update( +# { +# "rate": item_price.get("price_list_rate") or 0, +# "currency": item_price.get("currency") +# or pos_profile.get("currency"), +# "item_barcode": item_barcode or [], +# "actual_qty": item_stock_qty or 0, +# "serial_no_data": serial_no_data or [], +# "batch_no_data": batch_no_data or [], +# "attributes": attributes or "", +# "item_attributes": item_attributes or "", +# } +# ) +# result.append(row) +# return result + +# if _pos_profile.get("posa_use_server_cache"): +# return __get_items(pos_profile, price_list, item_group, search_value, customer) +# else: +# return _get_items(pos_profile, price_list, item_group, search_value, customer) + @frappe.whitelist() def get_items( pos_profile, price_list=None, item_group="", search_value="", customer=None @@ -155,21 +362,22 @@ def _get_items(pos_profile, price_list, item_group, search_value, customer=None) price_list = pos_profile.get("selling_price_list") limit = "" + condition = get_item_group_condition(pos_profile.get("name")) - condition = "" - condition += get_item_group_condition(pos_profile.get("name")) - + # Optimize search value handling + item_code, serial_no, batch_no, barcode = "", "", "", "" if use_limit_search: search_limit = pos_profile.get("posa_search_limit") or 500 if search_value: data = search_serial_or_batch_or_barcode_number( search_value, search_serial_no ) - - item_code = data.get("item_code") if data.get("item_code") else search_value - serial_no = data.get("serial_no") if data.get("serial_no") else "" - batch_no = data.get("batch_no") if data.get("batch_no") else "" - barcode = data.get("barcode") if data.get("barcode") else "" + item_code = data.get("item_code") or search_value + serial_no = data.get("serial_no") or "" + batch_no = data.get("batch_no") or "" + barcode = data.get("barcode") or "" + else: + item_code = search_value condition += get_seearch_items_conditions( item_code, serial_no, batch_no, barcode @@ -183,8 +391,7 @@ def _get_items(pos_profile, price_list, item_group, search_value, customer=None) if not posa_show_template_items: condition += " AND has_variants = 0" - result = [] - + # Use a single SQL query to fetch all required item fields items_data = frappe.db.sql( """ SELECT @@ -218,122 +425,145 @@ def _get_items(pos_profile, price_list, item_group, search_value, customer=None) as_dict=1, ) - if items_data: - items = [d.item_code for d in items_data] - item_prices_data = frappe.get_all( - "Item Price", - fields=["item_code", "price_list_rate", "currency", "uom"], + if not items_data: + return [] + + items = [d.item_code for d in items_data] + + # Batch fetch item prices + item_prices_data = frappe.get_all( + "Item Price", + fields=["item_code", "price_list_rate", "currency", "uom"], + filters={ + "price_list": price_list, + "item_code": ["in", items], + "currency": pos_profile.get("currency"), + "selling": 1, + "valid_from": ["<=", today], + "customer": ["in", ["", None, customer]], + }, + or_filters=[ + ["valid_upto", ">=", today], + ["valid_upto", "in", ["", None]], + ], + order_by="valid_from ASC, valid_upto DESC", + ) + + # Build price lookup + item_prices = {} + for d in item_prices_data: + item_prices.setdefault(d.item_code, {}) + item_prices[d.item_code][d.get("uom") or "None"] = d + + # Batch fetch barcodes for all items + barcodes_map = {} + barcodes_data = frappe.get_all( + "Item Barcode", + filters={"parent": ["in", items]}, + fields=["parent", "barcode", "posa_uom"], + ) + for b in barcodes_data: + barcodes_map.setdefault(b.parent, []).append({"barcode": b.barcode, "posa_uom": b.posa_uom}) + + # Pre-fetch batch and serial data if needed + batch_map = {} + if search_batch_no: + for item_code in items: + batch_list = get_batch_qty(warehouse=warehouse, item_code=item_code) + batch_map[item_code] = [] + if batch_list: + for batch in batch_list: + if batch.qty > 0 and batch.batch_no: + batch_doc = frappe.get_cached_doc("Batch", batch.batch_no) + if ( + str(batch_doc.expiry_date) > str(today) + or batch_doc.expiry_date in ["", None] + ) and batch_doc.disabled == 0: + batch_map[item_code].append( + { + "batch_no": batch.batch_no, + "batch_qty": batch.qty, + "expiry_date": batch_doc.expiry_date, + "batch_price": batch_doc.posa_batch_price, + "manufacturing_date": batch_doc.manufacturing_date, + } + ) + + serial_map = {} + if search_serial_no: + serial_data = frappe.get_all( + "Serial No", filters={ - "price_list": price_list, "item_code": ["in", items], - "currency": pos_profile.get("currency"), - "selling": 1, - "valid_from": ["<=", today], - "customer": ["in", ["", None, customer]], + "status": "Active", + "warehouse": warehouse, }, - or_filters=[ - ["valid_upto", ">=", today], - ["valid_upto", "in", ["", None]], - ], - order_by="valid_from ASC, valid_upto DESC", + fields=["item_code", "name as serial_no"], ) - - item_prices = {} - for d in item_prices_data: - item_prices.setdefault(d.item_code, {}) - item_prices[d.item_code][d.get("uom") or "None"] = d - + for s in serial_data: + serial_map.setdefault(s.item_code, []).append({"serial_no": s.serial_no}) + + # Batch fetch stock qty if needed + stock_qty_map = {} + if posa_display_items_in_stock or use_limit_search: + for item_code in items: + stock_qty_map[item_code] = get_stock_availability(item_code, warehouse) + + # Batch fetch attributes if needed + attributes_map = {} + item_attributes_map = {} + if posa_show_template_items: for item in items_data: - item_code = item.item_code - item_price = {} - if item_prices.get(item_code): - item_price = ( - item_prices.get(item_code).get(item.stock_uom) - or item_prices.get(item_code).get("None") - or {} - ) - item_barcode = frappe.get_all( - "Item Barcode", - filters={"parent": item_code}, - fields=["barcode", "posa_uom"], - ) - batch_no_data = [] - if search_batch_no: - batch_list = get_batch_qty(warehouse=warehouse, item_code=item_code) - if batch_list: - for batch in batch_list: - if batch.qty > 0 and batch.batch_no: - batch_doc = frappe.get_cached_doc( - "Batch", batch.batch_no - ) - if ( - str(batch_doc.expiry_date) > str(today) - or batch_doc.expiry_date in ["", None] - ) and batch_doc.disabled == 0: - batch_no_data.append( - { - "batch_no": batch.batch_no, - "batch_qty": batch.qty, - "expiry_date": batch_doc.expiry_date, - "batch_price": batch_doc.posa_batch_price, - "manufacturing_date": batch_doc.manufacturing_date, - } - ) - serial_no_data = [] - if search_serial_no: - serial_no_data = frappe.get_all( - "Serial No", - filters={ - "item_code": item_code, - "status": "Active", - "warehouse": warehouse, - }, - fields=["name as serial_no"], - ) - item_stock_qty = 0 - if pos_profile.get("posa_display_items_in_stock") or use_limit_search: - item_stock_qty = get_stock_availability( - item_code, pos_profile.get("warehouse") - ) - attributes = "" - if pos_profile.get("posa_show_template_items") and item.has_variants: - attributes = get_item_attributes(item.item_code) - item_attributes = "" - if pos_profile.get("posa_show_template_items") and item.variant_of: - item_attributes = frappe.get_all( + if item.has_variants: + attributes_map[item.item_code] = get_item_attributes(item.item_code) + if item.variant_of: + item_attributes_map[item.item_code] = frappe.get_all( "Item Variant Attribute", fields=["attribute", "attribute_value"], filters={"parent": item.item_code, "parentfield": "attributes"}, ) - if posa_display_items_in_stock and ( - not item_stock_qty or item_stock_qty < 0 - ): - pass - else: - row = {} - row.update(item) - row.update( - { - "rate": item_price.get("price_list_rate") or 0, - "currency": item_price.get("currency") - or pos_profile.get("currency"), - "item_barcode": item_barcode or [], - "actual_qty": item_stock_qty or 0, - "serial_no_data": serial_no_data or [], - "batch_no_data": batch_no_data or [], - "attributes": attributes or "", - "item_attributes": item_attributes or "", - } - ) - result.append(row) + + result = [] + for item in items_data: + item_code = item.item_code + item_price = {} + if item_prices.get(item_code): + item_price = ( + item_prices.get(item_code).get(item.stock_uom) + or item_prices.get(item_code).get("None") + or {} + ) + item_barcode = barcodes_map.get(item_code, []) + batch_no_data = batch_map.get(item_code, []) if search_batch_no else [] + serial_no_data = serial_map.get(item_code, []) if search_serial_no else [] + item_stock_qty = stock_qty_map.get(item_code, 0) if (posa_display_items_in_stock or use_limit_search) else 0 + attributes = attributes_map.get(item_code, "") if posa_show_template_items and item.has_variants else "" + item_attributes = item_attributes_map.get(item_code, "") if posa_show_template_items and item.variant_of else "" + + if posa_display_items_in_stock and (not item_stock_qty or item_stock_qty < 0): + continue + + row = {} + row.update(item) + row.update( + { + "rate": item_price.get("price_list_rate") or 0, + "currency": item_price.get("currency") or pos_profile.get("currency"), + "item_barcode": item_barcode, + "actual_qty": item_stock_qty or 0, + "serial_no_data": serial_no_data, + "batch_no_data": batch_no_data, + "attributes": attributes, + "item_attributes": item_attributes, + } + ) + result.append(row) return result if _pos_profile.get("posa_use_server_cache"): return __get_items(pos_profile, price_list, item_group, search_value, customer) else: return _get_items(pos_profile, price_list, item_group, search_value, customer) - - def get_item_group_condition(pos_profile): cond = " and 1=1" item_groups = get_item_groups(pos_profile) @@ -368,43 +598,40 @@ def get_items_groups(): as_dict=1, ) - def get_customer_groups(pos_profile): customer_groups = [] if pos_profile.get("customer_groups"): - # Get items based on the item groups defined in the POS profile + # Use set to avoid duplicates, and collect all child group names efficiently + seen = set() for data in pos_profile.get("customer_groups"): - customer_groups.extend( - [ - "%s" % frappe.db.escape(d.get("name")) - for d in get_child_nodes( - "Customer Group", data.get("customer_group") - ) - ] - ) - - return list(set(customer_groups)) - + # Get all child nodes for the customer group + for d in get_child_nodes("Customer Group", data.get("customer_group")): + group_name = d.get("name") + if group_name and group_name not in seen: + seen.add(group_name) + customer_groups = list(seen) + return customer_groups def get_child_nodes(group_type, root): - lft, rgt = frappe.db.get_value(group_type, root, ["lft", "rgt"]) + # Use tuple unpacking for lft, rgt and check for None + lft_rgt = frappe.db.get_value(group_type, root, ["lft", "rgt"]) + if not lft_rgt: + return [] + lft, rgt = lft_rgt return frappe.db.sql( - """ Select name, lft, rgt from `tab{tab}` where - lft >= {lft} and rgt <= {rgt} order by lft""".format( - tab=group_type, lft=lft, rgt=rgt - ), + """SELECT name, lft, rgt FROM `tab{tab}` WHERE lft >= %s AND rgt <= %s ORDER BY lft""".format(tab=group_type), + (lft, rgt), as_dict=1, ) - def get_customer_group_condition(pos_profile): cond = "disabled = 0" customer_groups = get_customer_groups(pos_profile) if customer_groups: - cond = " customer_group in (%s)" % (", ".join(["%s"] * len(customer_groups))) - - return cond % tuple(customer_groups) - + placeholders = ", ".join(["%s"] * len(customer_groups)) + cond += " AND customer_group IN ({})".format(placeholders) + return cond, tuple(customer_groups) + return cond, () @frappe.whitelist() def get_customer_names(pos_profile): @@ -419,19 +646,14 @@ def __get_customer_names(pos_profile): def _get_customer_names(pos_profile): pos_profile = json.loads(pos_profile) - condition = "" - condition += get_customer_group_condition(pos_profile) - customers = frappe.db.sql( - """ + condition, params = get_customer_group_condition(pos_profile) + query = """ SELECT name, mobile_no, email_id, tax_id, customer_name, primary_address FROM `tabCustomer` WHERE {0} - ORDER by name - """.format( - condition - ), - as_dict=1, - ) + ORDER BY name + """.format(condition) + customers = frappe.db.sql(query, params, as_dict=1) return customers if _pos_profile.get("posa_use_server_cache"): @@ -439,7 +661,6 @@ def _get_customer_names(pos_profile): else: return _get_customer_names(pos_profile) - @frappe.whitelist() def get_sales_person_names(): sales_persons = frappe.get_list( diff --git a/posawesome/posawesome/custom/pos_settings.json b/posawesome/posawesome/custom/pos_settings.json new file mode 100644 index 000000000..aadaa7ca8 --- /dev/null +++ b/posawesome/posawesome/custom/pos_settings.json @@ -0,0 +1,70 @@ +{ + "custom_fields": [ + { + "_assign": null, + "_comments": null, + "_liked_by": null, + "_user_tags": null, + "allow_in_quick_entry": 0, + "allow_on_submit": 0, + "bold": 0, + "collapsible": 0, + "collapsible_depends_on": null, + "columns": 0, + "creation": "2023-10-16 15:52:07.916335", + "default": null, + "depends_on": null, + "description": null, + "docstatus": 0, + "dt": "POS Settings", + "fetch_from": null, + "fetch_if_empty": 0, + "fieldname": "custom_auto_close_shift", + "fieldtype": "Datetime", + "hidden": 0, + "hide_border": 0, + "hide_days": 0, + "hide_seconds": 0, + "idx": 3, + "ignore_user_permissions": 0, + "ignore_xss_filter": 0, + "in_global_search": 0, + "in_list_view": 0, + "in_preview": 0, + "in_standard_filter": 0, + "insert_after": "auto_close_shift", + "is_system_generated": 0, + "is_virtual": 0, + "label": "Auto Close Shift", + "length": 0, + "mandatory_depends_on": null, + "modified": "2023-10-16 15:52:07.916335", + "modified_by": "Administrator", + "module": "POSAwesome", + "name": "POS Settings-custom_auto_close_shift", + "no_copy": 0, + "non_negative": 0, + "options": null, + "owner": "Administrator", + "permlevel": 0, + "precision": "", + "print_hide": 0, + "print_hide_if_no_value": 0, + "print_width": null, + "read_only": 0, + "read_only_depends_on": null, + "report_hide": 0, + "reqd": 0, + "search_index": 0, + "sort_options": 0, + "translatable": 0, + "unique": 0, + "width": null + } + ], + "custom_perms": [], + "doctype": "POS Settings", + "links": [], + "property_setters": [], + "sync_on_migrate": 1 +} \ No newline at end of file diff --git a/posawesome/posawesome/doctype/pezesha_settings/__init__.py b/posawesome/posawesome/doctype/pezesha_settings/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/posawesome/posawesome/doctype/pezesha_settings/pezesha_settings.js b/posawesome/posawesome/doctype/pezesha_settings/pezesha_settings.js new file mode 100644 index 000000000..6ed47adf7 --- /dev/null +++ b/posawesome/posawesome/doctype/pezesha_settings/pezesha_settings.js @@ -0,0 +1,8 @@ +// Copyright (c) 2024, Youssef Restom and contributors +// For license information, please see license.txt + +frappe.ui.form.on('Pezesha Settings', { + // refresh: function(frm) { + + // } +}); diff --git a/posawesome/posawesome/doctype/pezesha_settings/pezesha_settings.json b/posawesome/posawesome/doctype/pezesha_settings/pezesha_settings.json new file mode 100644 index 000000000..da359f4c8 --- /dev/null +++ b/posawesome/posawesome/doctype/pezesha_settings/pezesha_settings.json @@ -0,0 +1,64 @@ +{ + "actions": [], + "allow_rename": 1, + "creation": "2024-03-28 12:08:55.556406", + "default_view": "List", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "enable", + "client_id", + "client_secret_id", + "authorization" + ], + "fields": [ + { + "fieldname": "client_id", + "fieldtype": "Data", + "label": "Client ID", + "mandatory_depends_on": "eval:doc.enable == 1;" + }, + { + "fieldname": "client_secret_id", + "fieldtype": "Data", + "label": "Client Secret ID", + "mandatory_depends_on": "eval:doc.enable == 1;" + }, + { + "fieldname": "authorization", + "fieldtype": "Small Text", + "label": "Authorization", + "read_only": 1 + }, + { + "default": "0", + "fieldname": "enable", + "fieldtype": "Check", + "label": "Enable" + } + ], + "index_web_pages_for_search": 1, + "issingle": 1, + "links": [], + "modified": "2024-03-28 14:27:25.725083", + "modified_by": "Administrator", + "module": "POSAwesome", + "name": "Pezesha Settings", + "owner": "Administrator", + "permissions": [ + { + "create": 1, + "delete": 1, + "email": 1, + "print": 1, + "read": 1, + "role": "System Manager", + "share": 1, + "write": 1 + } + ], + "sort_field": "modified", + "sort_order": "DESC", + "states": [] +} \ No newline at end of file diff --git a/posawesome/posawesome/doctype/pezesha_settings/pezesha_settings.py b/posawesome/posawesome/doctype/pezesha_settings/pezesha_settings.py new file mode 100644 index 000000000..dd24e8a1a --- /dev/null +++ b/posawesome/posawesome/doctype/pezesha_settings/pezesha_settings.py @@ -0,0 +1,141 @@ +# Copyright (c) 2024, Youssef Restom and contributors +# For license information, please see license.txt + +import frappe +import requests +import json +from frappe.model.document import Document +from frappe import _ +from frappe.integrations.utils import (make_get_request, make_post_request, create_request_log) + +class PezeshaSettings(Document): + def before_validate(self): + if self.enable: + try: + response = make_post_request( + # url = 'https://gateway.pezesha.com', + url="https://gateway.pezesha.com/oauth/token", + headers = { + 'pezesha-apikey': '9ea7l6xraTJjDAXU6KYogxcArmlDGE1u', + 'Accept-Encoding': 'gzip, deflate' + }, + data={ + "grant_type": "client_credentials", + "client_id": self.client_id, + "client_secret": self.client_secret_id, + "provider": "users" + }, + auth=( + self.client_id, + self.get_password(fieldname="client_secret_id", raise_exception=False), + ), + ) + self.authorization = response['access_token'] + except Exception as e: + frappe.throw(_("Seems API Key or API Secret is wrong !!!")) + + + +@frappe.whitelist() +def pezesha_loan_offer(customer, pos_profile): + pos = frappe.get_doc("POS Profile", pos_profile) + pz_st = frappe.db.get_single_value('Pezesha Settings', 'authorization') + url = 'https://gateway.pezesha.com/mfi/v1/borrowers/options' + headers = { + 'Authorization': f'Bearer {pz_st}', + 'pezesha-apikey': '9ea7l6xraTJjDAXU6KYogxcArmlDGE1u', + 'Accept-Encoding': 'gzip, deflate' + } + data = { + 'channel': pos.custom_pezesha_channel_id, + 'identifier': customer + } + response = requests.post(url, headers=headers, data=data) + if response.status_code == 200: + try: + dt = response.json() + ddt = dt['data'] + return dt + except KeyError: + frappe.msgprint("You already have a pending loan. Cannot apply for new loan until current one is cleared") + return "You already have a pending loan. Cannot apply for new loan until current one is cleared" + else: + frappe.msgprint(f"Unable To Find Borrower {customer}") + return response.status_code + +@frappe.whitelist() +def pezesha_loan_application(data, pos_profile): + res = json.loads(data) + pos = frappe.get_doc("POS Profile", pos_profile) + pz_st = frappe.db.get_single_value('Pezesha Settings', 'authorization') + url = 'https://gateway.pezesha.com/mfi/v1/borrowers/loans' + headers = { + 'Authorization': f'Bearer {pz_st}', + 'pezesha-apikey': '9ea7l6xraTJjDAXU6KYogxcArmlDGE1u', + 'Accept-Encoding': 'gzip, deflate' + } + data = { + 'channel': pos.custom_pezesha_channel_id, + 'pezesha_id': res.get('pezesha_customer_id'), + 'amount': res.get('amount'), + 'duration': res.get('duration'), + 'interest': res.get('interest'), + 'rate': res.get('rate'), + 'fee': res.get('fee') + } + + response = requests.post(url, headers=headers, data=data) + return response.json() + +@frappe.whitelist() +def pezesha_loan_status(customer, pos_profile): + pos = frappe.get_doc("POS Profile", pos_profile) + pz_st = frappe.db.get_single_value('Pezesha Settings', 'authorization') + url = 'https://gateway.pezesha.com/mfi/v1/borrowers/latest' + headers = { + 'Authorization': f'Bearer {pz_st}', + 'pezesha-apikey': '9ea7l6xraTJjDAXU6KYogxcArmlDGE1u', + 'Accept-Encoding': 'gzip, deflate' + } + data = { + 'channel': pos.custom_pezesha_channel_id, + 'identifier': customer + } + response = requests.post(url, headers=headers, data=data) + if response.status_code == 200: + try: + dt = response.json() + ddt = dt['data'] + amt = ddt['loan_amount'] + return ddt + except KeyError: + frappe.msgprint("Please Apply Loan Application") + return "Please Apply Loan Application" + else: + frappe.msgprint("Please Apply Loan Application") + return response.status_code + +def corn(): + doc = frappe.get_doc('Pezesha Settings') + if doc.enable: + try: + response = make_post_request( + url="https://gateway.pezesha.com/oauth/token", + headers = { + 'pezesha-apikey': '9ea7l6xraTJjDAXU6KYogxcArmlDGE1u', + 'Accept-Encoding': 'gzip, deflate' + }, + data={ + "grant_type": "client_credentials", + "client_id": doc.client_id, + "client_secret": doc.client_secret_id, + "provider": "users" + }, + auth=( + doc.client_id, + doc.get_password(fieldname="client_secret_id", raise_exception=False), + ), + ) + doc.db_set('authorization', response['access_token']) + except Exception as e: + frappe.throw(_("Seems API Key or API Secret is wrong !!!")) diff --git a/posawesome/posawesome/doctype/pezesha_settings/test_pezesha_settings.py b/posawesome/posawesome/doctype/pezesha_settings/test_pezesha_settings.py new file mode 100644 index 000000000..39f34ace0 --- /dev/null +++ b/posawesome/posawesome/doctype/pezesha_settings/test_pezesha_settings.py @@ -0,0 +1,9 @@ +# Copyright (c) 2024, Youssef Restom and Contributors +# See license.txt + +# import frappe +from frappe.tests.utils import FrappeTestCase + + +class TestPezeshaSettings(FrappeTestCase): + pass diff --git a/posawesome/public/js/posapp/Home.vue b/posawesome/public/js/posapp/Home.vue index 67dd9b7a7..5d4f70c62 100644 --- a/posawesome/public/js/posapp/Home.vue +++ b/posawesome/public/js/posapp/Home.vue @@ -1,8 +1,10 @@ @@ -13,35 +15,43 @@ import POS from './components/pos/Pos.vue'; import Payments from './components/payments/Pay.vue'; export default { - data: function () { - return { - page: 'POS', - }; - }, + name: "Home", components: { Navbar, POS, Payments, }, + data() { + return { + page: 'POS', + frappeNavRemoved: false, + }; + }, methods: { setPage(page) { - this.page = page; + if (this.page !== page) { + this.page = page; + } }, - remove_frappe_nav() { - this.$nextTick(function () { - $('.page-head').remove(); - $('.navbar.navbar-default.navbar-fixed-top').remove(); + removeFrappeNav() { + // Only remove once for performance + if (this.frappeNavRemoved) return; + this.frappeNavRemoved = true; + this.$nextTick(() => { + const head = document.querySelector('.page-head'); + if (head) head.remove(); + const nav = document.querySelector('.navbar.navbar-default.navbar-fixed-top'); + if (nav) nav.remove(); }); }, }, mounted() { - this.remove_frappe_nav(); + this.removeFrappeNav(); }, - updated() {}, - created: function () { - setTimeout(() => { - this.remove_frappe_nav(); - }, 1000); + created() { + setTimeout(() => { + this.removeFrappeNav(); + }, 500); }, }; @@ -50,4 +60,4 @@ export default { .container1 { margin-top: 0px; } - + \ No newline at end of file diff --git a/posawesome/public/js/posapp/components/pos/Customer.vue b/posawesome/public/js/posapp/components/pos/Customer.vue index 86a5f036e..1f99b919b 100644 --- a/posawesome/public/js/posapp/components/pos/Customer.vue +++ b/posawesome/public/js/posapp/components/pos/Customer.vue @@ -61,6 +61,7 @@ + \ No newline at end of file diff --git a/posawesome/public/js/posapp/components/pos/Invoice.vue b/posawesome/public/js/posapp/components/pos/Invoice.vue index f77948da6..cea600808 100644 --- a/posawesome/public/js/posapp/components/pos/Invoice.vue +++ b/posawesome/public/js/posapp/components/pos/Invoice.vue @@ -629,7 +629,7 @@ - + @@ -742,7 +742,7 @@ - +
+ + + {{ dialogtitle }} + {{ dialogMessage }} + + Close + + + + + + + Loan Confirmation Request Pending Approval + + Close + + + + + + + Loan Application + + + + + + + + + + + + + Submit + Close + + + - -
+ + + + + + + + {{ payment.mode_of_payment }} + + + + + + + + {{ __("Request") }} + + + + + + + + {{ __("Credit Pezesha") }} + + + + + + + + - {{ payment.mode_of_payment }} - - - + + + + + + + + + + + + + + + + + + + + - {{ __("Request") }} + {{ __("Pezesha Loan Status") }}
- ({ + dialogtitle: "", + dialogMessage: "", + dialognotSuccessful: false, + dialogSuccessful: false, + dialogVisible: false, + success: true, + message: "Thank you for your Loan Approval.", + formLoan: { + loan_amount: null, + loan_id: null, + loan_status: null, + }, + formData: { + amount: 0, + rate: 0, + interest: 0, + }, loading: false, pos_profile: "", invoice_doc: "", @@ -731,6 +887,210 @@ export default { }), methods: { + openDialog() { + if (!this.invoice_doc.customer) { + evntBus.$emit("show_mesage", { + text: __(`There is no Customer!`), + color: "error", + }); + return; + } else { + // Emit a freeze event to indicate that a process is in progress + evntBus.$emit("freeze", { + title: __("Please wait..."), + }); + + // Make the server call + frappe.call({ + method: + "posawesome.posawesome.doctype.pezesha_settings.pezesha_settings.pezesha_loan_offer", + args: { + customer: this.invoice_doc.customer, + pos_profile: this.pos_profile.name, + }, + callback: (r) => { + st = r.message; + dt = st.data; + if (st.status == 200) { + // Populate formData with fetched values + this.formData.pezesha_customer_id = this.invoice_doc.customer; + this.formData.pezesha_channel_id = this.pos_profile.name; + this.formData.amount = dt.amount; + this.formData.fee = dt.fee; + this.formData.interest = (dt.amount * dt.rate) / 100; + this.formData.duration = dt.duration; + this.formData.rate = dt.rate; + this.changeHandler(); // Call changeHandler after setting initial values + // Once the server call is completed, emit an unfreeze event + evntBus.$emit("unfreeze"); + // Optionally, update dialog visibility or perform other actions + this.dialogVisible = true; + } else { + this.dialognotSuccessful = true; + evntBus.$emit("unfreeze"); + if (st.status == 400 || st == 400) { + this.dialogtitle = "Invalid Merchant ID"; + this.dialogMessage = + "Loan offer request failed: The provided merchant ID is invalid. Please verify your merchant ID and try again."; + } else if (st.status == 404 || st == 404) { + this.dialogtitle = "Merchant not found"; + this.dialogMessage = "Merchant not found."; + } else if (st.status == 503 || st == 503) { + this.dialogtitle = "Loan Offer Unavailable"; + this.dialogMessage = + "Loan offer request failed: Unable to retrieve loan offers at this time. Please try again later."; + } else if (st.status == 401 || st == 401) { + this.dialogtitle = "Missing Authorization Token"; + this.dialogMessage = + "The request does not include the required authorization token."; + } + } + }, + }); + } + }, + changeHandler() { + // if(dt.amount >= this.formData.amount){ + this.formData.interest = + (this.formData.amount * this.formData.rate) / 100; + // }else{ + // frappe.throw("Please ensure that the input value does not exceed the loan amount limit.") + // } + // change of user input, do something + }, + closeDialog() { + this.dialogVisible = false; + }, + closeSuccessfulDialog() { + this.dialogSuccessful = false; + }, + // pezeshaLoanStatus(){ + + // }, + pezeshaLoanStatus() { + evntBus.$emit("freeze", { + title: __("Please wait..."), + }); + frappe.call({ + method: + "posawesome.posawesome.doctype.pezesha_settings.pezesha_settings.pezesha_loan_status", + args: { + customer: this.invoice_doc.customer, + pos_profile: this.pos_profile.name, + }, + callback: (r) => { + if (r.message) { + pez = r.message; + this.formLoan.loan_amount = pez.loan_amount; + this.formLoan.loan_id = pez.loan_id; + this.formLoan.loan_status = pez.status; + evntBus.$emit("unfreeze"); + } else { + this.dialognotSuccessful = true; + evntBus.$emit("unfreeze"); + if (st.status == 404 || st == 404) { + this.dialogtitle = "Loan Not Found"; + this.dialogMessage = + "Loan status request failed: No loan found for the given identifier. Please verify your details and try again."; + } else if (st.status == 400 || st == 400) { + this.dialogtitle = "Invalid Loan Status Request"; + this.dialogMessage = + "Loan status request failed: Invalid request parameters. Please ensure you provide a valid channel and identifier."; + } else if (st.status == 401 || st == 401) { + this.dialogtitle = "Authorization Token Missing"; + this.dialogMessage = + "Loan status request failed: Missing authorization token. Please provide a valid token to retrieve loan status."; + } + } + }, + }); + }, + closeNotSuccessfulDialog() { + this.dialognotSuccessful = false; + }, + // submitForm() { + // if(this.invoice_doc.grand_total >= this.formData.amount){ + // frappe.call({ + // method: "posawesome.posawesome.doctype.pezesha_settings.pezesha_settings.pezesha_loan_application", + // args: { + // data: this.formData, + // pos_profile: this.pos_profile.name, + // }, + // callback: (r) => { + // // Emit an unfreeze event after receiving the response + // evntBus.$emit("unfreeze"); + // let s = r.message; + // if (s.status == 200) { + // this.dialogMessage = JSON.stringify(s); + // this.dialogSuccessful = true; + // } else { + // this.dialognotSuccessful = true; + // } + // } + // }); + + // }else{ + // frappe.throw("Please ensure that the input value does not exceed the loan amount limit.") + // } + // // Here you can handle form submission + // // Emit a freeze event to indicate that a process is in progress + // evntBus.$emit("freeze", { + // title: __("Please wait..."), + // }); + + // // Make the server call + + // }, + submitForm() { + if (this.invoice_doc.grand_total >= this.formData.amount) { + // Here you can handle form submission + // Emit a freeze event to indicate that a process is in progress + evntBus.$emit("freeze", { + title: __("Please wait..."), + }); + // Make the server call + frappe.call({ + method: + "posawesome.posawesome.doctype.pezesha_settings.pezesha_settings.pezesha_loan_application", + args: { + data: this.formData, + pos_profile: this.pos_profile.name, + }, + callback: (r) => { + // Emit an unfreeze event after receiving the response + evntBus.$emit("unfreeze"); + let s = r.message; + if (s.status == 200) { + // this.dialogMessage = JSON.stringify(s); + this.dialogSuccessful = true; + } else { + this.dialognotSuccessful = true; + if (s.status == 403 || s == 403) { + this.dialogtitle = "Loan Application Denied"; + this.dialogMessage = + "Loan application failed: Your previous loan is yet to be fully paid or is overdue. Please settle outstanding dues to apply for a new loan."; + } else if (s.status == 400 || s == 400) { + this.dialogtitle = "Invalid Loan Amount"; + this.dialogMessage = + "Loan application failed: The requested loan amount exceeds your available credit limit. Please request a lower amount."; + } + // else if(st.status == 503 ){ + // dialogtitle = "Duplicate loan request"; + // dialogMessage = 'Kindly clear your previous loan before applying for a new Loan'; + // } + // else if(st.status == 401){ + // dialogtitle = "Missing Authorization Token"; + // dialogMessage = 'The request does not include the required authorization token.'; + // } + } + // Close the dialog after form submission + this.dialogVisible = false; + }, + }); + } else { + frappe.throw("Please ensure the input value matches the order amount."); + } + }, back_to_invoice() { evntBus.$emit("show_payment", "false"); evntBus.$emit("set_customer_readonly", false); @@ -1248,6 +1608,10 @@ export default { }, computed: { + formattedPezeshaAmount() { + // Assuming pezesha_amount is a computed property or data property + return this.formtCurrency(this.pezesha_amount); + }, total_payments() { let total = parseFloat(this.invoice_doc.loyalty_amount); if (this.invoice_doc && this.invoice_doc.payments) {