diff --git a/erpnext/__init__.py b/erpnext/__init__.py index a181c2d42c..c90e01cfbd 100644 --- a/erpnext/__init__.py +++ b/erpnext/__init__.py @@ -5,7 +5,7 @@ import frappe from erpnext.hooks import regional_overrides from frappe.utils import getdate -__version__ = '13.7.1' +__version__ = '13.8.0' def get_default_company(user=None): '''Get default company for user''' diff --git a/erpnext/accounts/doctype/account/account.py b/erpnext/accounts/doctype/account/account.py index 1be2fbf5c8..f763df0852 100644 --- a/erpnext/accounts/doctype/account/account.py +++ b/erpnext/accounts/doctype/account/account.py @@ -230,7 +230,7 @@ class Account(NestedSet): if self.check_gle_exists(): throw(_("Account with existing transaction can not be converted to group.")) elif self.account_type and not self.flags.exclude_account_type_check: - throw(_("Cannot covert to Group because Account Type is selected.")) + throw(_("Cannot convert to Group because Account Type is selected.")) else: self.is_group = 1 self.save() diff --git a/erpnext/accounts/doctype/budget/test_budget.py b/erpnext/accounts/doctype/budget/test_budget.py index 603e21ea24..6c25f0024d 100644 --- a/erpnext/accounts/doctype/budget/test_budget.py +++ b/erpnext/accounts/doctype/budget/test_budget.py @@ -249,7 +249,7 @@ class TestBudget(unittest.TestCase): def set_total_expense_zero(posting_date, budget_against_field=None, budget_against_CC=None): if budget_against_field == "project": - budget_against = "_Test Project" + budget_against = frappe.db.get_value("Project", {"project_name": "_Test Project"}) else: budget_against = budget_against_CC or "_Test Cost Center - _TC" @@ -275,7 +275,7 @@ def set_total_expense_zero(posting_date, budget_against_field=None, budget_again "_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", posting_date=nowdate(), submit=True) elif budget_against_field == "project": make_journal_entry("_Test Account Cost for Goods Sold - _TC", - "_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", submit=True, project="_Test Project", posting_date=nowdate()) + "_Test Bank - _TC", -existing_expense, "_Test Cost Center - _TC", submit=True, project=budget_against, posting_date=nowdate()) def make_budget(**args): args = frappe._dict(args) diff --git a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py index 4fd8413d83..8456b49c8e 100644 --- a/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py +++ b/erpnext/accounts/doctype/chart_of_accounts_importer/chart_of_accounts_importer.py @@ -391,5 +391,5 @@ def set_default_accounts(company): }) company.save() - install_country_fixtures(company.name) + install_country_fixtures(company.name, company.country) company.create_default_tax_template() diff --git a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py index 6635128f9e..d788d91855 100644 --- a/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py +++ b/erpnext/accounts/doctype/payment_reconciliation/payment_reconciliation.py @@ -306,5 +306,5 @@ def reconcile_dr_cr_note(dr_cr_notes, company): } ] }) - + jv.flags.ignore_mandatory = True jv.submit() \ No newline at end of file diff --git a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json index 7459c11d4d..33c3e0432b 100644 --- a/erpnext/accounts/doctype/pos_invoice/pos_invoice.json +++ b/erpnext/accounts/doctype/pos_invoice/pos_invoice.json @@ -1545,6 +1545,7 @@ "fieldname": "consolidated_invoice", "fieldtype": "Link", "label": "Consolidated Sales Invoice", + "no_copy": 1, "options": "Sales Invoice", "read_only": 1 } @@ -1552,7 +1553,7 @@ "icon": "fa fa-file-text", "is_submittable": 1, "links": [], - "modified": "2021-02-01 15:03:33.800707", + "modified": "2021-07-29 13:37:20.636171", "modified_by": "Administrator", "module": "Accounts", "name": "POS Invoice", diff --git a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py index ffe8be1162..3173db13af 100644 --- a/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py +++ b/erpnext/accounts/doctype/pricing_rule/test_pricing_rule.py @@ -15,6 +15,7 @@ from erpnext.healthcare.doctype.lab_test_template.lab_test_template import make_ class TestPricingRule(unittest.TestCase): def setUp(self): delete_existing_pricing_rules() + setup_pricing_rule_data() def tearDown(self): delete_existing_pricing_rules() @@ -554,6 +555,8 @@ class TestPricingRule(unittest.TestCase): for doc in [si, si1]: doc.delete() +test_dependencies = ["Campaign"] + def make_pricing_rule(**args): args = frappe._dict(args) @@ -600,6 +603,13 @@ def make_pricing_rule(**args): if args.get(applicable_for): doc.db_set(applicable_for, args.get(applicable_for)) +def setup_pricing_rule_data(): + if not frappe.db.exists('Campaign', '_Test Campaign'): + frappe.get_doc({ + 'doctype': 'Campaign', + 'campaign_name': '_Test Campaign', + 'name': '_Test Campaign' + }).insert() def delete_existing_pricing_rules(): for doctype in ["Pricing Rule", "Pricing Rule Item Code", diff --git a/erpnext/accounts/doctype/pricing_rule/utils.py b/erpnext/accounts/doctype/pricing_rule/utils.py index b54d0e73a8..94abf3b3c0 100644 --- a/erpnext/accounts/doctype/pricing_rule/utils.py +++ b/erpnext/accounts/doctype/pricing_rule/utils.py @@ -168,7 +168,7 @@ def _get_tree_conditions(args, parenttype, table, allow_blank=True): frappe.throw(_("Invalid {0}").format(args.get(field))) parent_groups = frappe.db.sql_list("""select name from `tab%s` - where lft>=%s and rgt<=%s""" % (parenttype, '%s', '%s'), (lft, rgt)) + where lft<=%s and rgt>=%s""" % (parenttype, '%s', '%s'), (lft, rgt)) if parenttype in ["Customer Group", "Item Group", "Territory"]: parent_field = "parent_{0}".format(frappe.scrub(parenttype)) diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js index 7562418fd2..bda9f419a9 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.js @@ -134,7 +134,7 @@ erpnext.accounts.PurchaseInvoice = class PurchaseInvoice extends erpnext.buying. }, get_query_filters: { docstatus: 1, - status: ["not in", ["Closed", "Completed"]], + status: ["not in", ["Closed", "Completed", "Return Issued"]], company: me.frm.doc.company, is_return: 0 } diff --git a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py index b99d75ec49..863c104dff 100644 --- a/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/purchase_invoice.py @@ -27,6 +27,8 @@ from erpnext.accounts.doctype.tax_withholding_category.tax_withholding_category from erpnext.accounts.deferred_revenue import validate_service_stop_date from erpnext.stock.doctype.purchase_receipt.purchase_receipt import get_item_account_wise_additional_cost +class WarehouseMissingError(frappe.ValidationError): pass + form_grid_templates = { "items": "templates/form_grid/item_grid.html" } @@ -207,8 +209,8 @@ class PurchaseInvoice(BuyingController): if self.update_stock and for_validate: for d in self.get('items'): if not d.warehouse: - frappe.throw(_("Warehouse required at Row No {0}, please set default warehouse for the item {1} for the company {2}"). - format(d.idx, d.item_code, self.company)) + frappe.throw(_("Row No {0}: Warehouse is required. Please set a Default Warehouse for Item {1} and Company {2}"). + format(d.idx, d.item_code, self.company), exc=WarehouseMissingError) super(PurchaseInvoice, self).validate_warehouse() @@ -246,7 +248,7 @@ class PurchaseInvoice(BuyingController): and (not item.po_detail or not frappe.db.get_value("Purchase Order Item", item.po_detail, "delivered_by_supplier")): - if self.update_stock and (not item.from_warehouse): + if self.update_stock and item.warehouse and (not item.from_warehouse): if for_validate and item.expense_account and item.expense_account != warehouse_account[item.warehouse]["account"]: msg = _("Row {0}: Expense Head changed to {1} because account {2} is not linked to warehouse {3} or it is not the default inventory account").format( item.idx, frappe.bold(warehouse_account[item.warehouse]["account"]), frappe.bold(item.expense_account), frappe.bold(item.warehouse)) @@ -657,7 +659,7 @@ class PurchaseInvoice(BuyingController): ) gl_entries.append( self.get_gl_dict({ - "account": self.get_company_default("exchange_gain_loss_account"), + "account": self.get_company_default("exchange_gain_loss_account"), "against": self.supplier, "credit": discrepancy_caused_by_exchange_rate_difference, "cost_center": item.cost_center, @@ -1193,7 +1195,7 @@ def get_purchase_document_details(doc): purchase_receipts_or_invoices.append(item.get(doc_reference)) if item.get(items_reference): items.append(item.get(items_reference)) - + exchange_rate_map = frappe._dict(frappe.get_all(parent_doctype, filters={'name': ('in', purchase_receipts_or_invoices)}, fields=['name', 'conversion_rate'], as_list=1)) diff --git a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py index db6f143eb8..e90b35fc6a 100644 --- a/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py +++ b/erpnext/accounts/doctype/purchase_invoice/test_purchase_invoice.py @@ -240,7 +240,7 @@ class TestPurchaseInvoice(unittest.TestCase): pi.conversion_rate = 80 pi.insert() - pi.submit() + pi.submit() # Get exchnage gain and loss account exchange_gain_loss_account = frappe.db.get_value('Company', pi.company, 'exchange_gain_loss_account') @@ -978,7 +978,7 @@ class TestPurchaseInvoice(unittest.TestCase): unlink_enabled = frappe.db.get_value( "Accounts Settings", "Accounts Settings", "unlink_payment_on_cancel_of_invoice") - + frappe.db.set_value( "Accounts Settings", "Accounts Settings", "unlink_payment_on_cancel_of_invoice", 1) @@ -1018,8 +1018,8 @@ class TestPurchaseInvoice(unittest.TestCase): expected_gle = [ ["_Test Account Cost for Goods Sold - _TC", 37500.0], - ["_Test Payable USD - _TC", -40000.0], - ["Exchange Gain/Loss - _TC", 2500.0] + ["_Test Payable USD - _TC", -35000.0], + ["Exchange Gain/Loss - _TC", -2500.0] ] gl_entries = frappe.db.sql(""" @@ -1027,7 +1027,7 @@ class TestPurchaseInvoice(unittest.TestCase): where voucher_no=%s group by account order by account asc""", (pi.name), as_dict=1) - + for i, gle in enumerate(gl_entries): self.assertEqual(expected_gle[i][0], gle.account) self.assertEqual(expected_gle[i][1], gle.balance) @@ -1049,8 +1049,8 @@ class TestPurchaseInvoice(unittest.TestCase): expected_gle = [ ["_Test Account Cost for Goods Sold - _TC", 36500.0], - ["_Test Payable USD - _TC", -38000.0], - ["Exchange Gain/Loss - _TC", 1500.0] + ["_Test Payable USD - _TC", -35000.0], + ["Exchange Gain/Loss - _TC", -1500.0] ] gl_entries = frappe.db.sql(""" diff --git a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py index c6e6e3da6f..be20b18bea 100644 --- a/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py +++ b/erpnext/accounts/doctype/sales_invoice/test_sales_invoice.py @@ -2,13 +2,14 @@ # License: GNU General Public License v3. See license.txt from __future__ import unicode_literals -import frappe +import frappe, erpnext import unittest, copy, time from frappe.utils import nowdate, flt, getdate, cint, add_days, add_months from frappe.model.dynamic_links import get_dynamic_link_map from erpnext.stock.doctype.stock_entry.test_stock_entry import make_stock_entry, get_qty_after_transaction from erpnext.accounts.doctype.purchase_invoice.test_purchase_invoice import unlink_payment_on_cancel_of_invoice +from erpnext.accounts.doctype.purchase_invoice.purchase_invoice import WarehouseMissingError from erpnext.accounts.doctype.pos_profile.test_pos_profile import make_pos_profile from erpnext.assets.doctype.asset.test_asset import create_asset, create_asset_data from erpnext.exceptions import InvalidAccountCurrency, InvalidCurrency @@ -1073,7 +1074,7 @@ class TestSalesInvoice(unittest.TestCase): def test_gle_made_when_asset_is_returned(self): create_asset_data() asset = create_asset(item_code="Macbook Pro") - + si = create_sales_invoice(item_code="Macbook Pro", asset=asset.name, qty=1, rate=90000) return_si = create_sales_invoice(is_return=1, return_against=si.name, item_code="Macbook Pro", asset=asset.name, qty=-1, rate=90000) @@ -1081,7 +1082,7 @@ class TestSalesInvoice(unittest.TestCase): # Asset value is 100,000 but it was sold for 90,000, so there should be a loss of 10,000 loss_for_si = frappe.get_all( - "GL Entry", + "GL Entry", filters = { "voucher_no": si.name, "account": disposal_account @@ -1090,7 +1091,7 @@ class TestSalesInvoice(unittest.TestCase): )[0] loss_for_return_si = frappe.get_all( - "GL Entry", + "GL Entry", filters = { "voucher_no": return_si.name, "account": disposal_account @@ -1836,6 +1837,89 @@ class TestSalesInvoice(unittest.TestCase): self.assertEqual(target_doc.company, "_Test Company 1") self.assertEqual(target_doc.supplier, "_Test Internal Supplier") + def test_inter_company_transaction_without_default_warehouse(self): + "Check mapping (expense account) of inter company SI to PI in absence of default warehouse." + # setup + old_negative_stock = frappe.db.get_single_value("Stock Settings", "allow_negative_stock") + frappe.db.set_value("Stock Settings", None, "allow_negative_stock", 1) + + old_perpetual_inventory = erpnext.is_perpetual_inventory_enabled('_Test Company 1') + frappe.local.enable_perpetual_inventory['_Test Company 1'] = 1 + + frappe.db.set_value("Company", '_Test Company 1', "stock_received_but_not_billed", "Stock Received But Not Billed - _TC1") + frappe.db.set_value("Company", '_Test Company 1', "expenses_included_in_valuation", "Expenses Included In Valuation - _TC1") + + + if not frappe.db.exists("Customer", "_Test Internal Customer"): + customer = frappe.get_doc({ + "customer_group": "_Test Customer Group", + "customer_name": "_Test Internal Customer", + "customer_type": "Individual", + "doctype": "Customer", + "territory": "_Test Territory", + "is_internal_customer": 1, + "represents_company": "_Test Company 1" + }) + + customer.append("companies", { + "company": "Wind Power LLC" + }) + + customer.insert() + + if not frappe.db.exists("Supplier", "_Test Internal Supplier"): + supplier = frappe.get_doc({ + "supplier_group": "_Test Supplier Group", + "supplier_name": "_Test Internal Supplier", + "doctype": "Supplier", + "is_internal_supplier": 1, + "represents_company": "Wind Power LLC" + }) + + supplier.append("companies", { + "company": "_Test Company 1" + }) + + supplier.insert() + + # begin test + si = create_sales_invoice( + company = "Wind Power LLC", + customer = "_Test Internal Customer", + debit_to = "Debtors - WP", + warehouse = "Stores - WP", + income_account = "Sales - WP", + expense_account = "Cost of Goods Sold - WP", + cost_center = "Main - WP", + currency = "USD", + update_stock = 1, + do_not_save = 1 + ) + si.selling_price_list = "_Test Price List Rest of the World" + si.submit() + + target_doc = make_inter_company_transaction("Sales Invoice", si.name) + + # in absence of warehouse Stock Received But Not Billed is set as expense account while mapping + # mapping is not obstructed + self.assertIsNone(target_doc.items[0].warehouse) + self.assertEqual(target_doc.items[0].expense_account, "Stock Received But Not Billed - _TC1") + + target_doc.items[0].update({"cost_center": "Main - _TC1"}) + + # missing warehouse is validated on save, after mapping + self.assertRaises(WarehouseMissingError, target_doc.save) + + target_doc.items[0].update({"warehouse": "Stores - _TC1"}) + target_doc.save() + + # after warehouse is set, linked account or default inventory account is set + self.assertEqual(target_doc.items[0].expense_account, 'Stock In Hand - _TC1') + + # tear down + frappe.local.enable_perpetual_inventory['_Test Company 1'] = old_perpetual_inventory + frappe.db.set_value("Stock Settings", None, "allow_negative_stock", old_negative_stock) + def test_internal_transfer_gl_entry(self): ## Create internal transfer account account = create_account(account_name="Unrealized Profit", diff --git a/erpnext/erpnext_integrations/doctype/shopify_log/__init__.py b/erpnext/accounts/doctype/south_africa_vat_account/__init__.py similarity index 100% rename from erpnext/erpnext_integrations/doctype/shopify_log/__init__.py rename to erpnext/accounts/doctype/south_africa_vat_account/__init__.py diff --git a/erpnext/accounts/doctype/south_africa_vat_account/south_africa_vat_account.json b/erpnext/accounts/doctype/south_africa_vat_account/south_africa_vat_account.json new file mode 100644 index 0000000000..fa1aa7da59 --- /dev/null +++ b/erpnext/accounts/doctype/south_africa_vat_account/south_africa_vat_account.json @@ -0,0 +1,34 @@ +{ + "actions": [], + "autoname": "account", + "creation": "2021-07-08 22:04:24.634967", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "account" + ], + "fields": [ + { + "allow_in_quick_entry": 1, + "fieldname": "account", + "fieldtype": "Link", + "in_list_view": 1, + "in_preview": 1, + "label": "Account", + "options": "Account" + } + ], + "index_web_pages_for_search": 1, + "istable": 1, + "links": [], + "modified": "2021-07-08 22:35:33.202911", + "modified_by": "Administrator", + "module": "Accounts", + "name": "South Africa VAT Account", + "owner": "Administrator", + "permissions": [], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 +} \ No newline at end of file diff --git a/erpnext/accounts/doctype/south_africa_vat_account/south_africa_vat_account.py b/erpnext/accounts/doctype/south_africa_vat_account/south_africa_vat_account.py new file mode 100644 index 0000000000..4bd8c65a04 --- /dev/null +++ b/erpnext/accounts/doctype/south_africa_vat_account/south_africa_vat_account.py @@ -0,0 +1,8 @@ +# Copyright (c) 2021, Frappe Technologies Pvt. Ltd. and contributors +# For license information, please see license.txt + +# import frappe +from frappe.model.document import Document + +class SouthAfricaVATAccount(Document): + pass diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.json b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.json index f9160e281d..153906ffe9 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.json +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.json @@ -1,263 +1,151 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 1, - "allow_rename": 1, - "autoname": "Prompt", - "beta": 0, - "creation": "2018-04-13 18:42:06.431683", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, - "engine": "InnoDB", + "actions": [], + "allow_import": 1, + "allow_rename": 1, + "autoname": "Prompt", + "creation": "2018-04-13 18:42:06.431683", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "category_details_section", + "category_name", + "round_off_tax_amount", + "column_break_2", + "consider_party_ledger_amount", + "tax_on_excess_amount", + "section_break_8", + "rates", + "section_break_7", + "accounts" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "category_name", "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, "in_list_view": 1, - "in_standard_filter": 0, "label": "Category Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "show_days": 1, + "show_seconds": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "section_break_8", "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Tax Withholding Rates", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "show_days": 1, + "show_seconds": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, "fieldname": "rates", "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, "label": "Rates", - "length": 0, - "no_copy": 0, "options": "Tax Withholding Rate", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "show_days": 1, + "show_seconds": 1 }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "section_break_7", - "fieldtype": "Section Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, + "fieldname": "section_break_7", + "fieldtype": "Section Break", "label": "Account Details", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, + "show_days": 1, + "show_seconds": 1 + }, { - "allow_bulk_edit": 0, - "allow_in_quick_entry": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "accounts", - "fieldtype": "Table", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Accounts", - "length": 0, - "no_copy": 0, - "options": "Tax Withholding Account", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 1, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 + "fieldname": "accounts", + "fieldtype": "Table", + "label": "Accounts", + "options": "Tax Withholding Account", + "reqd": 1, + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "category_details_section", + "fieldtype": "Section Break", + "label": "Category Details", + "show_days": 1, + "show_seconds": 1 + }, + { + "fieldname": "column_break_2", + "fieldtype": "Column Break", + "show_days": 1, + "show_seconds": 1 + }, + { + "default": "0", + "description": "Even invoices with apply tax withholding unchecked will be considered for checking cumulative threshold breach", + "fieldname": "consider_party_ledger_amount", + "fieldtype": "Check", + "label": "Consider Entire Party Ledger Amount", + "show_days": 1, + "show_seconds": 1 + }, + { + "default": "0", + "description": "Tax will be withheld only for amount exceeding the cumulative threshold", + "fieldname": "tax_on_excess_amount", + "fieldtype": "Check", + "label": "Only Deduct Tax On Excess Amount ", + "show_days": 1, + "show_seconds": 1 + }, + { + "description": "Checking this will round off the tax amount to the nearest integer", + "fieldname": "round_off_tax_amount", + "fieldtype": "Check", + "label": "Round Off Tax Amount", + "show_days": 1, + "show_seconds": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-07-17 22:53:26.193179", - "modified_by": "Administrator", - "module": "Accounts", - "name": "Tax Withholding Category", - "name_case": "", - "owner": "Administrator", + ], + "index_web_pages_for_search": 1, + "links": [], + "modified": "2021-07-27 21:47:34.396071", + "modified_by": "Administrator", + "module": "Accounts", + "name": "Tax Withholding Category", + "owner": "Administrator", "permissions": [ { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "System Manager", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts Manager", + "share": 1, "write": 1 - }, + }, { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Accounts User", - "set_user_permissions": 0, - "share": 1, - "submit": 0, + "create": 1, + "delete": 1, + "email": 1, + "export": 1, + "print": 1, + "read": 1, + "report": 1, + "role": "Accounts User", + "share": 1, "write": 1 } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1, - "track_seen": 0, - "track_views": 0 + ], + "sort_field": "modified", + "sort_order": "DESC", + "track_changes": 1 } \ No newline at end of file diff --git a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py index b9ee4a0963..481ef285e7 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/tax_withholding_category.py @@ -6,7 +6,7 @@ from __future__ import unicode_literals import frappe from frappe import _ from frappe.model.document import Document -from frappe.utils import flt, getdate +from frappe.utils import flt, getdate, cint from erpnext.accounts.utils import get_fiscal_year class TaxWithholdingCategory(Document): @@ -86,7 +86,10 @@ def get_tax_withholding_details(tax_withholding_category, fiscal_year, company): "rate": tax_rate_detail.tax_withholding_rate, "threshold": tax_rate_detail.single_threshold, "cumulative_threshold": tax_rate_detail.cumulative_threshold, - "description": tax_withholding.category_name if tax_withholding.category_name else tax_withholding_category + "description": tax_withholding.category_name if tax_withholding.category_name else tax_withholding_category, + "consider_party_ledger_amount": tax_withholding.consider_party_ledger_amount, + "tax_on_excess_amount": tax_withholding.tax_on_excess_amount, + "round_off_tax_amount": tax_withholding.round_off_tax_amount }) def get_tax_withholding_rates(tax_withholding, fiscal_year): @@ -145,6 +148,7 @@ def get_lower_deduction_certificate(fiscal_year, pan_no): def get_tax_amount(party_type, parties, inv, tax_details, fiscal_year_details, pan_no=None): fiscal_year = fiscal_year_details[0] + vouchers = get_invoice_vouchers(parties, fiscal_year, inv.company, party_type=party_type) advance_vouchers = get_advance_vouchers(parties, fiscal_year, inv.company, party_type=party_type) taxable_vouchers = vouchers + advance_vouchers @@ -235,10 +239,18 @@ def get_deducted_tax(taxable_vouchers, fiscal_year, tax_details): def get_tds_amount(ldc, parties, inv, tax_details, fiscal_year_details, tax_deducted, vouchers): tds_amount = 0 + invoice_filters = { + 'name': ('in', vouchers), + 'docstatus': 1 + } - supp_credit_amt = frappe.db.get_value('Purchase Invoice', { - 'name': ('in', vouchers), 'docstatus': 1, 'apply_tds': 1 - }, 'sum(net_total)') or 0.0 + field = 'sum(net_total)' + + if not cint(tax_details.consider_party_ledger_amount): + invoice_filters.update({'apply_tds': 1}) + field = 'sum(grand_total)' + + supp_credit_amt = frappe.db.get_value('Purchase Invoice', invoice_filters, field) or 0.0 supp_jv_credit_amt = frappe.db.get_value('Journal Entry Account', { 'parent': ('in', vouchers), 'docstatus': 1, @@ -255,6 +267,13 @@ def get_tds_amount(ldc, parties, inv, tax_details, fiscal_year_details, tax_dedu cumulative_threshold = tax_details.get('cumulative_threshold', 0) if ((threshold and inv.net_total >= threshold) or (cumulative_threshold and supp_credit_amt >= cumulative_threshold)): + if (cumulative_threshold and supp_credit_amt >= cumulative_threshold) and cint(tax_details.tax_on_excess_amount): + # Get net total again as TDS is calculated on net total + # Grand is used to just check for threshold breach + net_total = frappe.db.get_value('Purchase Invoice', invoice_filters, 'sum(net_total)') or 0.0 + net_total += inv.net_total + supp_credit_amt = net_total - cumulative_threshold + if ldc and is_valid_certificate( ldc.valid_from, ldc.valid_upto, inv.get('posting_date') or inv.get('transaction_date'), tax_deducted, @@ -263,6 +282,9 @@ def get_tds_amount(ldc, parties, inv, tax_details, fiscal_year_details, tax_dedu tds_amount = get_ltds_amount(supp_credit_amt, 0, ldc.certificate_limit, ldc.rate, tax_details) else: tds_amount = supp_credit_amt * tax_details.rate / 100 if supp_credit_amt > 0 else 0 + + if cint(tax_details.round_off_tax_amount): + tds_amount = round(tds_amount) return tds_amount diff --git a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py index dd26be7c99..2ba22ca435 100644 --- a/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py +++ b/erpnext/accounts/doctype/tax_withholding_category/test_tax_withholding_category.py @@ -87,6 +87,31 @@ class TestTaxWithholdingCategory(unittest.TestCase): for d in invoices: d.cancel() + def test_tax_withholding_category_checks(self): + invoices = [] + frappe.db.set_value("Supplier", "Test TDS Supplier3", "tax_withholding_category", "New TDS Category") + + # First Invoice with no tds check + pi = create_purchase_invoice(supplier = "Test TDS Supplier3", rate = 20000, do_not_save=True) + pi.apply_tds = 0 + pi.save() + pi.submit() + invoices.append(pi) + + # Second Invoice will apply TDS checked + pi1 = create_purchase_invoice(supplier = "Test TDS Supplier3", rate = 20000) + pi1.submit() + invoices.append(pi1) + + # Cumulative threshold is 30000 + # Threshold calculation should be on both the invoices + # TDS should be applied only on 1000 + self.assertEqual(pi1.taxes[0].tax_amount, 1000) + + for d in invoices: + d.cancel() + + def test_cumulative_threshold_tcs(self): frappe.db.set_value("Customer", "Test TCS Customer", "tax_withholding_category", "Cumulative Threshold TCS") invoices = [] @@ -195,7 +220,7 @@ def create_sales_invoice(**args): def create_records(): # create a new suppliers - for name in ['Test TDS Supplier', 'Test TDS Supplier1', 'Test TDS Supplier2']: + for name in ['Test TDS Supplier', 'Test TDS Supplier1', 'Test TDS Supplier2', 'Test TDS Supplier3']: if frappe.db.exists('Supplier', name): continue @@ -311,3 +336,23 @@ def create_tax_with_holding_category(): 'account': 'TDS - _TC' }] }).insert() + + if not frappe.db.exists("Tax Withholding Category", "New TDS Category"): + frappe.get_doc({ + "doctype": "Tax Withholding Category", + "name": "New TDS Category", + "category_name": "New TDS Category", + "round_off_tax_amount": 1, + "consider_party_ledger_amount": 1, + "tax_on_excess_amount": 1, + "rates": [{ + 'fiscal_year': fiscal_year, + 'tax_withholding_rate': 10, + 'single_threshold': 0, + 'cumulative_threshold': 30000 + }], + "accounts": [{ + 'company': '_Test Company', + 'account': 'TDS - _TC' + }] + }).insert() diff --git a/erpnext/accounts/report/gross_profit/gross_profit.py b/erpnext/accounts/report/gross_profit/gross_profit.py index 84c74543da..6d8623c189 100644 --- a/erpnext/accounts/report/gross_profit/gross_profit.py +++ b/erpnext/accounts/report/gross_profit/gross_profit.py @@ -241,6 +241,7 @@ class GrossProfitGenerator(object): sle.voucher_detail_no == row.item_row: previous_stock_value = len(my_sle) > i+1 and \ flt(my_sle[i+1].stock_value) or 0.0 + if previous_stock_value: return (previous_stock_value - flt(sle.stock_value)) * flt(row.qty) / abs(flt(sle.qty)) else: @@ -335,7 +336,7 @@ class GrossProfitGenerator(object): res = frappe.db.sql("""select item_code, voucher_type, voucher_no, voucher_detail_no, stock_value, warehouse, actual_qty as qty from `tabStock Ledger Entry` - where company=%(company)s + where company=%(company)s and is_cancelled = 0 order by item_code desc, warehouse desc, posting_date desc, posting_time desc, creation desc""", self.filters, as_dict=True) diff --git a/erpnext/accounts/utils.py b/erpnext/accounts/utils.py index 9afe365f74..9272bc4fce 100644 --- a/erpnext/accounts/utils.py +++ b/erpnext/accounts/utils.py @@ -566,10 +566,10 @@ def remove_ref_doc_link_from_pe(ref_type, ref_no): frappe.msgprint(_("Payment Entries {0} are un-linked").format("\n".join(linked_pe))) @frappe.whitelist() -def get_company_default(company, fieldname): - value = frappe.get_cached_value('Company', company, fieldname) +def get_company_default(company, fieldname, ignore_validation=False): + value = frappe.get_cached_value('Company', company, fieldname) - if not value: + if not ignore_validation and not value: throw(_("Please set default {0} in Company {1}") .format(frappe.get_meta("Company").get_label(fieldname), company)) diff --git a/erpnext/change_log/v13/v13_8_0.md b/erpnext/change_log/v13/v13_8_0.md new file mode 100644 index 0000000000..98ed95ae04 --- /dev/null +++ b/erpnext/change_log/v13/v13_8_0.md @@ -0,0 +1,39 @@ +# Version 13.8.0 Release Notes + +### Features & Enhancements +- Report to show COGS by item groups ([#26222](https://github.com/frappe/erpnext/pull/26222)) +- Enhancements in TDS ([#26677](https://github.com/frappe/erpnext/pull/26677)) +- API Endpoint to update halted Razorpay subscriptions ([#26564](https://github.com/frappe/erpnext/pull/26564)) + +### Fixes +- Incorrect bom name ([#26600](https://github.com/frappe/erpnext/pull/26600)) +- Exchange rate revaluation posting date and precision fixes ([#26651](https://github.com/frappe/erpnext/pull/26651)) +- POS item cart dom updates ([#26460](https://github.com/frappe/erpnext/pull/26460)) +- General Ledger report not working with filter group by ([#26439](https://github.com/frappe/erpnext/pull/26438)) +- Tax calculation for Recurring additional salary ([#24206](https://github.com/frappe/erpnext/pull/24206)) +- Validation check for batch for stock reconciliation type in stock entry ([#26487](https://github.com/frappe/erpnext/pull/26487)) +- Improved UX for additional discount field ([#26502](https://github.com/frappe/erpnext/pull/26502)) +- Add missing cess amount in GSTR-3B report ([#26644](https://github.com/frappe/erpnext/pull/26644)) +- Optimized code for reposting item valuation ([#26431](https://github.com/frappe/erpnext/pull/26431)) +- FG item not fetched in manufacture entry ([#26508](https://github.com/frappe/erpnext/pull/26508)) +- Errors on parallel requests creation of company for India ([#26420](https://github.com/frappe/erpnext/pull/26420)) +- Incorrect valuation rate calculation in gross profit report ([#26558](https://github.com/frappe/erpnext/pull/26558)) +- Empty "against account" in Purchase Receipt GLE ([#26712](https://github.com/frappe/erpnext/pull/26712)) +- Remove cancelled entries from Stock and Account Value comparison report ([#26721](https://github.com/frappe/erpnext/pull/26721)) +- Remove manual permission checking ([#26691](https://github.com/frappe/erpnext/pull/26691)) +- Delete child docs when parent doc is deleted ([#26518](https://github.com/frappe/erpnext/pull/26518)) +- GST Reports timeout issue ([#26646](https://github.com/frappe/erpnext/pull/26646)) +- Parent condition in pricing rules ([#26727](https://github.com/frappe/erpnext/pull/26727)) +- Added Company filters for Loan ([#26294](https://github.com/frappe/erpnext/pull/26294)) +- Incorrect discount amount on amended document ([#26292](https://github.com/frappe/erpnext/pull/26292)) +- Exchange gain loss not set for advances linked with invoices ([#26436](https://github.com/frappe/erpnext/pull/26436)) +- Unallocated amount in Payment Entry after taxes ([#26412](https://github.com/frappe/erpnext/pull/26412)) +- Wrong operation time in Work Order ([#26613](https://github.com/frappe/erpnext/pull/26613)) +- Serial No and Batch validation ([#26614](https://github.com/frappe/erpnext/pull/26614)) +- Gl Entries for exchange gain loss ([#26734](https://github.com/frappe/erpnext/pull/26734)) +- TDS computation summary shows cancelled invoices ([#26485](https://github.com/frappe/erpnext/pull/26485)) +- Price List rate not fetched for return sales invoice fixed ([#26560](https://github.com/frappe/erpnext/pull/26560)) +- Included company in link document type filters for contact ([#26576](https://github.com/frappe/erpnext/pull/26576)) +- Ignore mandatory fields while creating payment reconciliation Journal Entry ([#26643](https://github.com/frappe/erpnext/pull/26643)) +- Unable to download GSTR-1 json ([#26418](https://github.com/frappe/erpnext/pull/26418)) +- Paging buttons not working on item group portal page ([#26498](https://github.com/frappe/erpnext/pull/26498)) diff --git a/erpnext/controllers/accounts_controller.py b/erpnext/controllers/accounts_controller.py index cdd865ac4a..bf4ab1a848 100644 --- a/erpnext/controllers/accounts_controller.py +++ b/erpnext/controllers/accounts_controller.py @@ -674,19 +674,24 @@ class AccountsController(TransactionBase): if self.get('doctype') in ['Purchase Invoice', 'Sales Invoice']: for d in self.get("advances"): if d.exchange_gain_loss: - party = self.supplier if self.get('doctype') == 'Purchase Invoice' else self.customer - party_account = self.credit_to if self.get('doctype') == 'Purchase Invoice' else self.debit_to - party_type = "Supplier" if self.get('doctype') == 'Purchase Invoice' else "Customer" + is_purchase_invoice = self.get('doctype') == 'Purchase Invoice' + party = self.supplier if is_purchase_invoice else self.customer + party_account = self.credit_to if is_purchase_invoice else self.debit_to + party_type = "Supplier" if is_purchase_invoice else "Customer" gain_loss_account = frappe.db.get_value('Company', self.company, 'exchange_gain_loss_account') + if not gain_loss_account: + frappe.throw(_("Please set Default Exchange Gain/Loss Account in Company {}") + .format(self.get('company'))) account_currency = get_account_currency(gain_loss_account) if account_currency != self.company_currency: - frappe.throw(_("Currency for {0} must be {1}").format(d.account, self.company_currency)) + frappe.throw(_("Currency for {0} must be {1}").format(gain_loss_account, self.company_currency)) # for purchase dr_or_cr = 'debit' if d.exchange_gain_loss > 0 else 'credit' - # just reverse for sales? - dr_or_cr = 'debit' if dr_or_cr == 'credit' else 'credit' + if not is_purchase_invoice: + # just reverse for sales? + dr_or_cr = 'debit' if dr_or_cr == 'credit' else 'credit' gl_entries.append( self.get_gl_dict({ @@ -904,9 +909,9 @@ class AccountsController(TransactionBase): frappe.throw(_("Cannot overbill for Item {0} in row {1} more than {2}. To allow over-billing, please set allowance in Accounts Settings") .format(item.item_code, item.idx, max_allowed_amt)) - def get_company_default(self, fieldname): + def get_company_default(self, fieldname, ignore_validation=False): from erpnext.accounts.utils import get_company_default - return get_company_default(self.company, fieldname) + return get_company_default(self.company, fieldname, ignore_validation=ignore_validation) def get_stock_items(self): stock_items = [] diff --git a/erpnext/controllers/queries.py b/erpnext/controllers/queries.py index 280319321f..21c052a391 100644 --- a/erpnext/controllers/queries.py +++ b/erpnext/controllers/queries.py @@ -407,6 +407,7 @@ def get_batch_no(doctype, txt, searchfield, start, page_len, filters): INNER JOIN `tabBatch` batch on sle.batch_no = batch.name where batch.disabled = 0 + and sle.is_cancelled = 0 and sle.item_code = %(item_code)s and sle.warehouse = %(warehouse)s and (sle.batch_no like %(txt)s diff --git a/erpnext/crm/doctype/opportunity/opportunity.js b/erpnext/crm/doctype/opportunity/opportunity.js index 43e1b99f3a..e9a7a95fc7 100644 --- a/erpnext/crm/doctype/opportunity/opportunity.js +++ b/erpnext/crm/doctype/opportunity/opportunity.js @@ -53,6 +53,13 @@ frappe.ui.form.on("Opportunity", { frm.get_field("items").grid.set_multiple_add("item_code", "qty"); }, + status:function(frm){ + if (frm.doc.status == "Lost"){ + frm.trigger('set_as_lost_dialog'); + } + + }, + customer_address: function(frm, cdt, cdn) { erpnext.utils.get_address_display(frm, 'customer_address', 'address_display', false); }, @@ -91,11 +98,6 @@ frappe.ui.form.on("Opportunity", { frm.add_custom_button(__('Quotation'), cur_frm.cscript.create_quotation, __('Create')); - if(doc.status!=="Quotation") { - frm.add_custom_button(__('Lost'), () => { - frm.trigger('set_as_lost_dialog'); - }); - } } if(!frm.doc.__islocal && frm.perm[0].write && frm.doc.docstatus==0) { diff --git a/erpnext/education/api.py b/erpnext/education/api.py index afa0be9b9f..4493a3fef1 100644 --- a/erpnext/education/api.py +++ b/erpnext/education/api.py @@ -34,11 +34,14 @@ def enroll_student(source_name): } }}, ignore_permissions=True) student.save() + + student_applicant = frappe.db.get_value("Student Applicant", source_name, + ["student_category", "program"], as_dict=True) program_enrollment = frappe.new_doc("Program Enrollment") program_enrollment.student = student.name - program_enrollment.student_category = student.student_category + program_enrollment.student_category = student_applicant.student_category program_enrollment.student_name = student.title - program_enrollment.program = frappe.db.get_value("Student Applicant", source_name, "program") + program_enrollment.program = student_applicant.program frappe.publish_realtime('enroll_student_progress', {"progress": [2, 4]}, user=frappe.session.user) return program_enrollment diff --git a/erpnext/education/doctype/program_enrollment_tool_student/program_enrollment_tool_student.json b/erpnext/education/doctype/program_enrollment_tool_student/program_enrollment_tool_student.json index 9be292b65e..1d7497387f 100644 --- a/erpnext/education/doctype/program_enrollment_tool_student/program_enrollment_tool_student.json +++ b/erpnext/education/doctype/program_enrollment_tool_student/program_enrollment_tool_student.json @@ -1,195 +1,68 @@ { - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2016-06-10 03:29:02.539914", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "", - "editable_grid": 1, + "actions": [], + "creation": "2016-06-10 03:29:02.539914", + "doctype": "DocType", + "editable_grid": 1, + "engine": "InnoDB", + "field_order": [ + "student_applicant", + "student", + "student_name", + "column_break_3", + "student_batch_name", + "student_category" + ], "fields": [ { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "student_applicant", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Student Applicant", - "length": 0, - "no_copy": 0, - "options": "Student Applicant", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "student_applicant", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Student Applicant", + "options": "Student Applicant" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "depends_on": "", - "fieldname": "student", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Student", - "length": 0, - "no_copy": 0, - "options": "Student", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "student", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Student", + "options": "Student" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "column_break_3", - "fieldtype": "Column Break", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "column_break_3", + "fieldtype": "Column Break" + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "student_name", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Student Name", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 - }, + "fieldname": "student_name", + "fieldtype": "Data", + "in_list_view": 1, + "label": "Student Name", + "read_only": 1 + }, { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "student_batch_name", - "fieldtype": "Link", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 1, - "in_standard_filter": 0, - "label": "Student Batch Name", - "length": 0, - "no_copy": 0, - "options": "Student Batch Name", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 0, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "unique": 0 + "fieldname": "student_batch_name", + "fieldtype": "Link", + "in_list_view": 1, + "label": "Student Batch Name", + "options": "Student Batch Name" + }, + { + "fieldname": "student_category", + "fieldtype": "Link", + "label": "Student Category", + "options": "Student Category", + "read_only": 1 } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 0, - "is_submittable": 0, - "issingle": 0, - "istable": 1, - "max_attachments": 0, - "modified": "2018-01-02 12:03:53.890741", - "modified_by": "Administrator", - "module": "Education", - "name": "Program Enrollment Tool Student", - "name_case": "", - "owner": "Administrator", - "permissions": [], - "quick_entry": 1, - "read_only": 0, - "read_only_onload": 0, - "restrict_to_domain": "Education", - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 0, - "track_seen": 0 + ], + "istable": 1, + "links": [], + "modified": "2021-07-29 18:19:54.471594", + "modified_by": "Administrator", + "module": "Education", + "name": "Program Enrollment Tool Student", + "owner": "Administrator", + "permissions": [], + "quick_entry": 1, + "restrict_to_domain": "Education", + "sort_field": "modified", + "sort_order": "DESC" } \ No newline at end of file diff --git a/erpnext/erpnext_integrations/connectors/shopify_connection.py b/erpnext/erpnext_integrations/connectors/shopify_connection.py deleted file mode 100644 index 5d5b2e19ce..0000000000 --- a/erpnext/erpnext_integrations/connectors/shopify_connection.py +++ /dev/null @@ -1,353 +0,0 @@ -from __future__ import unicode_literals -import frappe -from frappe import _ -import json -from frappe.utils import cstr, cint, nowdate, getdate, flt, get_request_session, get_datetime -from erpnext.erpnext_integrations.utils import validate_webhooks_request -from erpnext.selling.doctype.sales_order.sales_order import make_delivery_note, make_sales_invoice -from erpnext.erpnext_integrations.doctype.shopify_settings.sync_product import sync_item_from_shopify -from erpnext.erpnext_integrations.doctype.shopify_settings.sync_customer import create_customer -from erpnext.erpnext_integrations.doctype.shopify_log.shopify_log import make_shopify_log, dump_request_data -from erpnext.erpnext_integrations.doctype.shopify_settings.shopify_settings import get_shopify_url, get_header - -@frappe.whitelist(allow_guest=True) -@validate_webhooks_request("Shopify Settings", 'X-Shopify-Hmac-Sha256', secret_key='shared_secret') -def store_request_data(order=None, event=None): - if frappe.request: - order = json.loads(frappe.request.data) - event = frappe.request.headers.get('X-Shopify-Topic') - - dump_request_data(order, event) - -def sync_sales_order(order, request_id=None, old_order_sync=False): - frappe.set_user('Administrator') - shopify_settings = frappe.get_doc("Shopify Settings") - frappe.flags.request_id = request_id - - if not frappe.db.get_value("Sales Order", filters={"shopify_order_id": cstr(order['id'])}): - try: - validate_customer(order, shopify_settings) - validate_item(order, shopify_settings) - create_order(order, shopify_settings, old_order_sync=old_order_sync) - except Exception as e: - make_shopify_log(status="Error", exception=e) - - else: - make_shopify_log(status="Success") - -def prepare_sales_invoice(order, request_id=None): - frappe.set_user('Administrator') - shopify_settings = frappe.get_doc("Shopify Settings") - frappe.flags.request_id = request_id - - try: - sales_order = get_sales_order(cstr(order['id'])) - if sales_order: - create_sales_invoice(order, shopify_settings, sales_order) - make_shopify_log(status="Success") - except Exception as e: - make_shopify_log(status="Error", exception=e, rollback=True) - -def prepare_delivery_note(order, request_id=None): - frappe.set_user('Administrator') - shopify_settings = frappe.get_doc("Shopify Settings") - frappe.flags.request_id = request_id - - try: - sales_order = get_sales_order(cstr(order['id'])) - if sales_order: - create_delivery_note(order, shopify_settings, sales_order) - make_shopify_log(status="Success") - except Exception as e: - make_shopify_log(status="Error", exception=e, rollback=True) - -def get_sales_order(shopify_order_id): - sales_order = frappe.db.get_value("Sales Order", filters={"shopify_order_id": shopify_order_id}) - if sales_order: - so = frappe.get_doc("Sales Order", sales_order) - return so - -def validate_customer(order, shopify_settings): - customer_id = order.get("customer", {}).get("id") - if customer_id: - if not frappe.db.get_value("Customer", {"shopify_customer_id": customer_id}, "name"): - create_customer(order.get("customer"), shopify_settings) - -def validate_item(order, shopify_settings): - for item in order.get("line_items"): - if item.get("product_id") and not frappe.db.get_value("Item", {"shopify_product_id": item.get("product_id")}, "name"): - sync_item_from_shopify(shopify_settings, item) - -def create_order(order, shopify_settings, old_order_sync=False, company=None): - so = create_sales_order(order, shopify_settings, company) - if so: - if order.get("financial_status") == "paid": - create_sales_invoice(order, shopify_settings, so, old_order_sync=old_order_sync) - - if order.get("fulfillments") and not old_order_sync: - create_delivery_note(order, shopify_settings, so) - -def create_sales_order(shopify_order, shopify_settings, company=None): - product_not_exists = [] - customer = frappe.db.get_value("Customer", {"shopify_customer_id": shopify_order.get("customer", {}).get("id")}, "name") - so = frappe.db.get_value("Sales Order", {"shopify_order_id": shopify_order.get("id")}, "name") - - if not so: - items = get_order_items(shopify_order.get("line_items"), shopify_settings, getdate(shopify_order.get('created_at'))) - - if not items: - message = 'Following items exists in the shopify order but relevant records were not found in the shopify Product master' - message += "\n" + ", ".join(product_not_exists) - - make_shopify_log(status="Error", exception=message, rollback=True) - - return '' - - so = frappe.get_doc({ - "doctype": "Sales Order", - "naming_series": shopify_settings.sales_order_series or "SO-Shopify-", - "shopify_order_id": shopify_order.get("id"), - "shopify_order_number": shopify_order.get("name"), - "customer": customer or shopify_settings.default_customer, - "transaction_date": getdate(shopify_order.get("created_at")) or nowdate(), - "delivery_date": getdate(shopify_order.get("created_at")) or nowdate(), - "company": shopify_settings.company, - "selling_price_list": shopify_settings.price_list, - "ignore_pricing_rule": 1, - "items": items, - "taxes": get_order_taxes(shopify_order, shopify_settings), - "apply_discount_on": "Grand Total", - "discount_amount": get_discounted_amount(shopify_order), - }) - - if company: - so.update({ - "company": company, - "status": "Draft" - }) - so.flags.ignore_mandatory = True - so.save(ignore_permissions=True) - so.submit() - - else: - so = frappe.get_doc("Sales Order", so) - - frappe.db.commit() - return so - -def create_sales_invoice(shopify_order, shopify_settings, so, old_order_sync=False): - if not frappe.db.get_value("Sales Invoice", {"shopify_order_id": shopify_order.get("id")}, "name")\ - and so.docstatus==1 and not so.per_billed and cint(shopify_settings.sync_sales_invoice): - - if old_order_sync: - posting_date = getdate(shopify_order.get('created_at')) - else: - posting_date = nowdate() - - si = make_sales_invoice(so.name, ignore_permissions=True) - si.shopify_order_id = shopify_order.get("id") - si.shopify_order_number = shopify_order.get("name") - si.set_posting_time = 1 - si.posting_date = posting_date - si.due_date = posting_date - si.naming_series = shopify_settings.sales_invoice_series or "SI-Shopify-" - si.flags.ignore_mandatory = True - set_cost_center(si.items, shopify_settings.cost_center) - si.insert(ignore_mandatory=True) - si.submit() - make_payament_entry_against_sales_invoice(si, shopify_settings, posting_date) - frappe.db.commit() - -def set_cost_center(items, cost_center): - for item in items: - item.cost_center = cost_center - -def make_payament_entry_against_sales_invoice(doc, shopify_settings, posting_date=None): - from erpnext.accounts.doctype.payment_entry.payment_entry import get_payment_entry - payment_entry = get_payment_entry(doc.doctype, doc.name, bank_account=shopify_settings.cash_bank_account) - payment_entry.flags.ignore_mandatory = True - payment_entry.reference_no = doc.name - payment_entry.posting_date = posting_date or nowdate() - payment_entry.reference_date = posting_date or nowdate() - payment_entry.insert(ignore_permissions=True) - payment_entry.submit() - -def create_delivery_note(shopify_order, shopify_settings, so): - if not cint(shopify_settings.sync_delivery_note): - return - - for fulfillment in shopify_order.get("fulfillments"): - if not frappe.db.get_value("Delivery Note", {"shopify_fulfillment_id": fulfillment.get("id")}, "name")\ - and so.docstatus==1: - - dn = make_delivery_note(so.name) - dn.shopify_order_id = fulfillment.get("order_id") - dn.shopify_order_number = shopify_order.get("name") - dn.set_posting_time = 1 - dn.posting_date = getdate(fulfillment.get("created_at")) - dn.shopify_fulfillment_id = fulfillment.get("id") - dn.naming_series = shopify_settings.delivery_note_series or "DN-Shopify-" - dn.items = get_fulfillment_items(dn.items, fulfillment.get("line_items"), shopify_settings) - dn.flags.ignore_mandatory = True - dn.save() - dn.submit() - frappe.db.commit() - -def get_fulfillment_items(dn_items, fulfillment_items, shopify_settings): - return [dn_item.update({"qty": item.get("quantity")}) for item in fulfillment_items for dn_item in dn_items\ - if get_item_code(item) == dn_item.item_code] - -def get_discounted_amount(order): - discounted_amount = 0.0 - for discount in order.get("discount_codes"): - discounted_amount += flt(discount.get("amount")) - return discounted_amount - -def get_order_items(order_items, shopify_settings, delivery_date): - items = [] - all_product_exists = True - product_not_exists = [] - - for shopify_item in order_items: - if not shopify_item.get('product_exists'): - all_product_exists = False - product_not_exists.append({'title':shopify_item.get('title'), - 'shopify_order_id': shopify_item.get('id')}) - continue - - if all_product_exists: - item_code = get_item_code(shopify_item) - items.append({ - "item_code": item_code, - "item_name": shopify_item.get("name"), - "rate": shopify_item.get("price"), - "delivery_date": delivery_date, - "qty": shopify_item.get("quantity"), - "stock_uom": shopify_item.get("uom") or _("Nos"), - "warehouse": shopify_settings.warehouse - }) - else: - items = [] - - return items - -def get_item_code(shopify_item): - item_code = frappe.db.get_value("Item", {"shopify_variant_id": shopify_item.get("variant_id")}, "item_code") - if not item_code: - item_code = frappe.db.get_value("Item", {"shopify_product_id": shopify_item.get("product_id")}, "item_code") - if not item_code: - item_code = frappe.db.get_value("Item", {"item_name": shopify_item.get("title")}, "item_code") - - return item_code - -def get_order_taxes(shopify_order, shopify_settings): - taxes = [] - for tax in shopify_order.get("tax_lines"): - taxes.append({ - "charge_type": _("On Net Total"), - "account_head": get_tax_account_head(tax), - "description": "{0} - {1}%".format(tax.get("title"), tax.get("rate") * 100.0), - "rate": tax.get("rate") * 100.00, - "included_in_print_rate": 1 if shopify_order.get("taxes_included") else 0, - "cost_center": shopify_settings.cost_center - }) - - taxes = update_taxes_with_shipping_lines(taxes, shopify_order.get("shipping_lines"), shopify_settings) - - return taxes - -def update_taxes_with_shipping_lines(taxes, shipping_lines, shopify_settings): - """Shipping lines represents the shipping details, - each such shipping detail consists of a list of tax_lines""" - for shipping_charge in shipping_lines: - if shipping_charge.get("price"): - taxes.append({ - "charge_type": _("Actual"), - "account_head": get_tax_account_head(shipping_charge), - "description": shipping_charge["title"], - "tax_amount": shipping_charge["price"], - "cost_center": shopify_settings.cost_center - }) - - for tax in shipping_charge.get("tax_lines"): - taxes.append({ - "charge_type": _("Actual"), - "account_head": get_tax_account_head(tax), - "description": tax["title"], - "tax_amount": tax["price"], - "cost_center": shopify_settings.cost_center - }) - - return taxes - -def get_tax_account_head(tax): - tax_title = tax.get("title").encode("utf-8") - - tax_account = frappe.db.get_value("Shopify Tax Account", \ - {"parent": "Shopify Settings", "shopify_tax": tax_title}, "tax_account") - - if not tax_account: - frappe.throw(_("Tax Account not specified for Shopify Tax {0}").format(tax.get("title"))) - - return tax_account - -@frappe.whitelist(allow_guest=True) -def sync_old_orders(): - frappe.set_user('Administrator') - shopify_settings = frappe.get_doc('Shopify Settings') - - if not shopify_settings.sync_missing_orders: - return - - url = get_url(shopify_settings) - session = get_request_session() - - try: - res = session.get(url, headers=get_header(shopify_settings)) - res.raise_for_status() - orders = res.json()["orders"] - - for order in orders: - if is_sync_complete(shopify_settings, order): - stop_sync(shopify_settings) - return - - sync_sales_order(order=order, old_order_sync=True) - last_order_id = order.get('id') - - if last_order_id: - shopify_settings.load_from_db() - shopify_settings.last_order_id = last_order_id - shopify_settings.save() - frappe.db.commit() - - except Exception as e: - raise e - -def stop_sync(shopify_settings): - shopify_settings.sync_missing_orders = 0 - shopify_settings.last_order_id = '' - shopify_settings.save() - frappe.db.commit() - -def get_url(shopify_settings): - last_order_id = shopify_settings.last_order_id - - if not last_order_id: - if shopify_settings.sync_based_on == 'Date': - url = get_shopify_url("admin/api/2021-04/orders.json?limit=250&created_at_min={0}&since_id=0".format( - get_datetime(shopify_settings.from_date)), shopify_settings) - else: - url = get_shopify_url("admin/api/2021-04/orders.json?limit=250&since_id={0}".format( - shopify_settings.from_order_id), shopify_settings) - else: - url = get_shopify_url("admin/api/2021-04/orders.json?limit=250&since_id={0}".format(last_order_id), shopify_settings) - - return url - -def is_sync_complete(shopify_settings, order): - if shopify_settings.sync_based_on == 'Date': - return getdate(shopify_settings.to_date) < getdate(order.get('created_at')) - else: - return cstr(order.get('id')) == cstr(shopify_settings.to_order_id) - diff --git a/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log.js b/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log.js deleted file mode 100644 index d3fe7d2b4d..0000000000 --- a/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log.js +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright (c) 2016, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt - -frappe.ui.form.on('Shopify Log', { - refresh: function(frm) { - if (frm.doc.request_data && frm.doc.status=='Error'){ - frm.add_custom_button('Resync', function() { - frappe.call({ - method:"erpnext.erpnext_integrations.doctype.shopify_log.shopify_log.resync", - args:{ - method:frm.doc.method, - name: frm.doc.name, - request_data: frm.doc.request_data - }, - callback: function(r){ - frappe.msgprint(__("Order rescheduled for sync")) - } - }) - }).addClass('btn-primary'); - } - } -}); diff --git a/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log.json b/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log.json deleted file mode 100644 index ab373eedb4..0000000000 --- a/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log.json +++ /dev/null @@ -1,268 +0,0 @@ -{ - "allow_copy": 0, - "allow_guest_to_view": 0, - "allow_import": 0, - "allow_rename": 0, - "beta": 0, - "creation": "2016-03-14 10:02:06.227184", - "custom": 0, - "docstatus": 0, - "doctype": "DocType", - "document_type": "System", - "editable_grid": 0, - "fields": [ - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "title", - "fieldtype": "Data", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Title", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "default": "Queued", - "fieldname": "status", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Status", - "length": 0, - "no_copy": 0, - "options": "", - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "method", - "fieldtype": "Small Text", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Method", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "message", - "fieldtype": "Code", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Message", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "traceback", - "fieldtype": "Code", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Traceback", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - }, - { - "allow_bulk_edit": 0, - "allow_on_submit": 0, - "bold": 0, - "collapsible": 0, - "columns": 0, - "fieldname": "request_data", - "fieldtype": "Code", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_global_search": 0, - "in_list_view": 0, - "in_standard_filter": 0, - "label": "Request Data", - "length": 0, - "no_copy": 0, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "read_only": 1, - "remember_last_selected_value": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "set_only_once": 0, - "translatable": 0, - "unique": 0 - } - ], - "has_web_view": 0, - "hide_heading": 0, - "hide_toolbar": 0, - "idx": 0, - "image_view": 0, - "in_create": 1, - "is_submittable": 0, - "issingle": 0, - "istable": 0, - "max_attachments": 0, - "modified": "2018-04-20 16:23:36.862381", - "modified_by": "Administrator", - "module": "ERPNext Integrations", - "name": "Shopify Log", - "name_case": "", - "owner": "Administrator", - "permissions": [ - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "Administrator", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - }, - { - "amend": 0, - "cancel": 0, - "create": 1, - "delete": 1, - "email": 1, - "export": 1, - "if_owner": 0, - "import": 0, - "permlevel": 0, - "print": 1, - "read": 1, - "report": 1, - "role": "System Manager", - "set_user_permissions": 0, - "share": 1, - "submit": 0, - "write": 1 - } - ], - "quick_entry": 0, - "read_only": 0, - "read_only_onload": 0, - "show_name_in_global_search": 0, - "sort_field": "modified", - "sort_order": "DESC", - "title_field": "title", - "track_changes": 0, - "track_seen": 0 -} \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log.py b/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log.py deleted file mode 100644 index a2b6af99b2..0000000000 --- a/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log.py +++ /dev/null @@ -1,68 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -import json -from frappe.model.document import Document -from erpnext.erpnext_integrations.utils import get_webhook_address - -class ShopifyLog(Document): - pass - - -def make_shopify_log(status="Queued", exception=None, rollback=False): - # if name not provided by log calling method then fetch existing queued state log - make_new = False - - if not frappe.flags.request_id: - make_new = True - - if rollback: - frappe.db.rollback() - - if make_new: - log = frappe.get_doc({"doctype":"Shopify Log"}).insert(ignore_permissions=True) - else: - log = log = frappe.get_doc("Shopify Log", frappe.flags.request_id) - - log.message = get_message(exception) - log.traceback = frappe.get_traceback() - log.status = status - log.save(ignore_permissions=True) - frappe.db.commit() - -def get_message(exception): - message = None - - if hasattr(exception, 'message'): - message = exception.message - elif hasattr(exception, '__str__'): - message = exception.__str__() - else: - message = "Something went wrong while syncing" - return message - -def dump_request_data(data, event="create/order"): - event_mapper = { - "orders/create": get_webhook_address(connector_name='shopify_connection', method="sync_sales_order", exclude_uri=True), - "orders/paid" : get_webhook_address(connector_name='shopify_connection', method="prepare_sales_invoice", exclude_uri=True), - "orders/fulfilled": get_webhook_address(connector_name='shopify_connection', method="prepare_delivery_note", exclude_uri=True) - } - - log = frappe.get_doc({ - "doctype": "Shopify Log", - "request_data": json.dumps(data, indent=1), - "method": event_mapper[event] - }).insert(ignore_permissions=True) - - frappe.db.commit() - frappe.enqueue(method=event_mapper[event], queue='short', timeout=300, is_async=True, - **{"order": data, "request_id": log.name}) - -@frappe.whitelist() -def resync(method, name, request_data): - frappe.db.set_value("Shopify Log", name, "status", "Queued", update_modified=False) - frappe.enqueue(method=method, queue='short', timeout=300, is_async=True, - **{"order": json.loads(request_data), "request_id": name}) diff --git a/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log_list.js b/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log_list.js deleted file mode 100644 index 0913ce4ef3..0000000000 --- a/erpnext/erpnext_integrations/doctype/shopify_log/shopify_log_list.js +++ /dev/null @@ -1,12 +0,0 @@ -frappe.listview_settings['Shopify Log'] = { - add_fields: ["status"], - get_indicator: function(doc) { - if(doc.status==="Success"){ - return [__("Success"), "green", "status,=,Success"]; - } else if(doc.status ==="Error"){ - return [__("Error"), "red", "status,=,Error"]; - } else if(doc.status ==="Queued"){ - return [__("Queued"), "orange", "status,=,Queued"]; - } - } -} diff --git a/erpnext/erpnext_integrations/doctype/shopify_log/test_shopify_log.js b/erpnext/erpnext_integrations/doctype/shopify_log/test_shopify_log.js deleted file mode 100644 index d22b6d5240..0000000000 --- a/erpnext/erpnext_integrations/doctype/shopify_log/test_shopify_log.js +++ /dev/null @@ -1,23 +0,0 @@ -/* eslint-disable */ -// rename this file from _test_[name] to test_[name] to activate -// and remove above this line - -QUnit.test("test: Shopify Log", function (assert) { - let done = assert.async(); - - // number of asserts - assert.expect(1); - - frappe.run_serially([ - // insert a new Shopify Log - () => frappe.tests.make('Shopify Log', [ - // values to be set - {key: 'value'} - ]), - () => { - assert.equal(cur_frm.doc.key, 'value'); - }, - () => done() - ]); - -}); diff --git a/erpnext/erpnext_integrations/doctype/shopify_log/test_shopify_log.py b/erpnext/erpnext_integrations/doctype/shopify_log/test_shopify_log.py deleted file mode 100644 index 5892e1d6c4..0000000000 --- a/erpnext/erpnext_integrations/doctype/shopify_log/test_shopify_log.py +++ /dev/null @@ -1,12 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt -from __future__ import unicode_literals - -import frappe -import unittest - -# test_records = frappe.get_test_records('Shopify Log') - -class TestShopifyLog(unittest.TestCase): - pass diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.js b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.js deleted file mode 100644 index 1574795dfa..0000000000 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.js +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors -// License: GNU General Public License v3. See license.txt - -frappe.provide("erpnext_integrations.shopify_settings"); - -frappe.ui.form.on("Shopify Settings", "onload", function(frm){ - frappe.call({ - method:"erpnext.erpnext_integrations.doctype.shopify_settings.shopify_settings.get_series", - callback:function(r){ - $.each(r.message, function(key, value){ - set_field_options(key, value); - }); - } - }); - erpnext_integrations.shopify_settings.setup_queries(frm); -}) - -frappe.ui.form.on("Shopify Settings", "app_type", function(frm) { - frm.toggle_reqd("api_key", (frm.doc.app_type == "Private")); - frm.toggle_reqd("password", (frm.doc.app_type == "Private")); -}) - -frappe.ui.form.on("Shopify Settings", "refresh", function(frm){ - if(!frm.doc.__islocal && frm.doc.enable_shopify === 1){ - frm.toggle_reqd("price_list", true); - frm.toggle_reqd("warehouse", true); - frm.toggle_reqd("taxes", true); - frm.toggle_reqd("company", true); - frm.toggle_reqd("cost_center", true); - frm.toggle_reqd("cash_bank_account", true); - frm.toggle_reqd("sales_order_series", true); - frm.toggle_reqd("customer_group", true); - frm.toggle_reqd("shared_secret", true); - - frm.toggle_reqd("sales_invoice_series", frm.doc.sync_sales_invoice); - frm.toggle_reqd("delivery_note_series", frm.doc.sync_delivery_note); - - } -}) - -$.extend(erpnext_integrations.shopify_settings, { - setup_queries: function(frm) { - frm.fields_dict["warehouse"].get_query = function(doc) { - return { - filters:{ - "company": doc.company, - "is_group": "No" - } - } - } - - frm.fields_dict["taxes"].grid.get_field("tax_account").get_query = function(doc){ - return { - "query": "erpnext.controllers.queries.tax_account_query", - "filters": { - "account_type": ["Tax", "Chargeable", "Expense Account"], - "company": doc.company - } - } - } - - frm.fields_dict["cash_bank_account"].get_query = function(doc) { - return { - filters: [ - ["Account", "account_type", "in", ["Cash", "Bank"]], - ["Account", "root_type", "=", "Asset"], - ["Account", "is_group", "=",0], - ["Account", "company", "=", doc.company] - ] - } - } - - frm.fields_dict["cost_center"].get_query = function(doc) { - return { - filters:{ - "company": doc.company, - "is_group": "No" - } - } - } - - frm.fields_dict["price_list"].get_query = function() { - return { - filters:{ - "selling": 1 - } - } - } - } -}) diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json deleted file mode 100644 index 308e7d163f..0000000000 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.json +++ /dev/null @@ -1,353 +0,0 @@ -{ - "actions": [], - "creation": "2015-05-18 05:21:07.270859", - "doctype": "DocType", - "document_type": "System", - "engine": "InnoDB", - "field_order": [ - "status_html", - "enable_shopify", - "app_type", - "column_break_4", - "last_sync_datetime", - "section_break_2", - "shopify_url", - "api_key", - "column_break_3", - "password", - "shared_secret", - "access_token", - "section_break_38", - "webhooks", - "section_break_15", - "default_customer", - "column_break_19", - "customer_group", - "company_dependent_settings", - "company", - "cash_bank_account", - "column_break_20", - "cost_center", - "erp_settings", - "price_list", - "update_price_in_erpnext_price_list", - "column_break_26", - "warehouse", - "section_break_25", - "sales_order_series", - "column_break_27", - "sync_delivery_note", - "delivery_note_series", - "sync_sales_invoice", - "sales_invoice_series", - "section_break_22", - "html_16", - "taxes", - "syncing_details_section", - "sync_missing_orders", - "sync_based_on", - "column_break_41", - "from_date", - "to_date", - "from_order_id", - "to_order_id", - "last_order_id" - ], - "fields": [ - { - "fieldname": "status_html", - "fieldtype": "HTML", - "label": "status html", - "read_only": 1 - }, - { - "default": "0", - "fieldname": "enable_shopify", - "fieldtype": "Check", - "label": "Enable Shopify" - }, - { - "default": "Private", - "fieldname": "app_type", - "fieldtype": "Data", - "in_list_view": 1, - "label": "App Type", - "read_only": 1, - "reqd": 1 - }, - { - "fieldname": "column_break_4", - "fieldtype": "Column Break" - }, - { - "fieldname": "last_sync_datetime", - "fieldtype": "Datetime", - "label": "Last Sync Datetime", - "read_only": 1 - }, - { - "fieldname": "section_break_2", - "fieldtype": "Section Break" - }, - { - "description": "eg: frappe.myshopify.com", - "fieldname": "shopify_url", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Shop URL", - "reqd": 1 - }, - { - "depends_on": "eval:doc.app_type==\"Private\"", - "fieldname": "api_key", - "fieldtype": "Data", - "label": "API Key" - }, - { - "fieldname": "column_break_3", - "fieldtype": "Column Break" - }, - { - "depends_on": "eval:doc.app_type==\"Private\"", - "fieldname": "password", - "fieldtype": "Password", - "label": "Password" - }, - { - "fieldname": "shared_secret", - "fieldtype": "Data", - "label": "Shared secret" - }, - { - "fieldname": "access_token", - "fieldtype": "Data", - "hidden": 1, - "label": "Access Token", - "read_only": 1 - }, - { - "collapsible": 1, - "fieldname": "section_break_38", - "fieldtype": "Section Break", - "label": "Webhooks Details" - }, - { - "fieldname": "webhooks", - "fieldtype": "Table", - "label": "Webhooks", - "options": "Shopify Webhook Detail", - "read_only": 1 - }, - { - "fieldname": "section_break_15", - "fieldtype": "Section Break", - "label": "Customer Settings" - }, - { - "description": "If Shopify does not have a customer in the order, then while syncing the orders, the system will consider the default customer for the order", - "fieldname": "default_customer", - "fieldtype": "Link", - "label": "Default Customer", - "options": "Customer" - }, - { - "fieldname": "column_break_19", - "fieldtype": "Column Break" - }, - { - "description": "Customer Group will set to selected group while syncing customers from Shopify", - "fieldname": "customer_group", - "fieldtype": "Link", - "label": "Customer Group", - "options": "Customer Group" - }, - { - "fieldname": "company_dependent_settings", - "fieldtype": "Section Break" - }, - { - "fieldname": "company", - "fieldtype": "Link", - "label": "For Company", - "options": "Company" - }, - { - "description": "Cash Account will used for Sales Invoice creation", - "fieldname": "cash_bank_account", - "fieldtype": "Link", - "label": "Cash/Bank Account", - "options": "Account" - }, - { - "fieldname": "column_break_20", - "fieldtype": "Column Break" - }, - { - "fieldname": "cost_center", - "fieldtype": "Link", - "label": "Cost Center", - "options": "Cost Center" - }, - { - "fieldname": "erp_settings", - "fieldtype": "Section Break" - }, - { - "fieldname": "price_list", - "fieldtype": "Link", - "label": "Price List", - "options": "Price List" - }, - { - "default": "0", - "fieldname": "update_price_in_erpnext_price_list", - "fieldtype": "Check", - "label": "Update Price from Shopify To ERPNext Price List" - }, - { - "fieldname": "column_break_26", - "fieldtype": "Column Break" - }, - { - "description": "Default Warehouse to to create Sales Order and Delivery Note", - "fieldname": "warehouse", - "fieldtype": "Link", - "label": "Warehouse", - "options": "Warehouse" - }, - { - "fieldname": "section_break_25", - "fieldtype": "Section Break" - }, - { - "fieldname": "sales_order_series", - "fieldtype": "Select", - "label": "Sales Order Series" - }, - { - "fieldname": "column_break_27", - "fieldtype": "Column Break" - }, - { - "default": "0", - "fieldname": "sync_delivery_note", - "fieldtype": "Check", - "label": "Import Delivery Notes from Shopify on Shipment" - }, - { - "depends_on": "eval:doc.sync_delivery_note==1", - "fieldname": "delivery_note_series", - "fieldtype": "Select", - "label": "Delivery Note Series" - }, - { - "default": "0", - "fieldname": "sync_sales_invoice", - "fieldtype": "Check", - "label": "Import Sales Invoice from Shopify if Payment is marked" - }, - { - "depends_on": "eval:doc.sync_sales_invoice==1", - "fieldname": "sales_invoice_series", - "fieldtype": "Select", - "label": "Sales Invoice Series" - }, - { - "fieldname": "section_break_22", - "fieldtype": "Section Break" - }, - { - "fieldname": "html_16", - "fieldtype": "HTML", - "options": "Map Shopify Taxes / Shipping Charges to ERPNext Account" - }, - { - "fieldname": "taxes", - "fieldtype": "Table", - "label": "Shopify Tax Account", - "options": "Shopify Tax Account" - }, - { - "collapsible": 1, - "fieldname": "syncing_details_section", - "fieldtype": "Section Break", - "label": "Syncing Missing Orders" - }, - { - "depends_on": "eval:doc.sync_missing_orders", - "fieldname": "last_order_id", - "fieldtype": "Data", - "label": "Last Order Id", - "read_only": 1 - }, - { - "fieldname": "column_break_41", - "fieldtype": "Column Break" - }, - { - "default": "0", - "description": "On checking this Order from the ", - "fieldname": "sync_missing_orders", - "fieldtype": "Check", - "label": "Sync Missing Old Shopify Orders" - }, - { - "depends_on": "eval:doc.sync_missing_orders", - "fieldname": "sync_based_on", - "fieldtype": "Select", - "label": "Sync Based On", - "mandatory_depends_on": "eval:doc.sync_missing_orders", - "options": "\nDate\nShopify Order Id" - }, - { - "depends_on": "eval:doc.sync_based_on == 'Date' && doc.sync_missing_orders", - "fieldname": "from_date", - "fieldtype": "Date", - "label": "From Date", - "mandatory_depends_on": "eval:doc.sync_based_on == 'Date' && doc.sync_missing_orders" - }, - { - "depends_on": "eval:doc.sync_based_on == 'Date' && doc.sync_missing_orders", - "fieldname": "to_date", - "fieldtype": "Date", - "label": "To Date", - "mandatory_depends_on": "eval:doc.sync_based_on == 'Date' && doc.sync_missing_orders" - }, - { - "depends_on": "eval:doc.sync_based_on == 'Shopify Order Id' && doc.sync_missing_orders", - "fieldname": "from_order_id", - "fieldtype": "Data", - "label": "From Order Id", - "mandatory_depends_on": "eval:doc.sync_based_on == 'Shopify Order Id' && doc.sync_missing_orders" - }, - { - "depends_on": "eval:doc.sync_based_on == 'Shopify Order Id' && doc.sync_missing_orders", - "fieldname": "to_order_id", - "fieldtype": "Data", - "label": "To Order Id", - "mandatory_depends_on": "eval:doc.sync_based_on == 'Shopify Order Id' && doc.sync_missing_orders" - } - ], - "issingle": 1, - "links": [], - "modified": "2021-03-02 17:35:41.953317", - "modified_by": "Administrator", - "module": "ERPNext Integrations", - "name": "Shopify 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", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py b/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py deleted file mode 100644 index 381c5e5dec..0000000000 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/shopify_settings.py +++ /dev/null @@ -1,144 +0,0 @@ -# -*- coding: utf-8 -*- -# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and contributors -# For license information, please see license.txt - -from __future__ import unicode_literals -import frappe -import json -from frappe import _ -from frappe.model.document import Document -from frappe.utils import get_request_session -from requests.exceptions import HTTPError -from frappe.custom.doctype.custom_field.custom_field import create_custom_fields -from erpnext.erpnext_integrations.utils import get_webhook_address -from erpnext.erpnext_integrations.doctype.shopify_log.shopify_log import make_shopify_log - -class ShopifySettings(Document): - def validate(self): - if self.enable_shopify == 1: - setup_custom_fields() - self.validate_access_credentials() - self.register_webhooks() - else: - self.unregister_webhooks() - - def validate_access_credentials(self): - if not (self.get_password(raise_exception=False) and self.api_key and self.shopify_url): - frappe.msgprint(_("Missing value for Password, API Key or Shopify URL"), raise_exception=frappe.ValidationError) - - def register_webhooks(self): - webhooks = ["orders/create", "orders/paid", "orders/fulfilled"] - # url = get_shopify_url('admin/webhooks.json', self) - created_webhooks = [d.method for d in self.webhooks] - url = get_shopify_url('admin/api/2021-04/webhooks.json', self) - for method in webhooks: - session = get_request_session() - try: - res = session.post(url, data=json.dumps({ - "webhook": { - "topic": method, - "address": get_webhook_address(connector_name='shopify_connection', method='store_request_data', force_https=True), - "format": "json" - } - }), headers=get_header(self)) - res.raise_for_status() - self.update_webhook_table(method, res.json()) - - except HTTPError as e: - error_message = res.json().get('errors', e) - make_shopify_log(status="Warning", exception=error_message, rollback=True) - - except Exception as e: - make_shopify_log(status="Warning", exception=e, rollback=True) - - def unregister_webhooks(self): - session = get_request_session() - deleted_webhooks = [] - - for d in self.webhooks: - url = get_shopify_url('admin/api/2021-04/webhooks/{0}.json'.format(d.webhook_id), self) - try: - res = session.delete(url, headers=get_header(self)) - res.raise_for_status() - deleted_webhooks.append(d) - - except HTTPError as e: - error_message = res.json().get('errors', e) - make_shopify_log(status="Warning", exception=error_message, rollback=True) - - except Exception as e: - frappe.log_error(message=e, title='Shopify Webhooks Issue') - - for d in deleted_webhooks: - self.remove(d) - - def update_webhook_table(self, method, res): - self.append("webhooks", { - "webhook_id": res['webhook']['id'], - "method": method - }) - -def get_shopify_url(path, settings): - if settings.app_type == "Private": - return 'https://{}:{}@{}/{}'.format(settings.api_key, settings.get_password('password'), settings.shopify_url, path) - else: - return 'https://{}/{}'.format(settings.shopify_url, path) - -def get_header(settings): - header = {'Content-Type': 'application/json'} - - return header - -@frappe.whitelist() -def get_series(): - return { - "sales_order_series" : frappe.get_meta("Sales Order").get_options("naming_series") or "SO-Shopify-", - "sales_invoice_series" : frappe.get_meta("Sales Invoice").get_options("naming_series") or "SI-Shopify-", - "delivery_note_series" : frappe.get_meta("Delivery Note").get_options("naming_series") or "DN-Shopify-" - } - -def setup_custom_fields(): - custom_fields = { - "Customer": [ - dict(fieldname='shopify_customer_id', label='Shopify Customer Id', - fieldtype='Data', insert_after='series', read_only=1, print_hide=1) - ], - "Supplier": [ - dict(fieldname='shopify_supplier_id', label='Shopify Supplier Id', - fieldtype='Data', insert_after='supplier_name', read_only=1, print_hide=1) - ], - "Address": [ - dict(fieldname='shopify_address_id', label='Shopify Address Id', - fieldtype='Data', insert_after='fax', read_only=1, print_hide=1) - ], - "Item": [ - dict(fieldname='shopify_variant_id', label='Shopify Variant Id', - fieldtype='Data', insert_after='item_code', read_only=1, print_hide=1), - dict(fieldname='shopify_product_id', label='Shopify Product Id', - fieldtype='Data', insert_after='item_code', read_only=1, print_hide=1), - dict(fieldname='shopify_description', label='Shopify Description', - fieldtype='Text Editor', insert_after='description', read_only=1, print_hide=1) - ], - "Sales Order": [ - dict(fieldname='shopify_order_id', label='Shopify Order Id', - fieldtype='Data', insert_after='title', read_only=1, print_hide=1), - dict(fieldname='shopify_order_number', label='Shopify Order Number', - fieldtype='Data', insert_after='shopify_order_id', read_only=1, print_hide=1) - ], - "Delivery Note":[ - dict(fieldname='shopify_order_id', label='Shopify Order Id', - fieldtype='Data', insert_after='title', read_only=1, print_hide=1), - dict(fieldname='shopify_order_number', label='Shopify Order Number', - fieldtype='Data', insert_after='shopify_order_id', read_only=1, print_hide=1), - dict(fieldname='shopify_fulfillment_id', label='Shopify Fulfillment Id', - fieldtype='Data', insert_after='title', read_only=1, print_hide=1) - ], - "Sales Invoice": [ - dict(fieldname='shopify_order_id', label='Shopify Order Id', - fieldtype='Data', insert_after='title', read_only=1, print_hide=1), - dict(fieldname='shopify_order_number', label='Shopify Order Number', - fieldtype='Data', insert_after='shopify_order_id', read_only=1, print_hide=1) - ] - } - - create_custom_fields(custom_fields) diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/sync_customer.py b/erpnext/erpnext_integrations/doctype/shopify_settings/sync_customer.py deleted file mode 100644 index 2af57f4c89..0000000000 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/sync_customer.py +++ /dev/null @@ -1,71 +0,0 @@ -from __future__ import unicode_literals -import frappe -from frappe import _ - -def create_customer(shopify_customer, shopify_settings): - import frappe.utils.nestedset - - cust_name = (shopify_customer.get("first_name") + " " + (shopify_customer.get("last_name") \ - and shopify_customer.get("last_name") or "")) if shopify_customer.get("first_name")\ - else shopify_customer.get("email") - - try: - customer = frappe.get_doc({ - "doctype": "Customer", - "name": shopify_customer.get("id"), - "customer_name" : cust_name, - "shopify_customer_id": shopify_customer.get("id"), - "sync_with_shopify": 1, - "customer_group": shopify_settings.customer_group, - "territory": frappe.utils.nestedset.get_root_of("Territory"), - "customer_type": _("Individual") - }) - customer.flags.ignore_mandatory = True - customer.insert(ignore_permissions=True) - - if customer: - create_customer_address(customer, shopify_customer) - - frappe.db.commit() - - except Exception as e: - raise e - -def create_customer_address(customer, shopify_customer): - addresses = shopify_customer.get("addresses", []) - - if not addresses and "default_address" in shopify_customer: - addresses.append(shopify_customer["default_address"]) - - for i, address in enumerate(addresses): - address_title, address_type = get_address_title_and_type(customer.customer_name, i) - try : - frappe.get_doc({ - "doctype": "Address", - "shopify_address_id": address.get("id"), - "address_title": address_title, - "address_type": address_type, - "address_line1": address.get("address1") or "Address 1", - "address_line2": address.get("address2"), - "city": address.get("city") or "City", - "state": address.get("province"), - "pincode": address.get("zip"), - "country": address.get("country"), - "phone": address.get("phone"), - "email_id": shopify_customer.get("email"), - "links": [{ - "link_doctype": "Customer", - "link_name": customer.name - }] - }).insert(ignore_mandatory=True) - - except Exception as e: - raise e - -def get_address_title_and_type(customer_name, index): - address_type = _("Billing") - address_title = customer_name - if frappe.db.get_value("Address", "{0}-{1}".format(customer_name.strip(), address_type)): - address_title = "{0}-{1}".format(customer_name.strip(), index) - - return address_title, address_type diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py b/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py deleted file mode 100644 index 16efb6caee..0000000000 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/sync_product.py +++ /dev/null @@ -1,309 +0,0 @@ -from __future__ import unicode_literals -import frappe -from frappe import _ -from erpnext import get_default_company -from frappe.utils import cstr, cint, get_request_session -from erpnext.erpnext_integrations.doctype.shopify_settings.shopify_settings import get_shopify_url, get_header - -shopify_variants_attr_list = ["option1", "option2", "option3"] - -def sync_item_from_shopify(shopify_settings, item): - url = get_shopify_url("admin/api/2021-04/products/{0}.json".format(item.get("product_id")), shopify_settings) - session = get_request_session() - - try: - res = session.get(url, headers=get_header(shopify_settings)) - res.raise_for_status() - - shopify_item = res.json()["product"] - make_item(shopify_settings.warehouse, shopify_item) - except Exception as e: - raise e - -def make_item(warehouse, shopify_item): - add_item_weight(shopify_item) - - if has_variants(shopify_item): - attributes = create_attribute(shopify_item) - create_item(shopify_item, warehouse, 1, attributes) - create_item_variants(shopify_item, warehouse, attributes, shopify_variants_attr_list) - - else: - shopify_item["variant_id"] = shopify_item['variants'][0]["id"] - create_item(shopify_item, warehouse) - -def add_item_weight(shopify_item): - shopify_item["weight"] = shopify_item['variants'][0]["weight"] - shopify_item["weight_unit"] = shopify_item['variants'][0]["weight_unit"] - -def has_variants(shopify_item): - if len(shopify_item.get("options")) >= 1 and "Default Title" not in shopify_item.get("options")[0]["values"]: - return True - return False - -def create_attribute(shopify_item): - attribute = [] - # shopify item dict - for attr in shopify_item.get('options'): - if not frappe.db.get_value("Item Attribute", attr.get("name"), "name"): - frappe.get_doc({ - "doctype": "Item Attribute", - "attribute_name": attr.get("name"), - "item_attribute_values": [ - { - "attribute_value": attr_value, - "abbr":attr_value - } - for attr_value in attr.get("values") - ] - }).insert() - attribute.append({"attribute": attr.get("name")}) - - else: - # check for attribute values - item_attr = frappe.get_doc("Item Attribute", attr.get("name")) - if not item_attr.numeric_values: - set_new_attribute_values(item_attr, attr.get("values")) - item_attr.save() - attribute.append({"attribute": attr.get("name")}) - - else: - attribute.append({ - "attribute": attr.get("name"), - "from_range": item_attr.get("from_range"), - "to_range": item_attr.get("to_range"), - "increment": item_attr.get("increment"), - "numeric_values": item_attr.get("numeric_values") - }) - - return attribute - -def set_new_attribute_values(item_attr, values): - for attr_value in values: - if not any((d.abbr.lower() == attr_value.lower() or d.attribute_value.lower() == attr_value.lower())\ - for d in item_attr.item_attribute_values): - item_attr.append("item_attribute_values", { - "attribute_value": attr_value, - "abbr": attr_value - }) - -def create_item(shopify_item, warehouse, has_variant=0, attributes=None,variant_of=None): - item_dict = { - "doctype": "Item", - "shopify_product_id": shopify_item.get("id"), - "shopify_variant_id": shopify_item.get("variant_id"), - "variant_of": variant_of, - "sync_with_shopify": 1, - "is_stock_item": 1, - "item_code": cstr(shopify_item.get("item_code")) or cstr(shopify_item.get("id")), - "item_name": shopify_item.get("title", '').strip(), - "description": shopify_item.get("body_html") or shopify_item.get("title"), - "shopify_description": shopify_item.get("body_html") or shopify_item.get("title"), - "item_group": get_item_group(shopify_item.get("product_type")), - "has_variants": has_variant, - "attributes":attributes or [], - "stock_uom": shopify_item.get("uom") or _("Nos"), - "stock_keeping_unit": shopify_item.get("sku") or get_sku(shopify_item), - "default_warehouse": warehouse, - "image": get_item_image(shopify_item), - "weight_uom": shopify_item.get("weight_unit"), - "weight_per_unit": shopify_item.get("weight"), - "default_supplier": get_supplier(shopify_item), - "item_defaults": [ - { - "company": get_default_company() - } - ] - } - - if not is_item_exists(item_dict, attributes, variant_of=variant_of): - item_details = get_item_details(shopify_item) - name = '' - - if not item_details: - new_item = frappe.get_doc(item_dict) - new_item.insert(ignore_permissions=True, ignore_mandatory=True) - name = new_item.name - - if not name: - name = item_details.name - - if not has_variant: - add_to_price_list(shopify_item, name) - - frappe.db.commit() - -def create_item_variants(shopify_item, warehouse, attributes, shopify_variants_attr_list): - template_item = frappe.db.get_value("Item", filters={"shopify_product_id": shopify_item.get("id")}, - fieldname=["name", "stock_uom"], as_dict=True) - - if template_item: - for variant in shopify_item.get("variants"): - shopify_item_variant = { - "id" : variant.get("id"), - "item_code": variant.get("id"), - "title": variant.get("title"), - "product_type": shopify_item.get("product_type"), - "sku": variant.get("sku"), - "uom": template_item.stock_uom or _("Nos"), - "item_price": variant.get("price"), - "variant_id": variant.get("id"), - "weight_unit": variant.get("weight_unit"), - "weight": variant.get("weight") - } - - for i, variant_attr in enumerate(shopify_variants_attr_list): - if variant.get(variant_attr): - attributes[i].update({"attribute_value": get_attribute_value(variant.get(variant_attr), attributes[i])}) - create_item(shopify_item_variant, warehouse, 0, attributes, template_item.name) - -def get_attribute_value(variant_attr_val, attribute): - attribute_value = frappe.db.sql("""select attribute_value from `tabItem Attribute Value` - where parent = %s and (abbr = %s or attribute_value = %s)""", (attribute["attribute"], variant_attr_val, - variant_attr_val), as_list=1) - return attribute_value[0][0] if len(attribute_value)>0 else cint(variant_attr_val) - -def get_item_group(product_type=None): - import frappe.utils.nestedset - parent_item_group = frappe.utils.nestedset.get_root_of("Item Group") - - if product_type: - if not frappe.db.get_value("Item Group", product_type, "name"): - item_group = frappe.get_doc({ - "doctype": "Item Group", - "item_group_name": product_type, - "parent_item_group": parent_item_group, - "is_group": "No" - }).insert() - return item_group.name - else: - return product_type - else: - return parent_item_group - - -def get_sku(item): - if item.get("variants"): - return item.get("variants")[0].get("sku") - return "" - -def add_to_price_list(item, name): - shopify_settings = frappe.db.get_value("Shopify Settings", None, ["price_list", "update_price_in_erpnext_price_list"], as_dict=1) - if not shopify_settings.update_price_in_erpnext_price_list: - return - - item_price_name = frappe.db.get_value("Item Price", - {"item_code": name, "price_list": shopify_settings.price_list}, "name") - - if not item_price_name: - frappe.get_doc({ - "doctype": "Item Price", - "price_list": shopify_settings.price_list, - "item_code": name, - "price_list_rate": item.get("item_price") or item.get("variants")[0].get("price") - }).insert() - else: - item_rate = frappe.get_doc("Item Price", item_price_name) - item_rate.price_list_rate = item.get("item_price") or item.get("variants")[0].get("price") - item_rate.save() - -def get_item_image(shopify_item): - if shopify_item.get("image"): - return shopify_item.get("image").get("src") - return None - -def get_supplier(shopify_item): - if shopify_item.get("vendor"): - supplier = frappe.db.sql("""select name from tabSupplier - where name = %s or shopify_supplier_id = %s """, (shopify_item.get("vendor"), - shopify_item.get("vendor").lower()), as_list=1) - - if not supplier: - supplier = frappe.get_doc({ - "doctype": "Supplier", - "supplier_name": shopify_item.get("vendor"), - "shopify_supplier_id": shopify_item.get("vendor").lower(), - "supplier_group": get_supplier_group() - }).insert() - return supplier.name - else: - return shopify_item.get("vendor") - else: - return "" - -def get_supplier_group(): - supplier_group = frappe.db.get_value("Supplier Group", _("Shopify Supplier")) - if not supplier_group: - supplier_group = frappe.get_doc({ - "doctype": "Supplier Group", - "supplier_group_name": _("Shopify Supplier") - }).insert() - return supplier_group.name - return supplier_group - -def get_item_details(shopify_item): - item_details = {} - - item_details = frappe.db.get_value("Item", {"shopify_product_id": shopify_item.get("id")}, - ["name", "stock_uom", "item_name"], as_dict=1) - - if item_details: - return item_details - - else: - item_details = frappe.db.get_value("Item", {"shopify_variant_id": shopify_item.get("id")}, - ["name", "stock_uom", "item_name"], as_dict=1) - return item_details - -def is_item_exists(shopify_item, attributes=None, variant_of=None): - if variant_of: - name = variant_of - else: - name = frappe.db.get_value("Item", {"item_name": shopify_item.get("item_name")}) - - if name: - item = frappe.get_doc("Item", name) - item.flags.ignore_mandatory=True - - if not variant_of and not item.shopify_product_id: - item.shopify_product_id = shopify_item.get("shopify_product_id") - item.shopify_variant_id = shopify_item.get("shopify_variant_id") - item.save() - return True - - if item.shopify_product_id and attributes and attributes[0].get("attribute_value"): - if not variant_of: - variant_of = frappe.db.get_value("Item", - {"shopify_product_id": item.shopify_product_id}, "variant_of") - - # create conditions for all item attributes, - # as we are putting condition basis on OR it will fetch all items matching either of conditions - # thus comparing matching conditions with len(attributes) - # which will give exact matching variant item. - - conditions = ["(iv.attribute='{0}' and iv.attribute_value = '{1}')"\ - .format(attr.get("attribute"), attr.get("attribute_value")) for attr in attributes] - - conditions = "( {0} ) and iv.parent = it.name ) = {1}".format(" or ".join(conditions), len(attributes)) - - parent = frappe.db.sql(""" select * from tabItem it where - ( select count(*) from `tabItem Variant Attribute` iv - where {conditions} and it.variant_of = %s """.format(conditions=conditions) , - variant_of, as_list=1) - - if parent: - variant = frappe.get_doc("Item", parent[0][0]) - variant.flags.ignore_mandatory = True - - variant.shopify_product_id = shopify_item.get("shopify_product_id") - variant.shopify_variant_id = shopify_item.get("shopify_variant_id") - variant.save() - return False - - if item.shopify_product_id and item.shopify_product_id != shopify_item.get("shopify_product_id"): - return False - - return True - - else: - return False diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/test_data/custom_field.json b/erpnext/erpnext_integrations/doctype/shopify_settings/test_data/custom_field.json deleted file mode 100644 index db6c3d5aa3..0000000000 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/test_data/custom_field.json +++ /dev/null @@ -1,527 +0,0 @@ -[ - { - "allow_on_submit": 0, - "collapsible": 0, - "collapsible_depends_on": null, - "default": null, - "depends_on": null, - "description": null, - "docstatus": 0, - "doctype": "Custom Field", - "dt": "Print Settings", - "fieldname": "compact_item_print", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "insert_after": "with_letterhead", - "label": "Compact Item Print", - "modified": "2016-06-06 15:18:17.025602", - "name": "Print Settings-compact_item_print", - "no_copy": 0, - "options": null, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": null, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "unique": 0, - "width": null - }, - { - "allow_on_submit": 0, - "collapsible": 0, - "collapsible_depends_on": null, - "default": null, - "depends_on": null, - "description": null, - "docstatus": 0, - "doctype": "Custom Field", - "dt": "Customer", - "fieldname": "shopify_customer_id", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "insert_after": "naming_series", - "label": "Shopify Customer Id", - "modified": "2016-01-15 17:25:28.991818", - "name": "Customer-shopify_customer_id", - "no_copy": 1, - "options": null, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": null, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "unique": 0, - "width": null - }, - { - "allow_on_submit": 0, - "collapsible": 0, - "collapsible_depends_on": null, - "default": null, - "depends_on": null, - "description": null, - "docstatus": 0, - "doctype": "Custom Field", - "dt": "Address", - "fieldname": "shopify_address_id", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "insert_after": "fax", - "label": "Shopify Address Id", - "modified": "2016-01-15 17:50:52.213743", - "name": "Address-shopify_address_id", - "no_copy": 1, - "options": null, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": null, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "unique": 0, - "width": null - }, - { - "allow_on_submit": 0, - "collapsible": 0, - "collapsible_depends_on": null, - "default": null, - "depends_on": null, - "description": null, - "docstatus": 0, - "doctype": "Custom Field", - "dt": "Sales Order", - "fieldname": "shopify_order_id", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "insert_after": "title", - "label": "Shopify Order Id", - "modified": "2016-01-18 09:55:50.764524", - "name": "Sales Order-shopify_order_id", - "no_copy": 1, - "options": null, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": null, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "unique": 0, - "width": null - }, - { - "allow_on_submit": 0, - "collapsible": 0, - "collapsible_depends_on": null, - "default": null, - "depends_on": null, - "description": null, - "docstatus": 0, - "doctype": "Custom Field", - "dt": "Item", - "fieldname": "shopify_product_id", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "insert_after": "item_code", - "label": "Shopify Product Id", - "modified": "2016-01-19 15:44:16.132952", - "name": "Item-shopify_product_id", - "no_copy": 1, - "options": null, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": null, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "unique": 0, - "width": null - }, - { - "allow_on_submit": 0, - "collapsible": 0, - "collapsible_depends_on": null, - "default": null, - "depends_on": null, - "description": null, - "docstatus": 0, - "doctype": "Custom Field", - "dt": "Sales Invoice", - "fieldname": "shopify_order_id", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "insert_after": "naming_series", - "label": "Shopify Order Id", - "modified": "2016-01-19 16:30:12.261797", - "name": "Sales Invoice-shopify_order_id", - "no_copy": 1, - "options": null, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": null, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "unique": 0, - "width": null - }, - { - "allow_on_submit": 0, - "collapsible": 0, - "collapsible_depends_on": null, - "default": null, - "depends_on": null, - "description": null, - "docstatus": 0, - "doctype": "Custom Field", - "dt": "Delivery Note", - "fieldname": "shopify_order_id", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "insert_after": "title", - "label": "Shopify Order Id", - "modified": "2016-01-19 16:30:31.201198", - "name": "Delivery Note-shopify_order_id", - "no_copy": 1, - "options": null, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": null, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "unique": 0, - "width": null - }, - { - "allow_on_submit": 0, - "collapsible": 0, - "collapsible_depends_on": null, - "default": null, - "depends_on": null, - "description": null, - "docstatus": 0, - "doctype": "Custom Field", - "dt": "Item", - "fieldname": "stock_keeping_unit", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "insert_after": "stock_uom", - "label": "Stock Keeping Unit", - "modified": "2015-11-10 09:29:10.854943", - "name": "Item-stock_keeping_unit", - "no_copy": 1, - "options": null, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": null, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "unique": 0, - "width": null - }, - { - "allow_on_submit": 0, - "collapsible": 0, - "collapsible_depends_on": null, - "default": "0", - "depends_on": null, - "description": null, - "docstatus": 0, - "doctype": "Custom Field", - "dt": "Item", - "fieldname": "sync_with_shopify", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "insert_after": "is_stock_item", - "label": "Sync With Shopify", - "modified": "2015-10-12 15:54:31.997714", - "name": "Item-sync_with_shopify", - "no_copy": 0, - "options": null, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": null, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "unique": 0, - "width": null - }, - { - "allow_on_submit": 0, - "collapsible": 0, - "collapsible_depends_on": null, - "default": null, - "depends_on": null, - "description": null, - "docstatus": 0, - "doctype": "Custom Field", - "dt": "Customer", - "fieldname": "sync_with_shopify", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "insert_after": "is_frozen", - "label": "Sync With Shopify", - "modified": "2015-10-01 17:31:55.758826", - "name": "Customer-sync_with_shopify", - "no_copy": 0, - "options": null, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": null, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "unique": 0, - "width": null - }, - { - "allow_on_submit": 0, - "collapsible": 0, - "collapsible_depends_on": null, - "default": null, - "depends_on": null, - "description": null, - "docstatus": 0, - "doctype": "Custom Field", - "dt": "Item", - "fieldname": "shopify_variant_id", - "fieldtype": "Data", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "insert_after": "item_code", - "label": "Variant Id", - "modified": "2015-11-09 18:26:50.825858", - "name": "Item-shopify_variant_id", - "no_copy": 1, - "options": null, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": null, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "unique": 0, - "width": null - }, - { - "allow_on_submit": 0, - "collapsible": 0, - "collapsible_depends_on": null, - "default": null, - "depends_on": null, - "description": null, - "docstatus": 0, - "doctype": "Custom Field", - "dt": "Item", - "fieldname": "sync_qty_with_shopify", - "fieldtype": "Check", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "insert_after": "item_code", - "label": "Sync Quantity With Shopify", - "modified": "2015-12-29 08:37:46.183295", - "name": "Item-sync_qty_with_shopify", - "no_copy": 0, - "options": null, - "permlevel": 0, - "precision": "", - "print_hide": 0, - "print_hide_if_no_value": 0, - "print_width": null, - "read_only": 0, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "unique": 0, - "width": null - }, - { - "allow_on_submit": 0, - "collapsible": 0, - "collapsible_depends_on": null, - "default": null, - "depends_on": null, - "description": null, - "docstatus": 0, - "doctype": "Custom Field", - "dt": "Delivery Note", - "fieldname": "shopify_fulfillment_id", - "fieldtype": "Data", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "insert_after": "title", - "label": "Shopify Fulfillment Id", - "modified": "2016-01-20 23:50:35.609543", - "name": "Delivery Note-shopify_fulfillment_id", - "no_copy": 1, - "options": null, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": null, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "unique": 0, - "width": null - }, - { - "allow_on_submit": 0, - "collapsible": 0, - "collapsible_depends_on": null, - "default": null, - "depends_on": null, - "description": null, - "docstatus": 0, - "doctype": "Custom Field", - "dt": "Supplier", - "fieldname": "shopify_supplier_id", - "fieldtype": "Data", - "hidden": 1, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "insert_after": "supplier_name", - "label": "Shopify Supplier Id", - "modified": "2016-02-01 15:41:25.818306", - "name": "Supplier-shopify_supplier_id", - "no_copy": 1, - "options": null, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": null, - "read_only": 1, - "report_hide": 0, - "reqd": 0, - "search_index": 0, - "unique": 0, - "width": null - }, - { - "allow_on_submit": 0, - "collapsible": 0, - "collapsible_depends_on": null, - "default": null, - "depends_on": null, - "description": null, - "docstatus": 0, - "doctype": "Custom Field", - "dt": "Item", - "fieldname": "shopify_description", - "fieldtype": "Text Editor", - "hidden": 0, - "ignore_user_permissions": 0, - "ignore_xss_filter": 0, - "in_filter": 0, - "in_list_view": 0, - "insert_after": "section_break_11", - "label": "shopify_description", - "modified": "2016-06-15 12:15:36.325581", - "name": "Item-shopify_description", - "no_copy": 0, - "options": null, - "permlevel": 0, - "precision": "", - "print_hide": 1, - "print_hide_if_no_value": 0, - "print_width": null, - "read_only": 0, - "report_hide": 1, - "reqd": 0, - "search_index": 0, - "unique": 0, - "width": null - } -] \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/test_data/shopify_customer.json b/erpnext/erpnext_integrations/doctype/shopify_settings/test_data/shopify_customer.json deleted file mode 100644 index e91ce9abf8..0000000000 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/test_data/shopify_customer.json +++ /dev/null @@ -1,59 +0,0 @@ -{ - "customer": { - "id": 2324518599, - "email": "andrew@wyatt.co.in", - "accepts_marketing": false, - "created_at": "2016-01-20T17:18:35+05:30", - "updated_at": "2016-01-20T17:22:23+05:30", - "first_name": "Andrew", - "last_name": "Wyatt", - "orders_count": 0, - "state": "disabled", - "total_spent": "0.00", - "last_order_id": null, - "note": "", - "verified_email": true, - "multipass_identifier": null, - "tax_exempt": false, - "tags": "", - "last_order_name": null, - "default_address": { - "id": 2476804295, - "first_name": "Andrew", - "last_name": "Wyatt", - "company": "Wyatt Inc.", - "address1": "B-11, Betahouse", - "address2": "Street 11, Sector 52", - "city": "Manhattan", - "province": "New York", - "country": "United States", - "zip": "10027", - "phone": "145-112211", - "name": "Andrew Wyatt", - "province_code": "NY", - "country_code": "US", - "country_name": "United States", - "default": true - }, - "addresses": [ - { - "id": 2476804295, - "first_name": "Andrew", - "last_name": "Wyatt", - "company": "Wyatt Inc.", - "address1": "B-11, Betahouse", - "address2": "Street 11, Sector 52", - "city": "Manhattan", - "province": "New York", - "country": "United States", - "zip": "10027", - "phone": "145-112211", - "name": "Andrew Wyatt", - "province_code": "NY", - "country_code": "US", - "country_name": "United States", - "default": true - } - ] - } -} \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/shopify_settings/test_data/shopify_item.json b/erpnext/erpnext_integrations/doctype/shopify_settings/test_data/shopify_item.json deleted file mode 100644 index 296dede786..0000000000 --- a/erpnext/erpnext_integrations/doctype/shopify_settings/test_data/shopify_item.json +++ /dev/null @@ -1,125 +0,0 @@ -{ - "product": { - "id": 4059739520, - "title": "Shopify Test Item", - "body_html": "