diff --git a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/__init__.py b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/__init__.py deleted file mode 100644 index e69de29bb2d1..000000000000 diff --git a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py deleted file mode 100644 index 29bc36f384d4..000000000000 --- a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_methods.py +++ /dev/null @@ -1,524 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies and contributors -# For license information, please see license.txt - - -import csv -import math -import time -from io import StringIO - -import dateutil -import frappe -from frappe import _ - -import erpnext.erpnext_integrations.doctype.amazon_mws_settings.amazon_mws_api as mws - - -#Get and Create Products -def get_products_details(): - products = get_products_instance() - reports = get_reports_instance() - - mws_settings = frappe.get_doc("Amazon MWS Settings") - market_place_list = return_as_list(mws_settings.market_place_id) - - for marketplace in market_place_list: - report_id = request_and_fetch_report_id("_GET_FLAT_FILE_OPEN_LISTINGS_DATA_", None, None, market_place_list) - - if report_id: - listings_response = reports.get_report(report_id=report_id) - - #Get ASIN Codes - string_io = StringIO(frappe.safe_decode(listings_response.original)) - csv_rows = list(csv.reader(string_io, delimiter='\t')) - asin_list = list(set([row[1] for row in csv_rows[1:]])) - #break into chunks of 10 - asin_chunked_list = list(chunks(asin_list, 10)) - - #Map ASIN Codes to SKUs - sku_asin = [{"asin":row[1],"sku":row[0]} for row in csv_rows[1:]] - - #Fetch Products List from ASIN - for asin_list in asin_chunked_list: - products_response = call_mws_method(products.get_matching_product,marketplaceid=marketplace, - asins=asin_list) - - matching_products_list = products_response.parsed - for product in matching_products_list: - skus = [row["sku"] for row in sku_asin if row["asin"]==product.ASIN] - for sku in skus: - create_item_code(product, sku) - -def get_products_instance(): - mws_settings = frappe.get_doc("Amazon MWS Settings") - products = mws.Products( - account_id = mws_settings.seller_id, - access_key = mws_settings.aws_access_key_id, - secret_key = mws_settings.secret_key, - region = mws_settings.region, - domain = mws_settings.domain - ) - - return products - -def get_reports_instance(): - mws_settings = frappe.get_doc("Amazon MWS Settings") - reports = mws.Reports( - account_id = mws_settings.seller_id, - access_key = mws_settings.aws_access_key_id, - secret_key = mws_settings.secret_key, - region = mws_settings.region, - domain = mws_settings.domain - ) - - return reports - -#returns list as expected by amazon API -def return_as_list(input_value): - if isinstance(input_value, list): - return input_value - else: - return [input_value] - -#function to chunk product data -def chunks(l, n): - for i in range(0, len(l), n): - yield l[i:i+n] - -def request_and_fetch_report_id(report_type, start_date=None, end_date=None, marketplaceids=None): - reports = get_reports_instance() - report_response = reports.request_report(report_type=report_type, - start_date=start_date, - end_date=end_date, - marketplaceids=marketplaceids) - - report_request_id = report_response.parsed["ReportRequestInfo"]["ReportRequestId"]["value"] - generated_report_id = None - #poll to get generated report - for x in range(1,10): - report_request_list_response = reports.get_report_request_list(requestids=[report_request_id]) - report_status = report_request_list_response.parsed["ReportRequestInfo"]["ReportProcessingStatus"]["value"] - - if report_status == "_SUBMITTED_" or report_status == "_IN_PROGRESS_": - #add time delay to wait for amazon to generate report - time.sleep(15) - continue - elif report_status == "_CANCELLED_": - break - elif report_status == "_DONE_NO_DATA_": - break - elif report_status == "_DONE_": - generated_report_id = report_request_list_response.parsed["ReportRequestInfo"]["GeneratedReportId"]["value"] - break - return generated_report_id - -def call_mws_method(mws_method, *args, **kwargs): - - mws_settings = frappe.get_doc("Amazon MWS Settings") - max_retries = mws_settings.max_retry_limit - - for x in range(0, max_retries): - try: - response = mws_method(*args, **kwargs) - return response - except Exception as e: - delay = math.pow(4, x) * 125 - frappe.log_error(message=e, title=f'Method "{mws_method.__name__}" failed') - time.sleep(delay) - continue - - mws_settings.enable_sync = 0 - mws_settings.save() - - frappe.throw(_("Sync has been temporarily disabled because maximum retries have been exceeded")) - -def create_item_code(amazon_item_json, sku): - if frappe.db.get_value("Item", sku): - return - - item = frappe.new_doc("Item") - - new_manufacturer = create_manufacturer(amazon_item_json) - new_brand = create_brand(amazon_item_json) - - mws_settings = frappe.get_doc("Amazon MWS Settings") - - item.item_code = sku - item.amazon_item_code = amazon_item_json.ASIN - item.item_group = mws_settings.item_group - item.description = amazon_item_json.Product.AttributeSets.ItemAttributes.Title - item.brand = new_brand - item.manufacturer = new_manufacturer - - item.image = amazon_item_json.Product.AttributeSets.ItemAttributes.SmallImage.URL - - temp_item_group = amazon_item_json.Product.AttributeSets.ItemAttributes.ProductGroup - - item_group = frappe.db.get_value("Item Group",filters={"item_group_name": temp_item_group}) - - if not item_group: - igroup = frappe.new_doc("Item Group") - igroup.item_group_name = temp_item_group - igroup.parent_item_group = mws_settings.item_group - igroup.insert() - - item.append("item_defaults", {'company':mws_settings.company}) - - item.insert(ignore_permissions=True) - create_item_price(amazon_item_json, item.item_code) - - return item.name - -def create_manufacturer(amazon_item_json): - if not amazon_item_json.Product.AttributeSets.ItemAttributes.Manufacturer: - return None - - existing_manufacturer = frappe.db.get_value("Manufacturer", - filters={"short_name":amazon_item_json.Product.AttributeSets.ItemAttributes.Manufacturer}) - - if not existing_manufacturer: - manufacturer = frappe.new_doc("Manufacturer") - manufacturer.short_name = amazon_item_json.Product.AttributeSets.ItemAttributes.Manufacturer - manufacturer.insert() - return manufacturer.short_name - else: - return existing_manufacturer - -def create_brand(amazon_item_json): - if not amazon_item_json.Product.AttributeSets.ItemAttributes.Brand: - return None - - existing_brand = frappe.db.get_value("Brand", - filters={"brand":amazon_item_json.Product.AttributeSets.ItemAttributes.Brand}) - if not existing_brand: - brand = frappe.new_doc("Brand") - brand.brand = amazon_item_json.Product.AttributeSets.ItemAttributes.Brand - brand.insert() - return brand.brand - else: - return existing_brand - -def create_item_price(amazon_item_json, item_code): - item_price = frappe.new_doc("Item Price") - item_price.price_list = frappe.db.get_value("Amazon MWS Settings", "Amazon MWS Settings", "price_list") - if not("ListPrice" in amazon_item_json.Product.AttributeSets.ItemAttributes): - item_price.price_list_rate = 0 - else: - item_price.price_list_rate = amazon_item_json.Product.AttributeSets.ItemAttributes.ListPrice.Amount - - item_price.item_code = item_code - item_price.insert() - -#Get and create Orders -def get_orders(after_date): - try: - orders = get_orders_instance() - statuses = ["PartiallyShipped", "Unshipped", "Shipped", "Canceled"] - mws_settings = frappe.get_doc("Amazon MWS Settings") - market_place_list = return_as_list(mws_settings.market_place_id) - - orders_response = call_mws_method(orders.list_orders, marketplaceids=market_place_list, - fulfillment_channels=["MFN", "AFN"], - lastupdatedafter=after_date, - orderstatus=statuses, - max_results='50') - - while True: - orders_list = [] - - if "Order" in orders_response.parsed.Orders: - orders_list = return_as_list(orders_response.parsed.Orders.Order) - - if len(orders_list) == 0: - break - - for order in orders_list: - create_sales_order(order, after_date) - - if not "NextToken" in orders_response.parsed: - break - - next_token = orders_response.parsed.NextToken - orders_response = call_mws_method(orders.list_orders_by_next_token, next_token) - - except Exception as e: - frappe.log_error(title="get_orders", message=e) - -def get_orders_instance(): - mws_settings = frappe.get_doc("Amazon MWS Settings") - orders = mws.Orders( - account_id = mws_settings.seller_id, - access_key = mws_settings.aws_access_key_id, - secret_key = mws_settings.secret_key, - region= mws_settings.region, - domain= mws_settings.domain, - version="2013-09-01" - ) - - return orders - -def create_sales_order(order_json,after_date): - customer_name = create_customer(order_json) - create_address(order_json, customer_name) - - market_place_order_id = order_json.AmazonOrderId - - so = frappe.db.get_value("Sales Order", - filters={"amazon_order_id": market_place_order_id}, - fieldname="name") - - taxes_and_charges = frappe.db.get_value("Amazon MWS Settings", "Amazon MWS Settings", "taxes_charges") - - if so: - return - - if not so: - items = get_order_items(market_place_order_id) - delivery_date = dateutil.parser.parse(order_json.LatestShipDate).strftime("%Y-%m-%d") - transaction_date = dateutil.parser.parse(order_json.PurchaseDate).strftime("%Y-%m-%d") - - so = frappe.get_doc({ - "doctype": "Sales Order", - "naming_series": "SO-", - "amazon_order_id": market_place_order_id, - "marketplace_id": order_json.MarketplaceId, - "customer": customer_name, - "delivery_date": delivery_date, - "transaction_date": transaction_date, - "items": items, - "company": frappe.db.get_value("Amazon MWS Settings", "Amazon MWS Settings", "company") - }) - - try: - if taxes_and_charges: - charges_and_fees = get_charges_and_fees(market_place_order_id) - for charge in charges_and_fees.get("charges"): - so.append('taxes', charge) - - for fee in charges_and_fees.get("fees"): - so.append('taxes', fee) - - so.insert(ignore_permissions=True) - so.submit() - - except Exception as e: - import traceback - frappe.log_error(message=traceback.format_exc(), title="Create Sales Order") - -def create_customer(order_json): - order_customer_name = "" - - if not("BuyerName" in order_json): - order_customer_name = "Buyer - " + order_json.AmazonOrderId - else: - order_customer_name = order_json.BuyerName - - existing_customer_name = frappe.db.get_value("Customer", - filters={"name": order_customer_name}, fieldname="name") - - if existing_customer_name: - filters = [ - ["Dynamic Link", "link_doctype", "=", "Customer"], - ["Dynamic Link", "link_name", "=", existing_customer_name], - ["Dynamic Link", "parenttype", "=", "Contact"] - ] - - existing_contacts = frappe.get_list("Contact", filters) - - if existing_contacts: - pass - else: - new_contact = frappe.new_doc("Contact") - new_contact.first_name = order_customer_name - new_contact.append('links', { - "link_doctype": "Customer", - "link_name": existing_customer_name - }) - new_contact.insert() - - return existing_customer_name - else: - mws_customer_settings = frappe.get_doc("Amazon MWS Settings") - new_customer = frappe.new_doc("Customer") - new_customer.customer_name = order_customer_name - new_customer.customer_group = mws_customer_settings.customer_group - new_customer.territory = mws_customer_settings.territory - new_customer.customer_type = mws_customer_settings.customer_type - new_customer.save() - - new_contact = frappe.new_doc("Contact") - new_contact.first_name = order_customer_name - new_contact.append('links', { - "link_doctype": "Customer", - "link_name": new_customer.name - }) - - new_contact.insert() - - return new_customer.name - -def create_address(amazon_order_item_json, customer_name): - - filters = [ - ["Dynamic Link", "link_doctype", "=", "Customer"], - ["Dynamic Link", "link_name", "=", customer_name], - ["Dynamic Link", "parenttype", "=", "Address"] - ] - - existing_address = frappe.get_list("Address", filters) - - if not("ShippingAddress" in amazon_order_item_json): - return None - else: - make_address = frappe.new_doc("Address") - - if "AddressLine1" in amazon_order_item_json.ShippingAddress: - make_address.address_line1 = amazon_order_item_json.ShippingAddress.AddressLine1 - else: - make_address.address_line1 = "Not Provided" - - if "City" in amazon_order_item_json.ShippingAddress: - make_address.city = amazon_order_item_json.ShippingAddress.City - else: - make_address.city = "Not Provided" - - if "StateOrRegion" in amazon_order_item_json.ShippingAddress: - make_address.state = amazon_order_item_json.ShippingAddress.StateOrRegion - - if "PostalCode" in amazon_order_item_json.ShippingAddress: - make_address.pincode = amazon_order_item_json.ShippingAddress.PostalCode - - for address in existing_address: - address_doc = frappe.get_doc("Address", address["name"]) - if (address_doc.address_line1 == make_address.address_line1 and - address_doc.pincode == make_address.pincode): - return address - - make_address.append("links", { - "link_doctype": "Customer", - "link_name": customer_name - }) - make_address.address_type = "Shipping" - make_address.insert() - -def get_order_items(market_place_order_id): - mws_orders = get_orders_instance() - - order_items_response = call_mws_method(mws_orders.list_order_items, amazon_order_id=market_place_order_id) - final_order_items = [] - - order_items_list = return_as_list(order_items_response.parsed.OrderItems.OrderItem) - - warehouse = frappe.db.get_value("Amazon MWS Settings", "Amazon MWS Settings", "warehouse") - - while True: - for order_item in order_items_list: - - if not "ItemPrice" in order_item: - price = 0 - else: - price = order_item.ItemPrice.Amount - - final_order_items.append({ - "item_code": get_item_code(order_item), - "item_name": order_item.SellerSKU, - "description": order_item.Title, - "rate": price, - "qty": order_item.QuantityOrdered, - "stock_uom": "Nos", - "warehouse": warehouse, - "conversion_factor": "1.0" - }) - - if not "NextToken" in order_items_response.parsed: - break - - next_token = order_items_response.parsed.NextToken - - order_items_response = call_mws_method(mws_orders.list_order_items_by_next_token, next_token) - order_items_list = return_as_list(order_items_response.parsed.OrderItems.OrderItem) - - return final_order_items - -def get_item_code(order_item): - sku = order_item.SellerSKU - item_code = frappe.db.get_value("Item", {"item_code": sku}, "item_code") - if item_code: - return item_code - -def get_charges_and_fees(market_place_order_id): - finances = get_finances_instance() - - charges_fees = {"charges":[], "fees":[]} - - response = call_mws_method(finances.list_financial_events, amazon_order_id=market_place_order_id) - - shipment_event_list = return_as_list(response.parsed.FinancialEvents.ShipmentEventList) - - for shipment_event in shipment_event_list: - if shipment_event: - shipment_item_list = return_as_list(shipment_event.ShipmentEvent.ShipmentItemList.ShipmentItem) - - for shipment_item in shipment_item_list: - charges, fees = [], [] - - if 'ItemChargeList' in shipment_item.keys(): - charges = return_as_list(shipment_item.ItemChargeList.ChargeComponent) - - if 'ItemFeeList' in shipment_item.keys(): - fees = return_as_list(shipment_item.ItemFeeList.FeeComponent) - - for charge in charges: - if(charge.ChargeType != "Principal") and float(charge.ChargeAmount.CurrencyAmount) != 0: - charge_account = get_account(charge.ChargeType) - charges_fees.get("charges").append({ - "charge_type":"Actual", - "account_head": charge_account, - "tax_amount": charge.ChargeAmount.CurrencyAmount, - "description": charge.ChargeType + " for " + shipment_item.SellerSKU - }) - - for fee in fees: - if float(fee.FeeAmount.CurrencyAmount) != 0: - fee_account = get_account(fee.FeeType) - charges_fees.get("fees").append({ - "charge_type":"Actual", - "account_head": fee_account, - "tax_amount": fee.FeeAmount.CurrencyAmount, - "description": fee.FeeType + " for " + shipment_item.SellerSKU - }) - - return charges_fees - -def get_finances_instance(): - - mws_settings = frappe.get_doc("Amazon MWS Settings") - - finances = mws.Finances( - account_id = mws_settings.seller_id, - access_key = mws_settings.aws_access_key_id, - secret_key = mws_settings.secret_key, - region= mws_settings.region, - domain= mws_settings.domain, - version="2015-05-01" - ) - - return finances - -def get_account(name): - existing_account = frappe.db.get_value("Account", {"account_name": "Amazon {0}".format(name)}) - account_name = existing_account - mws_settings = frappe.get_doc("Amazon MWS Settings") - - if not existing_account: - try: - new_account = frappe.new_doc("Account") - new_account.account_name = "Amazon {0}".format(name) - new_account.company = mws_settings.company - new_account.parent_account = mws_settings.market_place_account_group - new_account.insert(ignore_permissions=True) - account_name = new_account.name - except Exception as e: - frappe.log_error(message=e, title="Create Account") - - return account_name diff --git a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_api.py b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_api.py deleted file mode 100755 index 4caf137455a3..000000000000 --- a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_api.py +++ /dev/null @@ -1,651 +0,0 @@ -#!/usr/bin/env python -# -# Basic interface to Amazon MWS -# Based on http://code.google.com/p/amazon-mws-python -# Extended to include finances object - -import base64 -import hashlib -import hmac -import re -from urllib.parse import quote - -from erpnext.erpnext_integrations.doctype.amazon_mws_settings import xml_utils - -try: - from xml.etree.ElementTree import ParseError as XMLError -except ImportError: - from xml.parsers.expat import ExpatError as XMLError - -from time import gmtime, strftime - -from requests import request -from requests.exceptions import HTTPError - -__all__ = [ - 'Feeds', - 'Inventory', - 'MWSError', - 'Reports', - 'Orders', - 'Products', - 'Recommendations', - 'Sellers', - 'Finances' -] - -# See https://images-na.ssl-images-amazon.com/images/G/01/mwsportal/doc/en_US/bde/MWSDeveloperGuide._V357736853_.pdf page 8 -# for a list of the end points and marketplace IDs - -MARKETPLACES = { - "CA": "https://mws.amazonservices.ca", #A2EUQ1WTGCTBG2 - "US": "https://mws.amazonservices.com", #ATVPDKIKX0DER", - "DE": "https://mws-eu.amazonservices.com", #A1PA6795UKMFR9 - "ES": "https://mws-eu.amazonservices.com", #A1RKKUPIHCS9HS - "FR": "https://mws-eu.amazonservices.com", #A13V1IB3VIYZZH - "IN": "https://mws.amazonservices.in", #A21TJRUUN4KGV - "IT": "https://mws-eu.amazonservices.com", #APJ6JRA9NG5V4 - "UK": "https://mws-eu.amazonservices.com", #A1F83G8C2ARO7P - "JP": "https://mws.amazonservices.jp", #A1VC38T7YXB528 - "CN": "https://mws.amazonservices.com.cn", #AAHKV2X7AFYLW - "AE": " https://mws.amazonservices.ae", #A2VIGQ35RCS4UG - "MX": "https://mws.amazonservices.com.mx", #A1AM78C64UM0Y8 - "BR": "https://mws.amazonservices.com", #A2Q3Y263D00KWC -} - - -class MWSError(Exception): - """ - Main MWS Exception class - """ - # Allows quick access to the response object. - # Do not rely on this attribute, always check if its not None. - response = None - -def calc_md5(string): - """Calculates the MD5 encryption for the given string - """ - md = hashlib.md5() - md.update(string) - return base64.encodebytes(md.digest()).decode().strip() - - - -def remove_empty(d): - """ - Helper function that removes all keys from a dictionary (d), - that have an empty value. - """ - for key in list(d): - if not d[key]: - del d[key] - return d - -def remove_namespace(xml): - xml = xml.decode('utf-8') - regex = re.compile(' xmlns(:ns2)?="[^"]+"|(ns2:)|(xml:)') - return regex.sub('', xml) - -class DictWrapper(object): - def __init__(self, xml, rootkey=None): - self.original = xml - self._rootkey = rootkey - self._mydict = xml_utils.xml2dict().fromstring(remove_namespace(xml)) - self._response_dict = self._mydict.get(list(self._mydict)[0], self._mydict) - - @property - def parsed(self): - if self._rootkey: - return self._response_dict.get(self._rootkey) - else: - return self._response_dict - -class DataWrapper(object): - """ - Text wrapper in charge of validating the hash sent by Amazon. - """ - def __init__(self, data, header): - self.original = data - if 'content-md5' in header: - hash_ = calc_md5(self.original) - if header['content-md5'] != hash_: - raise MWSError("Wrong Contentlength, maybe amazon error...") - - @property - def parsed(self): - return self.original - -class MWS(object): - """ Base Amazon API class """ - - # This is used to post/get to the different uris used by amazon per api - # ie. /Orders/2011-01-01 - # All subclasses must define their own URI only if needed - URI = "/" - - # The API version varies in most amazon APIs - VERSION = "2009-01-01" - - # There seem to be some xml namespace issues. therefore every api subclass - # is recommended to define its namespace, so that it can be referenced - # like so AmazonAPISubclass.NS. - # For more information see http://stackoverflow.com/a/8719461/389453 - NS = '' - - # Some APIs are available only to either a "Merchant" or "Seller" - # the type of account needs to be sent in every call to the amazon MWS. - # This constant defines the exact name of the parameter Amazon expects - # for the specific API being used. - # All subclasses need to define this if they require another account type - # like "Merchant" in which case you define it like so. - # ACCOUNT_TYPE = "Merchant" - # Which is the name of the parameter for that specific account type. - ACCOUNT_TYPE = "SellerId" - - def __init__(self, access_key, secret_key, account_id, region='US', domain='', uri="", version=""): - self.access_key = access_key - self.secret_key = secret_key - self.account_id = account_id - self.version = version or self.VERSION - self.uri = uri or self.URI - - if domain: - self.domain = domain - elif region in MARKETPLACES: - self.domain = MARKETPLACES[region] - else: - error_msg = "Incorrect region supplied ('%(region)s'). Must be one of the following: %(marketplaces)s" % { - "marketplaces" : ', '.join(MARKETPLACES.keys()), - "region" : region, - } - raise MWSError(error_msg) - - def make_request(self, extra_data, method="GET", **kwargs): - """Make request to Amazon MWS API with these parameters - """ - - # Remove all keys with an empty value because - # Amazon's MWS does not allow such a thing. - extra_data = remove_empty(extra_data) - - params = { - 'AWSAccessKeyId': self.access_key, - self.ACCOUNT_TYPE: self.account_id, - 'SignatureVersion': '2', - 'Timestamp': self.get_timestamp(), - 'Version': self.version, - 'SignatureMethod': 'HmacSHA256', - } - params.update(extra_data) - request_description = '&'.join(['%s=%s' % (k, quote(params[k], safe='-_.~')) for k in sorted(params)]) - signature = self.calc_signature(method, request_description) - url = '%s%s?%s&Signature=%s' % (self.domain, self.uri, request_description, quote(signature)) - headers = {'User-Agent': 'python-amazon-mws/0.0.1 (Language=Python)'} - headers.update(kwargs.get('extra_headers', {})) - - try: - # Some might wonder as to why i don't pass the params dict as the params argument to request. - # My answer is, here i have to get the url parsed string of params in order to sign it, so - # if i pass the params dict as params to request, request will repeat that step because it will need - # to convert the dict to a url parsed string, so why do it twice if i can just pass the full url :). - response = request(method, url, data=kwargs.get('body', ''), headers=headers) - response.raise_for_status() - # When retrieving data from the response object, - # be aware that response.content returns the content in bytes while response.text calls - # response.content and converts it to unicode. - data = response.content - - # I do not check the headers to decide which content structure to server simply because sometimes - # Amazon's MWS API returns XML error responses with "text/plain" as the Content-Type. - try: - parsed_response = DictWrapper(data, extra_data.get("Action") + "Result") - except XMLError: - parsed_response = DataWrapper(data, response.headers) - - except HTTPError as e: - error = MWSError(str(e)) - error.response = e.response - raise error - - # Store the response object in the parsed_response for quick access - parsed_response.response = response - return parsed_response - - def get_service_status(self): - """ - Returns a GREEN, GREEN_I, YELLOW or RED status. - Depending on the status/availability of the API its being called from. - """ - - return self.make_request(extra_data=dict(Action='GetServiceStatus')) - - def calc_signature(self, method, request_description): - """Calculate MWS signature to interface with Amazon - """ - sig_data = method + '\n' + self.domain.replace('https://', '').lower() + '\n' + self.uri + '\n' + request_description - sig_data = sig_data.encode('utf-8') - secret_key = self.secret_key.encode('utf-8') - digest = hmac.new(secret_key, sig_data, hashlib.sha256).digest() - return base64.b64encode(digest).decode('utf-8') - - def get_timestamp(self): - """ - Returns the current timestamp in proper format. - """ - return strftime("%Y-%m-%dT%H:%M:%SZ", gmtime()) - - def enumerate_param(self, param, values): - """ - Builds a dictionary of an enumerated parameter. - Takes any iterable and returns a dictionary. - ie. - enumerate_param('MarketplaceIdList.Id', (123, 345, 4343)) - returns - { - MarketplaceIdList.Id.1: 123, - MarketplaceIdList.Id.2: 345, - MarketplaceIdList.Id.3: 4343 - } - """ - params = {} - if values is not None: - if not param.endswith('.'): - param = "%s." % param - for num, value in enumerate(values): - params['%s%d' % (param, (num + 1))] = value - return params - - -class Feeds(MWS): - """ Amazon MWS Feeds API """ - - ACCOUNT_TYPE = "Merchant" - - def submit_feed(self, feed, feed_type, marketplaceids=None, - content_type="text/xml", purge='false'): - """ - Uploads a feed ( xml or .tsv ) to the seller's inventory. - Can be used for creating/updating products on Amazon. - """ - data = dict(Action='SubmitFeed', - FeedType=feed_type, - PurgeAndReplace=purge) - data.update(self.enumerate_param('MarketplaceIdList.Id.', marketplaceids)) - md = calc_md5(feed) - return self.make_request(data, method="POST", body=feed, - extra_headers={'Content-MD5': md, 'Content-Type': content_type}) - - def get_feed_submission_list(self, feedids=None, max_count=None, feedtypes=None, - processingstatuses=None, fromdate=None, todate=None): - """ - Returns a list of all feed submissions submitted in the previous 90 days. - That match the query parameters. - """ - - data = dict(Action='GetFeedSubmissionList', - MaxCount=max_count, - SubmittedFromDate=fromdate, - SubmittedToDate=todate,) - data.update(self.enumerate_param('FeedSubmissionIdList.Id', feedids)) - data.update(self.enumerate_param('FeedTypeList.Type.', feedtypes)) - data.update(self.enumerate_param('FeedProcessingStatusList.Status.', processingstatuses)) - return self.make_request(data) - - def get_submission_list_by_next_token(self, token): - data = dict(Action='GetFeedSubmissionListByNextToken', NextToken=token) - return self.make_request(data) - - def get_feed_submission_count(self, feedtypes=None, processingstatuses=None, fromdate=None, todate=None): - data = dict(Action='GetFeedSubmissionCount', - SubmittedFromDate=fromdate, - SubmittedToDate=todate) - data.update(self.enumerate_param('FeedTypeList.Type.', feedtypes)) - data.update(self.enumerate_param('FeedProcessingStatusList.Status.', processingstatuses)) - return self.make_request(data) - - def cancel_feed_submissions(self, feedids=None, feedtypes=None, fromdate=None, todate=None): - data = dict(Action='CancelFeedSubmissions', - SubmittedFromDate=fromdate, - SubmittedToDate=todate) - data.update(self.enumerate_param('FeedSubmissionIdList.Id.', feedids)) - data.update(self.enumerate_param('FeedTypeList.Type.', feedtypes)) - return self.make_request(data) - - def get_feed_submission_result(self, feedid): - data = dict(Action='GetFeedSubmissionResult', FeedSubmissionId=feedid) - return self.make_request(data) - -class Reports(MWS): - """ Amazon MWS Reports API """ - - ACCOUNT_TYPE = "Merchant" - - ## REPORTS ### - - def get_report(self, report_id): - data = dict(Action='GetReport', ReportId=report_id) - return self.make_request(data) - - def get_report_count(self, report_types=(), acknowledged=None, fromdate=None, todate=None): - data = dict(Action='GetReportCount', - Acknowledged=acknowledged, - AvailableFromDate=fromdate, - AvailableToDate=todate) - data.update(self.enumerate_param('ReportTypeList.Type.', report_types)) - return self.make_request(data) - - def get_report_list(self, requestids=(), max_count=None, types=(), acknowledged=None, - fromdate=None, todate=None): - data = dict(Action='GetReportList', - Acknowledged=acknowledged, - AvailableFromDate=fromdate, - AvailableToDate=todate, - MaxCount=max_count) - data.update(self.enumerate_param('ReportRequestIdList.Id.', requestids)) - data.update(self.enumerate_param('ReportTypeList.Type.', types)) - return self.make_request(data) - - def get_report_list_by_next_token(self, token): - data = dict(Action='GetReportListByNextToken', NextToken=token) - return self.make_request(data) - - def get_report_request_count(self, report_types=(), processingstatuses=(), fromdate=None, todate=None): - data = dict(Action='GetReportRequestCount', - RequestedFromDate=fromdate, - RequestedToDate=todate) - data.update(self.enumerate_param('ReportTypeList.Type.', report_types)) - data.update(self.enumerate_param('ReportProcessingStatusList.Status.', processingstatuses)) - return self.make_request(data) - - def get_report_request_list(self, requestids=(), types=(), processingstatuses=(), - max_count=None, fromdate=None, todate=None): - data = dict(Action='GetReportRequestList', - MaxCount=max_count, - RequestedFromDate=fromdate, - RequestedToDate=todate) - data.update(self.enumerate_param('ReportRequestIdList.Id.', requestids)) - data.update(self.enumerate_param('ReportTypeList.Type.', types)) - data.update(self.enumerate_param('ReportProcessingStatusList.Status.', processingstatuses)) - return self.make_request(data) - - def get_report_request_list_by_next_token(self, token): - data = dict(Action='GetReportRequestListByNextToken', NextToken=token) - return self.make_request(data) - - def request_report(self, report_type, start_date=None, end_date=None, marketplaceids=()): - data = dict(Action='RequestReport', - ReportType=report_type, - StartDate=start_date, - EndDate=end_date) - data.update(self.enumerate_param('MarketplaceIdList.Id.', marketplaceids)) - return self.make_request(data) - - ### ReportSchedule ### - - def get_report_schedule_list(self, types=()): - data = dict(Action='GetReportScheduleList') - data.update(self.enumerate_param('ReportTypeList.Type.', types)) - return self.make_request(data) - - def get_report_schedule_count(self, types=()): - data = dict(Action='GetReportScheduleCount') - data.update(self.enumerate_param('ReportTypeList.Type.', types)) - return self.make_request(data) - - -class Orders(MWS): - """ Amazon Orders API """ - - URI = "/Orders/2013-09-01" - VERSION = "2013-09-01" - NS = '{https://mws.amazonservices.com/Orders/2011-01-01}' - - def list_orders(self, marketplaceids, created_after=None, created_before=None, lastupdatedafter=None, - lastupdatedbefore=None, orderstatus=(), fulfillment_channels=(), - payment_methods=(), buyer_email=None, seller_orderid=None, max_results='100'): - - data = dict(Action='ListOrders', - CreatedAfter=created_after, - CreatedBefore=created_before, - LastUpdatedAfter=lastupdatedafter, - LastUpdatedBefore=lastupdatedbefore, - BuyerEmail=buyer_email, - SellerOrderId=seller_orderid, - MaxResultsPerPage=max_results, - ) - data.update(self.enumerate_param('OrderStatus.Status.', orderstatus)) - data.update(self.enumerate_param('MarketplaceId.Id.', marketplaceids)) - data.update(self.enumerate_param('FulfillmentChannel.Channel.', fulfillment_channels)) - data.update(self.enumerate_param('PaymentMethod.Method.', payment_methods)) - return self.make_request(data) - - def list_orders_by_next_token(self, token): - data = dict(Action='ListOrdersByNextToken', NextToken=token) - return self.make_request(data) - - def get_order(self, amazon_order_ids): - data = dict(Action='GetOrder') - data.update(self.enumerate_param('AmazonOrderId.Id.', amazon_order_ids)) - return self.make_request(data) - - def list_order_items(self, amazon_order_id): - data = dict(Action='ListOrderItems', AmazonOrderId=amazon_order_id) - return self.make_request(data) - - def list_order_items_by_next_token(self, token): - data = dict(Action='ListOrderItemsByNextToken', NextToken=token) - return self.make_request(data) - - -class Products(MWS): - """ Amazon MWS Products API """ - - URI = '/Products/2011-10-01' - VERSION = '2011-10-01' - NS = '{http://mws.amazonservices.com/schema/Products/2011-10-01}' - - def list_matching_products(self, marketplaceid, query, contextid=None): - """ Returns a list of products and their attributes, ordered by - relevancy, based on a search query that you specify. - Your search query can be a phrase that describes the product - or it can be a product identifier such as a UPC, EAN, ISBN, or JAN. - """ - data = dict(Action='ListMatchingProducts', - MarketplaceId=marketplaceid, - Query=query, - QueryContextId=contextid) - return self.make_request(data) - - def get_matching_product(self, marketplaceid, asins): - """ Returns a list of products and their attributes, based on a list of - ASIN values that you specify. - """ - data = dict(Action='GetMatchingProduct', MarketplaceId=marketplaceid) - data.update(self.enumerate_param('ASINList.ASIN.', asins)) - return self.make_request(data) - - def get_matching_product_for_id(self, marketplaceid, type, id): - """ Returns a list of products and their attributes, based on a list of - product identifier values (asin, sellersku, upc, ean, isbn and JAN) - Added in Fourth Release, API version 2011-10-01 - """ - data = dict(Action='GetMatchingProductForId', - MarketplaceId=marketplaceid, - IdType=type) - data.update(self.enumerate_param('IdList.Id', id)) - return self.make_request(data) - - def get_competitive_pricing_for_sku(self, marketplaceid, skus): - """ Returns the current competitive pricing of a product, - based on the SellerSKU and MarketplaceId that you specify. - """ - data = dict(Action='GetCompetitivePricingForSKU', MarketplaceId=marketplaceid) - data.update(self.enumerate_param('SellerSKUList.SellerSKU.', skus)) - return self.make_request(data) - - def get_competitive_pricing_for_asin(self, marketplaceid, asins): - """ Returns the current competitive pricing of a product, - based on the ASIN and MarketplaceId that you specify. - """ - data = dict(Action='GetCompetitivePricingForASIN', MarketplaceId=marketplaceid) - data.update(self.enumerate_param('ASINList.ASIN.', asins)) - return self.make_request(data) - - def get_lowest_offer_listings_for_sku(self, marketplaceid, skus, condition="Any", excludeme="False"): - data = dict(Action='GetLowestOfferListingsForSKU', - MarketplaceId=marketplaceid, - ItemCondition=condition, - ExcludeMe=excludeme) - data.update(self.enumerate_param('SellerSKUList.SellerSKU.', skus)) - return self.make_request(data) - - def get_lowest_offer_listings_for_asin(self, marketplaceid, asins, condition="Any", excludeme="False"): - data = dict(Action='GetLowestOfferListingsForASIN', - MarketplaceId=marketplaceid, - ItemCondition=condition, - ExcludeMe=excludeme) - data.update(self.enumerate_param('ASINList.ASIN.', asins)) - return self.make_request(data) - - def get_product_categories_for_sku(self, marketplaceid, sku): - data = dict(Action='GetProductCategoriesForSKU', - MarketplaceId=marketplaceid, - SellerSKU=sku) - return self.make_request(data) - - def get_product_categories_for_asin(self, marketplaceid, asin): - data = dict(Action='GetProductCategoriesForASIN', - MarketplaceId=marketplaceid, - ASIN=asin) - return self.make_request(data) - - def get_my_price_for_sku(self, marketplaceid, skus, condition=None): - data = dict(Action='GetMyPriceForSKU', - MarketplaceId=marketplaceid, - ItemCondition=condition) - data.update(self.enumerate_param('SellerSKUList.SellerSKU.', skus)) - return self.make_request(data) - - def get_my_price_for_asin(self, marketplaceid, asins, condition=None): - data = dict(Action='GetMyPriceForASIN', - MarketplaceId=marketplaceid, - ItemCondition=condition) - data.update(self.enumerate_param('ASINList.ASIN.', asins)) - return self.make_request(data) - - -class Sellers(MWS): - """ Amazon MWS Sellers API """ - - URI = '/Sellers/2011-07-01' - VERSION = '2011-07-01' - NS = '{http://mws.amazonservices.com/schema/Sellers/2011-07-01}' - - def list_marketplace_participations(self): - """ - Returns a list of marketplaces a seller can participate in and - a list of participations that include seller-specific information in that marketplace. - The operation returns only those marketplaces where the seller's account is in an active state. - """ - - data = dict(Action='ListMarketplaceParticipations') - return self.make_request(data) - - def list_marketplace_participations_by_next_token(self, token): - """ - Takes a "NextToken" and returns the same information as "list_marketplace_participations". - Based on the "NextToken". - """ - data = dict(Action='ListMarketplaceParticipations', NextToken=token) - return self.make_request(data) - -#### Fulfillment APIs #### - -class InboundShipments(MWS): - URI = "/FulfillmentInboundShipment/2010-10-01" - VERSION = '2010-10-01' - - # To be completed - - -class Inventory(MWS): - """ Amazon MWS Inventory Fulfillment API """ - - URI = '/FulfillmentInventory/2010-10-01' - VERSION = '2010-10-01' - NS = "{http://mws.amazonaws.com/FulfillmentInventory/2010-10-01}" - - def list_inventory_supply(self, skus=(), datetime=None, response_group='Basic'): - """ Returns information on available inventory """ - - data = dict(Action='ListInventorySupply', - QueryStartDateTime=datetime, - ResponseGroup=response_group, - ) - data.update(self.enumerate_param('SellerSkus.member.', skus)) - return self.make_request(data, "POST") - - def list_inventory_supply_by_next_token(self, token): - data = dict(Action='ListInventorySupplyByNextToken', NextToken=token) - return self.make_request(data, "POST") - - -class OutboundShipments(MWS): - URI = "/FulfillmentOutboundShipment/2010-10-01" - VERSION = "2010-10-01" - # To be completed - - -class Recommendations(MWS): - - """ Amazon MWS Recommendations API """ - - URI = '/Recommendations/2013-04-01' - VERSION = '2013-04-01' - NS = "{https://mws.amazonservices.com/Recommendations/2013-04-01}" - - def get_last_updated_time_for_recommendations(self, marketplaceid): - """ - Checks whether there are active recommendations for each category for the given marketplace, and if there are, - returns the time when recommendations were last updated for each category. - """ - - data = dict(Action='GetLastUpdatedTimeForRecommendations', - MarketplaceId=marketplaceid) - return self.make_request(data, "POST") - - def list_recommendations(self, marketplaceid, recommendationcategory=None): - """ - Returns your active recommendations for a specific category or for all categories for a specific marketplace. - """ - - data = dict(Action="ListRecommendations", - MarketplaceId=marketplaceid, - RecommendationCategory=recommendationcategory) - return self.make_request(data, "POST") - - def list_recommendations_by_next_token(self, token): - """ - Returns the next page of recommendations using the NextToken parameter. - """ - - data = dict(Action="ListRecommendationsByNextToken", - NextToken=token) - return self.make_request(data, "POST") - -class Finances(MWS): - """ Amazon Finances API""" - URI = '/Finances/2015-05-01' - VERSION = '2015-05-01' - NS = "{https://mws.amazonservices.com/Finances/2015-05-01}" - - def list_financial_events(self , posted_after=None, posted_before=None, - amazon_order_id=None, max_results='100'): - - data = dict(Action='ListFinancialEvents', - PostedAfter=posted_after, - PostedBefore=posted_before, - AmazonOrderId=amazon_order_id, - MaxResultsPerPage=max_results, - ) - return self.make_request(data) diff --git a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_settings.js b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_settings.js deleted file mode 100644 index f5ea8047c6a1..000000000000 --- a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_settings.js +++ /dev/null @@ -1,2 +0,0 @@ -// Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and contributors -// For license information, please see license.txt diff --git a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_settings.json b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_settings.json deleted file mode 100644 index 5a678e77d163..000000000000 --- a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_settings.json +++ /dev/null @@ -1,237 +0,0 @@ -{ - "actions": [], - "creation": "2018-07-31 05:51:41.357047", - "doctype": "DocType", - "editable_grid": 1, - "engine": "InnoDB", - "field_order": [ - "enable_amazon", - "mws_credentials", - "seller_id", - "aws_access_key_id", - "mws_auth_token", - "secret_key", - "column_break_4", - "market_place_id", - "region", - "domain", - "section_break_13", - "company", - "warehouse", - "item_group", - "price_list", - "column_break_17", - "customer_group", - "territory", - "customer_type", - "market_place_account_group", - "section_break_12", - "after_date", - "taxes_charges", - "sync_products", - "sync_orders", - "column_break_10", - "enable_sync", - "max_retry_limit" - ], - "fields": [ - { - "default": "0", - "fieldname": "enable_amazon", - "fieldtype": "Check", - "label": "Enable Amazon" - }, - { - "fieldname": "mws_credentials", - "fieldtype": "Section Break", - "label": "MWS Credentials" - }, - { - "fieldname": "seller_id", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Seller ID", - "reqd": 1 - }, - { - "fieldname": "aws_access_key_id", - "fieldtype": "Data", - "in_list_view": 1, - "label": "AWS Access Key ID", - "reqd": 1 - }, - { - "fieldname": "mws_auth_token", - "fieldtype": "Data", - "in_list_view": 1, - "label": "MWS Auth Token", - "reqd": 1 - }, - { - "fieldname": "secret_key", - "fieldtype": "Data", - "in_list_view": 1, - "label": "Secret Key", - "reqd": 1 - }, - { - "fieldname": "column_break_4", - "fieldtype": "Column Break" - }, - { - "fieldname": "market_place_id", - "fieldtype": "Data", - "label": "Market Place ID", - "reqd": 1 - }, - { - "fieldname": "region", - "fieldtype": "Select", - "label": "Region", - "options": "\nAE\nAU\nBR\nCA\nCN\nDE\nES\nFR\nIN\nJP\nIT\nMX\nUK\nUS", - "reqd": 1 - }, - { - "fieldname": "domain", - "fieldtype": "Data", - "label": "Domain", - "reqd": 1 - }, - { - "fieldname": "section_break_13", - "fieldtype": "Section Break" - }, - { - "fieldname": "company", - "fieldtype": "Link", - "label": "Company", - "options": "Company", - "reqd": 1 - }, - { - "fieldname": "warehouse", - "fieldtype": "Link", - "label": "Warehouse", - "options": "Warehouse", - "reqd": 1 - }, - { - "fieldname": "item_group", - "fieldtype": "Link", - "label": "Item Group", - "options": "Item Group", - "reqd": 1 - }, - { - "fieldname": "price_list", - "fieldtype": "Link", - "label": "Price List", - "options": "Price List", - "reqd": 1 - }, - { - "fieldname": "column_break_17", - "fieldtype": "Column Break" - }, - { - "fieldname": "customer_group", - "fieldtype": "Link", - "label": "Customer Group", - "options": "Customer Group", - "reqd": 1 - }, - { - "fieldname": "territory", - "fieldtype": "Link", - "label": "Territory", - "options": "Territory", - "reqd": 1 - }, - { - "fieldname": "customer_type", - "fieldtype": "Select", - "label": "Customer Type", - "options": "Individual\nCompany", - "reqd": 1 - }, - { - "fieldname": "market_place_account_group", - "fieldtype": "Link", - "label": "Market Place Account Group", - "options": "Account", - "reqd": 1 - }, - { - "fieldname": "section_break_12", - "fieldtype": "Section Break" - }, - { - "description": "Amazon will synch data updated after this date", - "fieldname": "after_date", - "fieldtype": "Datetime", - "label": "After Date", - "reqd": 1 - }, - { - "default": "0", - "description": "Get financial breakup of Taxes and charges data by Amazon ", - "fieldname": "taxes_charges", - "fieldtype": "Check", - "label": "Sync Taxes and Charges" - }, - { - "fieldname": "column_break_10", - "fieldtype": "Column Break" - }, - { - "default": "3", - "fieldname": "max_retry_limit", - "fieldtype": "Int", - "label": "Max Retry Limit" - }, - { - "description": "Always sync your products from Amazon MWS before synching the Orders details", - "fieldname": "sync_products", - "fieldtype": "Button", - "label": "Sync Products", - "options": "get_products_details" - }, - { - "description": "Click this button to pull your Sales Order data from Amazon MWS.", - "fieldname": "sync_orders", - "fieldtype": "Button", - "label": "Sync Orders", - "options": "get_order_details" - }, - { - "default": "0", - "description": "Check this to enable a scheduled Daily synchronization routine via scheduler", - "fieldname": "enable_sync", - "fieldtype": "Check", - "label": "Enable Scheduled Sync" - } - ], - "issingle": 1, - "links": [], - "modified": "2020-04-07 14:26:20.174848", - "modified_by": "Administrator", - "module": "ERPNext Integrations", - "name": "Amazon MWS Settings", - "owner": "Administrator", - "permissions": [ - { - "create": 1, - "delete": 1, - "email": 1, - "print": 1, - "read": 1, - "role": "System Manager", - "share": 1, - "write": 1 - } - ], - "quick_entry": 1, - "sort_field": "modified", - "sort_order": "DESC", - "track_changes": 1 -} \ No newline at end of file diff --git a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_settings.py b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_settings.py deleted file mode 100644 index c1f460f49b6d..000000000000 --- a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/amazon_mws_settings.py +++ /dev/null @@ -1,46 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies and contributors -# For license information, please see license.txt - - -import dateutil -import frappe -from frappe.custom.doctype.custom_field.custom_field import create_custom_fields -from frappe.model.document import Document - -from erpnext.erpnext_integrations.doctype.amazon_mws_settings.amazon_methods import get_orders - - -class AmazonMWSSettings(Document): - def validate(self): - if self.enable_amazon == 1: - self.enable_sync = 1 - setup_custom_fields() - else: - self.enable_sync = 0 - - @frappe.whitelist() - def get_products_details(self): - if self.enable_amazon == 1: - frappe.enqueue('erpnext.erpnext_integrations.doctype.amazon_mws_settings.amazon_methods.get_products_details') - - @frappe.whitelist() - def get_order_details(self): - if self.enable_amazon == 1: - after_date = dateutil.parser.parse(self.after_date).strftime("%Y-%m-%d") - frappe.enqueue('erpnext.erpnext_integrations.doctype.amazon_mws_settings.amazon_methods.get_orders', after_date=after_date) - -def schedule_get_order_details(): - mws_settings = frappe.get_doc("Amazon MWS Settings") - if mws_settings.enable_sync and mws_settings.enable_amazon: - after_date = dateutil.parser.parse(mws_settings.after_date).strftime("%Y-%m-%d") - get_orders(after_date = after_date) - -def setup_custom_fields(): - custom_fields = { - "Item": [dict(fieldname='amazon_item_code', label='Amazon Item Code', - fieldtype='Data', insert_after='series', read_only=1, print_hide=1)], - "Sales Order": [dict(fieldname='amazon_order_id', label='Amazon Order ID', - fieldtype='Data', insert_after='title', read_only=1, print_hide=1)] - } - - create_custom_fields(custom_fields) diff --git a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/test_amazon_mws_settings.py b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/test_amazon_mws_settings.py deleted file mode 100644 index 4be7960deda2..000000000000 --- a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/test_amazon_mws_settings.py +++ /dev/null @@ -1,8 +0,0 @@ -# Copyright (c) 2018, Frappe Technologies Pvt. Ltd. and Contributors -# See license.txt - -import unittest - - -class TestAmazonMWSSettings(unittest.TestCase): - pass diff --git a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/xml_utils.py b/erpnext/erpnext_integrations/doctype/amazon_mws_settings/xml_utils.py deleted file mode 100644 index d9dfc6f72d45..000000000000 --- a/erpnext/erpnext_integrations/doctype/amazon_mws_settings/xml_utils.py +++ /dev/null @@ -1,104 +0,0 @@ -""" -Created on Tue Jun 26 15:42:07 2012 - -Borrowed from https://github.com/timotheus/ebaysdk-python - -@author: pierre -""" - -import re -import xml.etree.ElementTree as ET - - -class object_dict(dict): - """object view of dict, you can - >>> a = object_dict() - >>> a.fish = 'fish' - >>> a['fish'] - 'fish' - >>> a['water'] = 'water' - >>> a.water - 'water' - >>> a.test = {'value': 1} - >>> a.test2 = object_dict({'name': 'test2', 'value': 2}) - >>> a.test, a.test2.name, a.test2.value - (1, 'test2', 2) - """ - def __init__(self, initd=None): - if initd is None: - initd = {} - dict.__init__(self, initd) - - def __getattr__(self, item): - - try: - d = self.__getitem__(item) - except KeyError: - return None - - if isinstance(d, dict) and 'value' in d and len(d) == 1: - return d['value'] - else: - return d - - # if value is the only key in object, you can omit it - def __setstate__(self, item): - return False - - def __setattr__(self, item, value): - self.__setitem__(item, value) - - def getvalue(self, item, value=None): - return self.get(item, {}).get('value', value) - - -class xml2dict(object): - - def __init__(self): - pass - - def _parse_node(self, node): - node_tree = object_dict() - # Save attrs and text, hope there will not be a child with same name - if node.text: - node_tree.value = node.text - for (k, v) in node.attrib.items(): - k, v = self._namespace_split(k, object_dict({'value':v})) - node_tree[k] = v - #Save childrens - for child in node.getchildren(): - tag, tree = self._namespace_split(child.tag, - self._parse_node(child)) - if tag not in node_tree: # the first time, so store it in dict - node_tree[tag] = tree - continue - old = node_tree[tag] - if not isinstance(old, list): - node_tree.pop(tag) - node_tree[tag] = [old] # multi times, so change old dict to a list - node_tree[tag].append(tree) # add the new one - - return node_tree - - def _namespace_split(self, tag, value): - """ - Split the tag '{http://cs.sfsu.edu/csc867/myscheduler}patients' - ns = http://cs.sfsu.edu/csc867/myscheduler - name = patients - """ - result = re.compile(r"\{(.*)\}(.*)").search(tag) - if result: - value.namespace, tag = result.groups() - - return (tag, value) - - def parse(self, file): - """parse a xml file to a dict""" - f = open(file, 'r') - return self.fromstring(f.read()) - - def fromstring(self, s): - """parse a string""" - t = ET.fromstring(s) - root_tag, root_tree = self._namespace_split(t.tag, self._parse_node(t)) - return object_dict({root_tag: root_tree}) diff --git a/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json b/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json index 45077aa66c3f..1f2619b9a6eb 100644 --- a/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json +++ b/erpnext/erpnext_integrations/workspace/erpnext_integrations/erpnext_integrations.json @@ -29,17 +29,6 @@ "onboard": 0, "type": "Link" }, - { - "dependencies": "", - "hidden": 0, - "is_query_report": 0, - "label": "Amazon MWS Settings", - "link_count": 0, - "link_to": "Amazon MWS Settings", - "link_type": "DocType", - "onboard": 0, - "type": "Link" - }, { "hidden": 0, "is_query_report": 0, diff --git a/erpnext/hooks.py b/erpnext/hooks.py index d99f23ed64e7..38fa6916a513 100644 --- a/erpnext/hooks.py +++ b/erpnext/hooks.py @@ -333,7 +333,6 @@ "hourly": [ 'erpnext.hr.doctype.daily_work_summary_group.daily_work_summary_group.trigger_emails', "erpnext.accounts.doctype.subscription.subscription.process_all", - "erpnext.erpnext_integrations.doctype.amazon_mws_settings.amazon_mws_settings.schedule_get_order_details", "erpnext.accounts.doctype.gl_entry.gl_entry.rename_gle_sle_docs", "erpnext.erpnext_integrations.doctype.plaid_settings.plaid_settings.automatic_synchronization", "erpnext.projects.doctype.project.project.hourly_reminder", diff --git a/erpnext/patches.txt b/erpnext/patches.txt index d30034067103..d104bc003c80 100644 --- a/erpnext/patches.txt +++ b/erpnext/patches.txt @@ -351,3 +351,4 @@ erpnext.patches.v13_0.convert_to_website_item_in_item_card_group_template erpnext.patches.v13_0.shopping_cart_to_ecommerce erpnext.patches.v13_0.update_disbursement_account erpnext.patches.v13_0.update_reserved_qty_closed_wo +erpnext.patches.v14_0.delete_amazon_mws_doctype diff --git a/erpnext/patches/v12_0/rename_mws_settings_fields.py b/erpnext/patches/v12_0/rename_mws_settings_fields.py deleted file mode 100644 index d5bf38d204d2..000000000000 --- a/erpnext/patches/v12_0/rename_mws_settings_fields.py +++ /dev/null @@ -1,12 +0,0 @@ -# Copyright (c) 2020, Frappe and Contributors -# License: GNU General Public License v3. See license.txt - -import frappe - - -def execute(): - count = frappe.db.sql("SELECT COUNT(*) FROM `tabSingles` WHERE doctype='Amazon MWS Settings' AND field='enable_sync';")[0][0] - if count == 0: - frappe.db.sql("UPDATE `tabSingles` SET field='enable_sync' WHERE doctype='Amazon MWS Settings' AND field='enable_synch';") - - frappe.reload_doc("ERPNext Integrations", "doctype", "Amazon MWS Settings") diff --git a/erpnext/patches/v14_0/delete_amazon_mws_doctype.py b/erpnext/patches/v14_0/delete_amazon_mws_doctype.py new file mode 100644 index 000000000000..525da6cbe5a2 --- /dev/null +++ b/erpnext/patches/v14_0/delete_amazon_mws_doctype.py @@ -0,0 +1,5 @@ +import frappe + + +def execute(): + frappe.delete_doc("DocType", "Amazon MWS Settings", ignore_missing=True) \ No newline at end of file