├── .gitignore ├── CODE_OF_CONDUCT.md ├── README.md ├── baselinker ├── __init__.py ├── baselinker.py ├── external_storages.py ├── orders.py ├── product_catalog.py └── request.py ├── main.py ├── pyproject.toml ├── requirements.txt └── tests ├── __init__.py ├── test_baselinker.py ├── test_external_storages.py ├── test_orders.py ├── test_product_catalog.py └── test_request.py /.gitignore: -------------------------------------------------------------------------------- 1 | Environments 2 | .env 3 | .venv 4 | env/ 5 | venv/ 6 | ENV/ 7 | env.bak/ 8 | venv.bak/ -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our community a harassment-free experience for everyone, regardless of age, body size, visible or invisible disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. 6 | 7 | We pledge to act and interact in ways that contribute to an open, welcoming, diverse, inclusive, and healthy community. 8 | 9 | ## Our Standards 10 | 11 | Examples of behavior that contributes to a positive environment for our community include: 12 | 13 | * Demonstrating empathy and kindness toward other people 14 | * Being respectful of differing opinions, viewpoints, and experiences 15 | * Giving and gracefully accepting constructive feedback 16 | * Accepting responsibility and apologizing to those affected by our mistakes, and learning from the experience 17 | * Focusing on what is best not just for us as individuals, but for the overall community 18 | 19 | Examples of unacceptable behavior include: 20 | 21 | * The use of sexualized language or imagery, and sexual attention or 22 | advances of any kind 23 | * Trolling, insulting or derogatory comments, and personal or political attacks 24 | * Public or private harassment 25 | * Publishing others' private information, such as a physical or email 26 | address, without their explicit permission 27 | * Other conduct which could reasonably be considered inappropriate in a 28 | professional setting 29 | 30 | ## Enforcement Responsibilities 31 | 32 | Community leaders are responsible for clarifying and enforcing our standards of acceptable behavior and will take appropriate and fair corrective action in response to any behavior that they deem inappropriate, threatening, offensive, or harmful. 33 | 34 | Community leaders have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, and will communicate reasons for moderation decisions when appropriate. 35 | 36 | ## Scope 37 | 38 | This Code of Conduct applies within all community spaces, and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. 39 | 40 | ## Enforcement 41 | 42 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported to the community leaders responsible for enforcement at michalkulisiewicz@gmail.com. All complaints will be reviewed and investigated promptly and fairly. 43 | 44 | All community leaders are obligated to respect the privacy and security of the reporter of any incident. 45 | 46 | ## Enforcement Guidelines 47 | 48 | Community leaders will follow these Community Impact Guidelines in determining the consequences for any action they deem in violation of this Code of Conduct: 49 | 50 | ### 1. Correction 51 | 52 | **Community Impact**: Use of inappropriate language or other behavior deemed unprofessional or unwelcome in the community. 53 | 54 | **Consequence**: A private, written warning from community leaders, providing clarity around the nature of the violation and an explanation of why the behavior was inappropriate. A public apology may be requested. 55 | 56 | ### 2. Warning 57 | 58 | **Community Impact**: A violation through a single incident or series of actions. 59 | 60 | **Consequence**: A warning with consequences for continued behavior. No interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, for a specified period of time. This includes avoiding interactions in community spaces as well as external channels like social media. Violating these terms may lead to a temporary or permanent ban. 61 | 62 | ### 3. Temporary Ban 63 | 64 | **Community Impact**: A serious violation of community standards, including sustained inappropriate behavior. 65 | 66 | **Consequence**: A temporary ban from any sort of interaction or public communication with the community for a specified period of time. No public or private interaction with the people involved, including unsolicited interaction with those enforcing the Code of Conduct, is allowed during this period. Violating these terms may lead to a permanent ban. 67 | 68 | ### 4. Permanent Ban 69 | 70 | **Community Impact**: Demonstrating a pattern of violation of community standards, including sustained inappropriate behavior, harassment of an individual, or aggression toward or disparagement of classes of individuals. 71 | 72 | **Consequence**: A permanent ban from any sort of public interaction within the community. 73 | 74 | ## Attribution 75 | 76 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 2.0, 77 | available at https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 78 | 79 | Community Impact Guidelines were inspired by [Mozilla's code of conduct enforcement ladder](https://github.com/mozilla/diversity). 80 | 81 | [homepage]: https://www.contributor-covenant.org 82 | 83 | For answers to common questions about this code of conduct, see the FAQ at 84 | https://www.contributor-covenant.org/faq. Translations are available at https://www.contributor-covenant.org/translations. 85 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | A Python client library for accessing baselinker API. 4 | List of the methods available [here](https://api.baselinker.com/) 5 | 6 | ## Installation 7 | Use git clone to get the library 8 | ``` 9 | git clone https://github.com/michalkulisiewicz/python-baselinker.git 10 | ``` 11 | Install all requirements using pip 12 | ```python 13 | pip install -r requirements.txt 14 | ``` 15 | 16 | ## Quickstart 17 | The token is assigned directly to the BaseLinker user account. User can generate API token in BaseLinker panel in "Account & other -> My account -> API" section. 18 | 19 | ```python 20 | from baselinker import Baselinker 21 | 22 | API_TOKEN = 'INSERT YOUR TOKEN HERE' 23 | 24 | 25 | def run(): 26 | # Create a baselinker client instance 27 | baselinker = Baselinker(API_TOKEN) 28 | # Prints 100 orders from baselinker 29 | print(baselinker.orders.get_orders()) 30 | 31 | 32 | if __name__ == '__main__': 33 | run() 34 | ``` 35 | 36 | # Usage 37 | ### Product catalog 38 | ```python 39 | baselinker.orders.get_orders() 40 | ``` 41 | **Sample response:** 42 | ```python 43 | { 44 | "status": "SUCCESS", 45 | "price_groups": [ 46 | { 47 | "price_group_id": 104, 48 | "name": "Default", 49 | "description": "Default price group", 50 | "currency": "EUR", 51 | "is_default": true 52 | }, 53 | { 54 | "price_group_id": 105, 55 | "name": "USA", 56 | "description": "Price group for US market", 57 | "currency": "USD", 58 | "is_default": false 59 | } 60 | ] 61 | } 62 | ``` 63 | 64 | ### External storages 65 | ```python 66 | baselinker.external_storages.get_external_storages_list() 67 | ``` 68 | **Sample response:** 69 | ```python 70 | { 71 | "status": "SUCCESS", 72 | "storages": [ 73 | { 74 | "storage_id": "shop_2444", 75 | "name": "Online store", 76 | "methods": ["getExternalStorageCategories", "getExternalStorageProductsData", "getExternalStorageProductsList", "getExternalStorageProductsPrices", "getExternalStorageProductsQuantity", "updateExternalStorageProductsQuantity"] 77 | }, 78 | { 79 | "storage_id": "warehouse_1334", 80 | "name": "Wholesaler", 81 | "methods": ["getExternalStorageCategories", "getExternalStorageProductsData", "getExternalStorageProductsList", "getExternalStorageProductsPrices", "getExternalStorageProductsQuantity"] 82 | }, 83 | ] 84 | } 85 | ``` 86 | 87 | ### Orders 88 | ```python 89 | baselinker.orders.get_orders() 90 | ``` 91 | **Sample response:** 92 | ```python 93 | { 94 | "status": "SUCCESS", 95 | "orders": [ 96 | { 97 | "order_id": "1630473", 98 | "shop_order_id": "2824", 99 | "external_order_id": "534534234", 100 | "order_source": "amazon", 101 | "order_source_id": "2598", 102 | "order_source_info": "-", 103 | "order_status_id": "6624", 104 | "date_add": "1407841161", 105 | "date_confirmed": "1407841256", 106 | "date_in_status": "1407841256", 107 | "user_login": "nick123", 108 | "phone": "693123123", 109 | "email": "test@test.com", 110 | "user_comments": "User comment", 111 | "admin_comments": "Seller test comments", 112 | "currency": "GBP", 113 | "payment_method": "PayPal", 114 | "payment_method_cod": "0", 115 | "payment_done": "50", 116 | "delivery_method": "Expedited shipping", 117 | "delivery_price": "10", 118 | "delivery_package_module": "other", 119 | "delivery_package_nr": "0042348723648234", 120 | "delivery_fullname": "John Doe", 121 | "delivery_company": "Company", 122 | "delivery_address": "Long Str 12", 123 | "delivery_city": "London", 124 | "delivery_postcode": "E2 8HQ", 125 | "delivery_country": "Great Britain", 126 | "delivery_point_id": "", 127 | "delivery_point_name": "", 128 | "delivery_point_address": "", 129 | "delivery_point_postcode": "", 130 | "delivery_point_city": "", 131 | "invoice_fullname": "John Doe", 132 | "invoice_company": "Company", 133 | "invoice_nip": "GB8943245", 134 | "invoice_address": "Long Str 12", 135 | "invoice_city": "London", 136 | "invoice_postcode": "E2 8HQ", 137 | "invoice_country": "Great Britain", 138 | "want_invoice": "0", 139 | "extra_field_1": "", 140 | "extra_field_2": "", 141 | "custom_extra_fields": { 142 | "135": "B2B", 143 | "172": "1646913115" 144 | }, 145 | "order_page": "https://klient.baselinker.com/1630473/4ceca0d940/", 146 | "pick_status": "1", 147 | "pack_status": "0", 148 | "products": [ 149 | { 150 | "storage": "shop" 151 | "storage_id": 1, 152 | "order_product_id": "154904741", 153 | "product_id": "5434", 154 | "variant_id": 52124, 155 | "name": "Harry Potter and the Chamber of Secrets", 156 | "attributes": "Colour: green", 157 | "sku": "LU4235", 158 | "ean": "1597368451236", 159 | "location": "A1-13-7", 160 | "auction_id": "0", 161 | "price_brutto": 20.00, 162 | "tax_rate": 23, 163 | "quantity": 2, 164 | "weight": 1, 165 | "bundle_id": 0 166 | } 167 | ] 168 | } 169 | ] 170 | } 171 | ``` 172 | 173 | ## Contributing 174 | 175 | Bug reports and pull requests are welcome on GitHub at https://github.com/michalkulisiewicz/python-baselinker. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/michalkulisiewicz/python-baselinker/blob/master/CODE_OF_CONDUCT.md). 176 | 177 | ## License 178 | 179 | Project is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). 180 | 181 | ## Code of Conduct 182 | 183 | Everyone that interacts in the project codebase, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/michalkulisiewicz/python-baselinker/blob/master/CODE_OF_CONDUCT.md) 184 | -------------------------------------------------------------------------------- /baselinker/__init__.py: -------------------------------------------------------------------------------- 1 | from .baselinker import Baselinker 2 | from .request import Request 3 | -------------------------------------------------------------------------------- /baselinker/baselinker.py: -------------------------------------------------------------------------------- 1 | from .request import Request 2 | from .orders import Orders 3 | from .external_storages import ExternalStorages 4 | from .product_catalog import ProductCatalog 5 | 6 | 7 | class Baselinker: 8 | """Baselinker API client""" 9 | 10 | def __init__(self, api_token): 11 | self.api_token = api_token 12 | self.request = Request(self.api_token) 13 | self.orders = Orders(self.api_token) 14 | self.external_storages = ExternalStorages(self.api_token) 15 | self.product_catalog = ProductCatalog(self.api_token) 16 | -------------------------------------------------------------------------------- /baselinker/external_storages.py: -------------------------------------------------------------------------------- 1 | from .request import Request 2 | 3 | 4 | class ExternalStorages: 5 | def __init__(self, api_token): 6 | self.api_token = api_token 7 | self.request = Request(self.api_token) 8 | 9 | def get_external_storages_list(self): 10 | """ 11 | The method allows you to retrieve a list of available external storages (shops, wholesalers) 12 | that can be referenced via API. 13 | """ 14 | return self.request.make_request('getExternalStoragesList') 15 | 16 | def get_external_storage_categories(self, storage_id): 17 | """ 18 | The method allows you to retrieve a category list from an external storage (shop/wholesale) 19 | connected to BaseLinker. 20 | Keywords: 21 | storage_id varchar(30): (required) Storage ID in format "[type:shop|warehouse]_[id:int]" (e.g. "shop_2445"). 22 | """ 23 | return self.request.make_request('getExternalStorageCategories', storage_id=storage_id) 24 | 25 | def get_external_storage_products_data(self, storage_id, products): 26 | """ 27 | The method allows you to retrieve a category list from an external storage (shop/wholesale) 28 | connected to BaseLinker. 29 | Keywords: 30 | storage_id varchar(30): (required) Storage ID in format "[type:shop|warehouse]_[id:int]" (e.g. "shop_2445"). 31 | products array: (required) An array of product ID numbers to download 32 | """ 33 | return self.request.make_request('getExternalStorageProductsData', storage_id=storage_id, products=products) 34 | 35 | def get_external_storage_products_list(self, storage_id, filter_category_id=None, filter_sort=None, filter_id=None, 36 | filter_ean=None, filter_sku=None, filter_name=None, filter_price_from=None, 37 | filter_price_to=None, filter_quantity_from=None, filter_quantity_to=None, 38 | filter_available=None, page=None): 39 | """ 40 | The method allows you to retrieve a category list from an external storage (shop/wholesale) 41 | connected to BaseLinker. 42 | Keywords: 43 | storage_id varchar(30): (required) Storage ID in format "[type:shop|warehouse]_[id:int]" (e.g. "shop_2445"). 44 | filter_category_id varchar(30): (optional) Retrieving products from a specific category 45 | filter_sort varchar(30): (optional) the value for sorting the product list. 46 | Possible values: "id [ASC|DESC]", "name [ASC|DESC]", "quantity [ASC|DESC]", "price [ASC|DESC]" 47 | filter_id varchar(30): (optional) limiting results to a specific product id 48 | filter_ean varchar(320): (optional) limiting results to a specific ean 49 | filter_sku varchar(32): (optional) limiting the results to a specific SKU (stock keeping number) 50 | filter_name varchar(100): (optional) item name filter (part of the searched name or an empty field) 51 | filter_price_from (float): (optional) minimum price limit (not displaying products with lower price) 52 | filter_price_to (float): (optional) maximum price limit 53 | filter_quantity_from (int): (optional) maximum price limit 54 | filter_quantity_to (int): (optional) maximum quantity limit 55 | filter_available (int): (optional) displaying only products marked as available (value 1) or not available (0) or all (empty value) 56 | page (int): (optional) pagination 57 | """ 58 | return self.request.make_request('getExternalStorageProductsList', storage_id=storage_id, 59 | filter_category_id=filter_category_id, 60 | filter_sort=filter_sort, filter_id=filter_id, filter_ean=filter_ean, 61 | filter_sku=filter_sku, filter_name=filter_name, 62 | filter_price_from=filter_price_from, 63 | filter_price_to=filter_price_to, filter_quantity_from=filter_quantity_from, 64 | filter_quantity_to=filter_quantity_to, filter_available=filter_available, 65 | page=page) 66 | 67 | def get_external_storage_products_quantity(self, storage_id, page): 68 | """ 69 | The method allows you to retrieve a category list from an external storage (shop/wholesale) 70 | connected to BaseLinker. 71 | Keywords: 72 | storage_id varchar(30): (required) Storage ID in format "[type:shop|warehouse]_[id:int]" (e.g. "shop_2445"). 73 | page (int): (optional) pagination 74 | """ 75 | return self.request.make_request('getExternalStorageProductsQuantity', storage_id=storage_id, page=page) 76 | 77 | def get_external_storage_products_prices(self, storage_id, page): 78 | """ 79 | The method allows you to retrieve a category list from an external storage (shop/wholesale) 80 | connected to BaseLinker. 81 | Keywords: 82 | storage_id varchar(30): (required) Storage ID in format "[type:shop|warehouse]_[id:int]" (e.g. "shop_2445"). 83 | page (int): (optional) pagination 84 | """ 85 | return self.request.make_request('getExternalStorageProductsPrices', storage_id=storage_id, page=page) 86 | 87 | def update_external_storage_products_quantity(self, storage_id, products): 88 | """ 89 | The method allows to bulk update the product stock (and/or variants) in an external storage (shop/wholesaler) 90 | connected to BaseLinker. Maximum 1000 products at a time. 91 | Keywords: 92 | storage_id varchar(30): (required) Storage ID in format "[type:shop|warehouse]_[id:int]" (e.g. "shop_2445"). 93 | products array: (required) An array of products. Each product is a separate element of the array. 94 | The product consists of a 3 element array of components: 95 | 0 => product ID number (varchar) 96 | 1 => variant ID number (0 if the main product is changed, not the variant) (int) 97 | 2 => Stock quantity (int) 98 | """ 99 | return self.request.make_request('updateExternalStorageProductsQuantity', storage_id=storage_id, 100 | products=products) -------------------------------------------------------------------------------- /baselinker/orders.py: -------------------------------------------------------------------------------- 1 | from .request import Request 2 | 3 | 4 | class Orders: 5 | def __init__(self, api_token): 6 | self.api_token = api_token 7 | self.request = Request(self.api_token) 8 | 9 | def get_journal_list(self, last_log_id, logs_types, order_id): 10 | """ 11 | The method allows you to download a list of order events from the last 3 days. 12 | Keywords: 13 | last_log_id (int): (required) Log ID number from which the logs are to be retrieved 14 | logs_types (array): (required) Event ID List 15 | order_id (int): (required) Order ID number 16 | """ 17 | return self.request.make_request('getJournalList', last_log_id=last_log_id, logs_types=logs_types, 18 | order_id=order_id) 19 | 20 | def get_order_extra_fields(self): 21 | """ 22 | The method returns extra fields defined for the orders. 23 | Values of those fields can be set with method set_order_fields. 24 | In order to retrieve values of those fields set parameter include_custom_extra_fields in method get_orders 25 | """ 26 | return self.request.make_request('getOrderExtraFields') 27 | 28 | def get_orders(self, order_id=None, date_confirmed_from=None, date_from=None, id_from=None, 29 | get_unconfirmed_orders=None, status_id=None, filter_email=None): 30 | """ 31 | Method allows you to download orders from a specific date from the BaseLinker order manager. 32 | Keywords: 33 | order_id (int): (optional) Order identifier. Completing this field will download 34 | information about only one specific order. 35 | date_confirmed_from (int): (optional) Date of order confirmation from which orders are to be collected. 36 | Format unix time stamp. 37 | date_from (int): (optional) (optional) The order date from which orders are to be collected. 38 | Format unix time stamp. 39 | id_from (int): (optional) The order ID number from which subsequent orders are to be collected. 40 | get_unconfirmed_orders (bool): (optional, false by default) Download unconfirmed orders as well 41 | (this is e.g. an order from Allegro to which the customer has not yet completed the delivery form). 42 | Default is false. Unconfirmed orders may not be complete yet, the shipping method and price is also unknown. 43 | status_id (int): (optional) The status identifier from which orders are to be collected. 44 | Leave blank to download orders from all statuses. 45 | filter_email varchar(50): (optional) Filtering of order lists by e-mail address 46 | (displays only orders with the given e-mail address). 47 | """ 48 | return self.request.make_request('getOrders', order_id=order_id, date_confirmed_from=date_confirmed_from, 49 | date_from=date_from, id_from=id_from, 50 | get_unconfirmed_orders=get_unconfirmed_orders, 51 | status_id=status_id, filter_email=filter_email) 52 | 53 | def get_order_sources(self): 54 | """ 55 | The method returns types of order sources along with their IDs. Order sources are grouped by their type that 56 | corresponds to a field order_source from the getOrders method. Available source types are "personal", "shop" 57 | or "marketplace_code" e.g. "ebay", "amazon", "ceneo", "emag", "allegro", etc. 58 | """ 59 | return self.request.make_request('getOrderSources') 60 | 61 | def get_order_transaction_details(self, order_id): 62 | """ 63 | The method allows you to retrieve transaction details for a selected order 64 | (it now works only for orders from Amazon) 65 | Keywords: 66 | order_id (int): (optional) Order Identifier from BaseLinker order manager. 67 | """ 68 | return self.request.make_request('getOrderTransactionDetails', order_id=order_id) 69 | 70 | def get_orders_by_email(self, email): 71 | """ 72 | The method allows to search for orders related to the given e-mail address. 73 | Keywords: 74 | email (varchar(50): (required) The e-mail address we search for in orders. 75 | """ 76 | return self.request.make_request('getOrdersByEmail', email=email) 77 | 78 | def get_orders_by_phone(self, phone): 79 | """ 80 | The method allows you to search for orders related to the given phone number. 81 | Keywords: 82 | phone (varchar(50): (required) The phone number we search for in orders. 83 | """ 84 | return self.request.make_request('getOrdersByPhone', phone=phone) 85 | 86 | def add_invoice(self, order_id, series_id): 87 | """ 88 | The method allows to issue an order invoice. 89 | Keywords: 90 | order_id (int): (required) Order Identifier from BaseLinker order manager. 91 | series_id (int): (required) Series numbering identifier 92 | """ 93 | return self.request.make_request('addInvoice', order_id=order_id, series_id=series_id) 94 | 95 | def get_invoices(self, invoice_id=None, order_id=None, date_from=None, 96 | id_from=None, series_id=None, get_external_invoices=None): 97 | """ 98 | The method allows you to download invoices issued from the BaseLinker order manager. 99 | the list of invoices can be limited using filters described in the method parameters. 100 | Maximum 100 invoices are returned at a time. 101 | Keywords: 102 | invoice_id (int): (optional) Invoice identifier. Completing this field will result in downloading 103 | information about only one specific invoice. 104 | order_id (int): (optional) Order identifier. Completing this field will result in downloading information 105 | only about the invoice associated with this order (if the order has an invoice created). 106 | date_from (int): (optional) Date from which invoices are to be collected. Unix time stamp format. 107 | id_from (int): (optional) The invoice ID number from which subsequent invoices are to be retrieved. 108 | series_id (int): (optional) numbering series ID that allows filtering after the invoice numbering series. 109 | get_external_invoices (bool): (optional, true by default) Download external invoices as well. 110 | """ 111 | return self.request.make_request('getInvoices', invoice_id=invoice_id, order_id=order_id, 112 | date_from=date_from, id_from=id_from, series_id=series_id, 113 | get_external_invoices=get_external_invoices) 114 | 115 | def get_series(self): 116 | """ 117 | The method allows to download a series of invoice/receipt numbering. 118 | """ 119 | return self.request.make_request('getSeries') 120 | 121 | def get_order_status_list(self): 122 | """ 123 | The method allows you to download order statuses created by the customer in the BaseLinker order manager. 124 | """ 125 | return self.request.make_request('getOrderStatusList') 126 | 127 | def get_order_payments_history(self, order_id=None, show_full_history=None): 128 | """ 129 | The method allows you to retrieve payment history for a selected order, 130 | including an external payment identifier from the payment gateway. 131 | One order can have multiple payment history entries,caused by surcharges, 132 | order value changes, manual payment editing 133 | Keywords: 134 | order_id (int): (required) Order Identifier from BaseLinker order manager. 135 | show_full_history (bool): (optional, false by default) Download full payment history, 136 | including order value change entries, manual order payment edits. 137 | False by default - only returns entries containing an external payment identifier (most commonly used) 138 | """ 139 | return self.request.make_request('getOrderPaymentsHistory', order_id=order_id, 140 | show_full_history=show_full_history) 141 | 142 | def get_new_receipts(self, series_id=None): 143 | """ 144 | The method allows you to retrieve receipts waiting to be issued. 145 | This method should be used in creating integration with a fiscal printer. 146 | The method can be requested for new receipts every e.g. 10 seconds. 147 | If any receipts appear in response, they should be confirmed by the setOrderReceipt method 148 | after printing to disappear from the waiting list. 149 | Keywords: 150 | series_id (int): (optional) The numbering series ID allows filtering by the receipt numbering series. 151 | Using multiple series numbering for receipts is recommended when the user has multiple fiscal printers. 152 | Each fiscal printer should have a separate series. 153 | """ 154 | return self.request.make_request('getNewReceipts', series_id=series_id) 155 | 156 | def get_receipt(self, receipt_id=None, order_id=None): 157 | """ 158 | The method allows you to retrieve a single receipt from the BaseLinker order manager. 159 | To retrieve a list of new receipts (when integrating a fiscal printer), use the getNewReceipts method. 160 | Keywords: 161 | receipt_id (int): Receipt ID. Not required if order_id is provided. 162 | order_id (int): Order ID. Not required if receipt_id is provided. 163 | """ 164 | return self.request.make_request('getReceipt', receipt_id=receipt_id, order_id=order_id) 165 | 166 | def set_order_fields(self, order_id=None, admin_comments=None, user_comments=None, payment_method=None, 167 | payment_method_cod=None, email=None, phone=None, user_login=None, delivery_method=None, 168 | delivery_price=None, delivery_fullname=None, delivery_company=None, delivery_address=None, 169 | delivery_postcode=None, delivery_city=None, delivery_country_code=None, 170 | delivery_point_id=None, 171 | delivery_point_name=None, delivery_point_address=None, delivery_point_postcode=None, 172 | delivery_point_city=None, invoice_fullname=None, invoice_company=None, invoice_nip=None, 173 | invoice_address=None, invoice_postcode=None, invoice_city=None, invoice_country_code=None, 174 | want_invoice=None, extra_field_1=None, extra_field_2=None, pick_state=None, 175 | pack_state=None): 176 | """ 177 | The method allows you to edit selected fields (e.g. address data, notes, etc.) of a specific order. 178 | Only the fields that you want to edit should be given, other fields can be omitted in the request. 179 | Keywords: 180 | admin_comments varchar(200) Seller comments 181 | user_comments varchar(510) Buyer comments 182 | payment_method varchar(30) Payment method 183 | payment_method_cod (bool) Flag indicating whether the type of payment is COD (cash on delivery) 184 | email varchar(150) Buyer e-mail address 185 | phone varchar(100) Buyer phone number 186 | user_login varchar(30) Buyer login 187 | delivery_method varchar(30) Delivery method name 188 | delivery_price (float) Gross delivery price 189 | delivery_fullname varchar(100) Delivery address - name and surname 190 | delivery_company varchar(100) Delivery address - company 191 | delivery_address varchar(100) Delivery address - street and number 192 | delivery_postcode varchar(100) Delivery address - postcode 193 | delivery_city varchar(100) Delivery address - city 194 | delivery_country_code char(2) Delivery address - country code (two-letter, e.g. EN) 195 | delivery_point_id varchar(40) Pick-up point delivery - pick-up point identifier 196 | delivery_point_name varchar(100) Pick-up point delivery - pick-up point name 197 | delivery_point_address varchar(100) Pick-up point delivery - pick-up point address 198 | delivery_point_postcode varchar(100) Pick-up point delivery - pick-up point postcode 199 | delivery_point_city varchar(100) Pick-up point delivery - pick-up point city 200 | invoice_fullname varchar(100) Billing details - name and surname 201 | invoice_company varchar(100) Billing details - company 202 | invoice_nip varchar(100) Billing details - Vat Reg. no./tax number 203 | invoice_address varchar(100) Billing details - street and house number 204 | invoice_postcode varchar(100) Billing details - postcode 205 | invoice_city varchar(100) Billing details - city 206 | invoice_country_code char(2) Billing details - country code (two-letter, e.g. EN) 207 | want_invoice (bool) Flag indicating whether the customer wants an invoice (1 - yes, 0 - no) 208 | extra_field_1 varchar(50) Value from "extra field 1". - the seller can store any information there 209 | extra_field_2 varchar(50) Value from "extra field 2". - the seller can store any information there 210 | pick_state (int) Flag indicating the status of the order products collection 211 | (1 - all products have been collected, 0 - not all products have been collected) 212 | pack_state (int) Flag indicating the status of the order products packing 213 | (1 - all products have been packed, 0 - not all products have been packed) 214 | """ 215 | return self.request.make_request('setOrderFields', order_id=order_id, admin_comments=admin_comments, 216 | user_comments=user_comments, 217 | payment_method=payment_method, payment_method_cod=payment_method_cod, 218 | email=email, 219 | phone=phone, user_login=user_login, 220 | delivery_method=delivery_method, delivery_price=delivery_price, 221 | delivery_fullname=delivery_fullname, delivery_company=delivery_company, 222 | delivery_address=delivery_address, delivery_postcode=delivery_postcode, 223 | delivery_city=delivery_city, delivery_country_code=delivery_country_code, 224 | delivery_point_id=delivery_point_id, delivery_point_name=delivery_point_name, 225 | delivery_point_address=delivery_point_address, 226 | delivery_point_postcode=delivery_point_postcode, 227 | delivery_point_city=delivery_point_city, 228 | invoice_fullname=invoice_fullname, invoice_company=invoice_company, 229 | invoice_nip=invoice_nip, invoice_address=invoice_address, 230 | invoice_postcode=invoice_postcode, invoice_city=invoice_city, 231 | invoice_country_code=invoice_country_code, 232 | want_invoice=want_invoice, extra_field_1=extra_field_1, 233 | extra_field_2=extra_field_2, 234 | pick_state=pick_state, pack_state=pack_state) 235 | 236 | def add_order_product(self, order_id=None, storage=None, storage_id=None, 237 | product_id=None, variant_id=None, auction_id=None, 238 | name=None, sku=None, ean=None, attributes=None, 239 | price_brutto=None, tax_rate=None, quantity=None, weight=None): 240 | """ 241 | The method allows you to add a new product to your order. 242 | Keywords: 243 | order_id (int) Order Identifier from BaseLinker order manager 244 | storage varchar(9) Type of product source storage (available values: "db" - BaseLinker internal catalog, 245 | "shop" - online shop storage, "warehouse" - the connected wholesaler) 246 | storage_id varchar(50) ID of the product source storage 247 | (one from BaseLinker catalogs or one of the stores connected to the account). 248 | Value "0" when the product comes from BaseLinker internal catalog. 249 | product_id varchar(50) Product identifier in BaseLinker or shop storage. 250 | Blank if the product number is not known 251 | variant_id varchar(30) Product variant ID. Blank if the variant number is unknown 252 | auction_id varchar(20) Listing ID number (if the order comes from ebay/allegro) 253 | name varchar(130) Product name 254 | sku varchar(40) Product SKU number 255 | ean varchar(40) Product EAN number 256 | attributes varchar(150) The detailed product attributes, e.g. "Colour: blue" (Variant name) 257 | price_brutto (float) Single item gross price 258 | tax_rate (float) VAT rate 259 | quantity (int) Number of pieces 260 | weight (float) Single piece weight 261 | """ 262 | return self.request.make_request('addOrderProduct', order_id=order_id, storage=storage, storage_id=storage_id, 263 | product_id=product_id, variant_id=variant_id, auction_id=auction_id, 264 | name=name, sku=sku, ean=ean, attributes=attributes, 265 | price_brutto=price_brutto, tax_rate=tax_rate, quantity=quantity, weight=weight) 266 | 267 | def set_order_product_fields(self, order_id, order_product_id, storage=None, storage_id=None, 268 | product_id=None, variant_id=None, auction_id=None, 269 | name=None, sku=None, ean=None, attributes=None, 270 | price_brutto=None, tax_rate=None, quantity=None, weight=None): 271 | """ 272 | The method allows you to edit the data of selected items (e.g. prices, quantities etc.) of a specific order. 273 | Only the fields that you want to edit should be given, the remaining fields can be omitted in the request. 274 | Keywords: 275 | order_id (int): (required) Order Identifier from BaseLinker order manager 276 | order_product_id (int): (required) Order item ID from BaseLinker order manager. Field required. 277 | storage varchar(9) Type of product source storage 278 | (available values: "db" - BaseLinker internal catalog, "shop" - online shop storage, 279 | "warehouse" - the connected wholesaler) 280 | storage_id varchar(50) ID of the product source storage 281 | (one from BaseLinker catalogs or one of the stores connected to the account). 282 | Value "0" when the product comes from BaseLinker internal catalog. 283 | product_id varchar(50) Product identifier in BaseLinker or shop storage. Blank if the product number is not 284 | known variant_id varchar(30) Product variant ID. Blank if the variant number is unknown 285 | auction_id varchar(20) Listing ID number (if the order comes from ebay/allegro) 286 | name varchar(130) Product name 287 | sku varchar(40) Product SKU number 288 | ean varchar(40) Product EAN number 289 | attributes varchar(150) The detailed product attributes, e.g. "Colour: blue" (Variant name) 290 | price_brutto (float) Single item gross price 291 | tax_rate (float) VAT rate 292 | quantity (int) Number of pieces 293 | weight (float) Single piece weight 294 | """ 295 | return self.request.make_request('setOrderProductFields', order_id=order_id, order_product_id=order_product_id, 296 | storage=storage, storage_id=storage_id, product_id=product_id, 297 | variant_id=variant_id, 298 | auction_id=auction_id, name=name, sku=sku, ean=ean, attributes=attributes, 299 | price_brutto=price_brutto, tax_rate=tax_rate, quantity=quantity, weight=weight) 300 | 301 | def delete_order_product(self, order_id, order_product_id): 302 | """ 303 | The method allows you to remove a specific product from the order. 304 | Keywords: 305 | order_id (int): (required) Order Identifier from BaseLinker order manager. 306 | order_product_id (int): (required) Order item ID from BaseLinker order manager. 307 | """ 308 | return self.request.make_request('deleteOrderProduct', order_id=order_id, order_product_id=order_product_id) 309 | 310 | def set_order_payment(self, order_id, payment_done, payment_date, payment_comment): 311 | """ 312 | The method allows you to add a payment to the order. 313 | Keywords: 314 | order_id (int): (required) Order ID number 315 | payment_done (float): (required) The amount of the payment. The value changes the current payment in the 316 | order (not added to the previous value). If the amount matches the order value, 317 | the order will be marked as paid. 318 | payment_date (int): (required) Payment date unixtime. 319 | payment_comment varchar(30): (required) Payments commentary. 320 | """ 321 | return self.request.make_request('setOrderPayment', order_id=order_id, payment_done=payment_done, 322 | payment_date=payment_date, payment_comment=payment_comment) 323 | 324 | def set_order_status(self, order_id, status_id): 325 | """ 326 | The method allows you to change order status. 327 | Keywords: 328 | order_id (int): (required) Order ID number 329 | status_id (int): (required) Status ID number. The status list can be retrieved using getOrderStatusList. 330 | """ 331 | return self.request.make_request('setOrderStatus', order_id=order_id, status_id=status_id) 332 | 333 | def set_order_receipt(self, receipt_id, receipt_nr, date, printer_error=None): 334 | """ 335 | The method allows you to mark orders with a receipt already issued. 336 | Keywords: 337 | receipt_id (int): (required) Receipt_id number received in the get_new_receipts method 338 | receipt_nr varchar(20): (required) The number of the issued receipt 339 | (may be blank if the printer does not return the number) 340 | date (int): (required) Receipt printing date (unixtime format) 341 | printer_error (bool): Flag indicating whether an error occurred during receipt printing (false by default) 342 | """ 343 | return self.request.make_request('setOrderReceipt', receipt_id=receipt_id, receipt_nr=receipt_nr, 344 | date=date, printer_error=printer_error) 345 | 346 | def add_order_invoice_file(self, invoice_id, file, external_invoice_number): 347 | """ 348 | The method allows you to mark orders with a receipt already issued. 349 | Keywords: 350 | invoice_id (int): (required) BaseLinker invoice identifier 351 | file (text): (required) Invoice PDF file in binary format encoded in base64, 352 | at the very beginning of the invoice string provide a prefix "data:" e.g. "data:4AAQSkSzkJRgABA[...]" 353 | external_invoice_number varchar(30): (required) External system invoice number 354 | (overwrites BaseLinker invoice number) 355 | """ 356 | return self.request.make_request('addOrderInvoiceFile', invoice_id=invoice_id, file=file, 357 | external_invoice_number=external_invoice_number) 358 | -------------------------------------------------------------------------------- /baselinker/product_catalog.py: -------------------------------------------------------------------------------- 1 | from .request import Request 2 | 3 | 4 | class ProductCatalog: 5 | def __init__(self, api_token): 6 | self.api_token = api_token 7 | self.request = Request(self.api_token) 8 | 9 | 10 | def add_inventory_price_group(self, price_group_id, name, description, currency): 11 | """ 12 | The method allows to create a price group in BaseLinker storage. 13 | Providing a price group ID will update the existing price group. 14 | Such price groups may be later assigned in addInventory method. 15 | Keywords: 16 | price_group_id int: (required) Price group identifier 17 | name varchar(100): (required) Name of the price group 18 | description text: (required) Price group description 19 | currency char(3): (required) 3-letter currency symbol e.g. PLN, EUR 20 | """ 21 | return self.request.make_request('addInventoryPriceGroup', price_group_id=price_group_id, name=name, 22 | description=description, currency=currency) 23 | 24 | 25 | def delete_inventory_price_group(self, price_group_id): 26 | """ 27 | The method allows you to remove the price group from BaseLinker storage. 28 | Keywords: 29 | price_group_id int: (required) Price group identifier 30 | """ 31 | return self.request.make_request('deleteInventoryPriceGroup', price_group_id=price_group_id) 32 | 33 | 34 | def get_inventory_price_groups(self): 35 | """ 36 | The method allows to retrieve price groups existing in BaseLinker storage 37 | """ 38 | return self.request.make_request('getInventoryPriceGroups') 39 | 40 | 41 | def add_inventory_warehouse(self, warehouse_id, name, description, stock_edition): 42 | """ 43 | The method allows you to add a new warehouse available in BaseLinker catalogues. 44 | Adding a warehouse with the same identifier again will cause updates of the previously saved warehouse. 45 | The method does not allow editing warehouses created automatically 46 | for the purpose of keeping external stocks of shops, 47 | wholesalers etc. Such warehouse may be later used in addInventory method. 48 | Keywords: 49 | warehouse_id int: (required) ID of the warehouse 50 | name varchar(100): (required) Warehouse name 51 | description text: (required) Warehouse description 52 | stock_edition bool: (required) Is manual editing of stocks permitted. 53 | A false value means that you can only edit your stock through the API. 54 | """ 55 | return self.request.make_request('addInventoryWarehouse', warehouse_id=warehouse_id, name=name, 56 | description=description, stock_edition=stock_edition) 57 | 58 | 59 | def delete_inventory_warehouse(self, warehouse_id): 60 | """ 61 | The method allows you to remove the warehouse available in BaseLinker catalogues. 62 | The method does not allow to remove warehouses created automatically 63 | for the purpose of keeping external stocks of shops, wholesalers etc. 64 | Keywords: 65 | warehouse_id int: (required) ID of the warehouse 66 | """ 67 | return self.request.make_request('deleteInventoryWarehouse', warehouse_id=warehouse_id) 68 | 69 | 70 | def get_inventory_warehouses(self): 71 | """ 72 | The method allows you to retrieve a list of warehouses available in BaseLinker catalogues. 73 | The method also returns information about the warehouses created automatically 74 | for the purpose of keeping external stocks (shops, wholesalers etc.) 75 | """ 76 | return self.request.make_request('getInventoryWarehouses') 77 | 78 | 79 | def add_inventory(self, inventory_id, name, description, languages, default_language, price_groups, 80 | default_price_group, warehouses, default_warehouse, reservations): 81 | """ 82 | The method allows you to add the BaseLinker catalogs. 83 | Adding a catalog with the same identifier again will cause updates of the previously saved catalog. 84 | Keywords: 85 | inventory_id int: (required) Catalog ID. The list of identifiers can be retrieved using the method get_inventories. 86 | name varchar(100): (required) Catalog name 87 | description text: (required) Catalog description 88 | languages array: (required) An array of languages available in the catalogue. 89 | default_language char(2): (required) Default catalogue language. Must be included in the "languages" parameter. 90 | price_groups (array): (required) An array of price group identifiers available in the catalogue. 91 | The list of price group identifiers can be downloaded using the get_inventory_price_groups method 92 | default_price_group int: (required) ID of the price group default for the catalogue. 93 | The identifier must be included in the "price_groups" parameter. 94 | warehouses (array): (required) An array of warehouse identifiers available in the catalogue. 95 | The list of warehouse identifiers can be retrieved using the get_inventory_warehouses API method. 96 | The format of the identifier should be as follows: "[type:bl|shop|warehouse]_[id:int]". (e.g. "shop_2445") 97 | default_warehouse varchar(30): (required) Identifier of the warehouse default for the catalogue. 98 | The identifier must be included in the "warehouses" parameter. 99 | reservations (bool): (required) Does this catalogue support reservations 100 | """ 101 | return self.request.make_request('addInventory', inventory_id=inventory_id, name=name, 102 | description=description, languages=languages, 103 | default_language=default_language, price_groups=price_groups, 104 | default_price_group=default_price_group, warehouses=warehouses, 105 | default_warehouse=default_warehouse, reservations=reservations) 106 | 107 | 108 | def delete_inventory(self, inventory_id): 109 | """ 110 | The method allows you to delete a catalog from BaseLinker storage. 111 | Keywords: 112 | inventory_id int: (required) Catalog ID. The list of identifiers can be retrieved using the method get_inventories. 113 | """ 114 | return self.request.make_request('deleteInventory', inventory_id=inventory_id) 115 | 116 | 117 | def get_inventories(self): 118 | """ 119 | The method allows you to retrieve a list of catalogs available in the BaseLinker storage. 120 | """ 121 | return self.request.make_request('getInventories') 122 | 123 | 124 | def add_inventory_category(self, inventory_id, category_id, name, parent_id): 125 | """ 126 | The method allows you to add a category to the BaseLinker catalog. 127 | Adding a category with the same identifier again, updates the previously saved category 128 | Keywords: 129 | inventory_id int: (required) Catalog ID. The list of identifiers can be retrieved using the method get_inventories. 130 | category_id int: (required) The category identifier to be provided for updates. Should be left blank when creating a new category. 131 | name varchar(200): (required) Category name 132 | parent_id int: (required) The parent category identifier obtained previously at the output of the 133 | add_category method. Categories should be added starting from the hierarchy root so that 134 | the child is always added after the parent (you need to know the parent ID to add the child). 135 | For the top level category, 0 should be given as parent_id. 136 | """ 137 | return self.request.make_request('addInventoryCategory', inventory_id=inventory_id, category_id=category_id, 138 | name=name, parent_id=parent_id) 139 | 140 | 141 | def delete_inventory_category(self, category_id): 142 | """ 143 | The method allows you to remove categories from BaseLinker warehouse. 144 | Along with the category, the products contained therein are removed 145 | (however, this does not apply to products in subcategories). 146 | The subcategories will be changed to the highest level categories. 147 | Keywords: 148 | category_id int: (required) The number of the category to be removed in the BaseLinker storage. 149 | """ 150 | return self.request.make_request('deleteInventoryCategory', category_id=category_id) 151 | 152 | 153 | def get_inventory_categories(self, inventory_id): 154 | """ 155 | The method allows you to retrieve a list of categories for a BaseLinker catalog. 156 | Keywords: 157 | inventory_id int: (required) Catalog ID. The list of identifiers can be retrieved by 158 | the get_inventories method (inventory_id field). 159 | To retrieve categories available for all catalogs created in BaseLinker, this field should be omitted. 160 | """ 161 | return self.request.make_request('getInventoryCategories', inventory_id=inventory_id) 162 | 163 | 164 | def add_inventory_manufacturer(self, manufacturer_id, name): 165 | """ 166 | The method allows you to retrieve a list of categories for a BaseLinker catalog. 167 | Keywords: 168 | manufacturer_id int: (required) Manufacturer ID provided in case of an update. Should be blank when creating a new manufacturer. 169 | name varchar(200): (required) Manufacturer name 170 | """ 171 | return self.request.make_request('addInventoryManufacturer', manufacturer_id=manufacturer_id, name=name) 172 | 173 | 174 | def delete_inventory_manufacturer(self, manufacturer_id): 175 | """ 176 | The method allows you to remove manufacturer from BaseLinker catalog 177 | Keywords: 178 | manufacturer_id int: (required) The ID of the manufacturer removed from BaseLinker warehouse. 179 | """ 180 | return self.request.make_request('deleteInventoryManufacturer', manufacturer_id=manufacturer_id) 181 | 182 | 183 | def get_inventory_manufacturers(self): 184 | """ 185 | The method allows you to retrieve a list of manufacturers for a BaseLinker catalog. 186 | """ 187 | return self.request.make_request('getInventoryManufacturers') 188 | 189 | 190 | def get_inventory_extra_fields(self): 191 | """ 192 | The method allows you to retrieve a list of extra fields for a BaseLinker catalog. 193 | """ 194 | return self.request.make_request('getInventoryExtraFields') 195 | 196 | 197 | def get_inventory_integrations(self, inventory_id): 198 | """ 199 | The method returns a list of integrations where text values in the catalog can be overwritten. 200 | The returned data contains a list of accounts for each integration 201 | and a list of languages supported by the integration 202 | Keywords: 203 | inventory_id int: (required) Catalog ID. 204 | The list of identifiers can be retrieved using the method get_inventories. (inventory_id field). 205 | """ 206 | return self.request.make_request('getInventoryIntegrations', inventory_id=inventory_id) 207 | 208 | 209 | def get_inventory_available_text_field_keys(self, inventory_id): 210 | """ 211 | The method returns a list of product text fields that can be overwritten for specific integration. 212 | Keywords: 213 | inventory_id int: (required) Catalog ID. The list of identifiers can be retrieved 214 | by the get_inventories method (inventory_id field). 215 | """ 216 | return self.request.make_request('getInventoryAvailableTextFieldKeys', inventory_id=inventory_id) 217 | 218 | 219 | def add_inventory_product(self, inventory_id, product_id, parent_id, is_bundle, ean, 220 | sku, tax_rate, weight, height, width, length, star, 221 | manufacturer_id, category_id, prices, stock, locations, 222 | text_fields, images, links, bundle_products): 223 | """ 224 | The method allows you to add a new product to BaseLinker catalog. Entering the product with the ID updates previously saved product. 225 | Keywords: 226 | inventory_id varchar(30) Catalog ID. The list of identifiers can be retrieved using the method getInventories. (inventory_id field). 227 | product_id varchar(30) Main product identifier, given only during the update. Should be left blank when creating a new product. The new product identifier is returned as a response to this method. 228 | parent_id varchar(30) Product parent ID. To be provided only if the added/edited product is a variant of another product. 229 | is_bundle bool Is the given product a part of a bundle 230 | ean varchar(32) Product EAN number. 231 | sku varchar(50) Product SKU number. 232 | tax_rate float VAT tax rate (e.g. "20") 233 | weight decimal(10,2) Weight in kilograms. 234 | height decimal(10,2) Product height 235 | width decimal(10,2) Product width 236 | length decimal(10,2) Product length 237 | star int Product star type. It takes from 0 to 5 values. 0 means no starring. 238 | manufacturer_id int Product manufacturer ID. IDs can be retrieved with getInventoryManufacturers method. 239 | category_id int Product category ID (category must be previously created with addInventoryCategories) method. 240 | prices array A list containing product prices, where the key is the price group ID 241 | and value is a product gross price for a given price group. 242 | The list of price groups can be retrieved with get_inventory_price_groups method. 243 | locations array A list containing product locations where the key is the warehouse ID and value is 244 | a product location for a given warehouse, eg. "A-5-2". 245 | Warehouse ID should have this format: "[type:bl|shop|warehouse]_[id:int]" (eg. "bl_123"). 246 | The list of warehouse IDs can be retrieved with get_inventory_warehouses method. 247 | text_fields array A list containing field text values (names, descriptions, etc.) of a product, 248 | where the key is the field text ID and value is the field value. 249 | The field text ID consists of the following components separated with the "|" character: 250 | [field] - Field name. Accepted field names: "name", "description", "features", "description_extra1", "description_extra2", "description_extra3", "description_extra4", "extra_field_[extra-field-ID]" e.g. "extra_field_75". The list of extra fields IDs can be retrieved with getInventoryExtraFields method. 251 | [lang] - A two-letter code of language, which gets assigned given value e.g. "en". If this value is not specified, the default catalog language is assigned. The list of languages available for each integration can be retrieved with getInventoryIntegrations method. 252 | [source_id] - Integration ID provided when the given text field value is to be overwritten only for a specific integration. ID should have a following format: "[type:varchar]_[id:int]", where the type means a kind of integration (e.g. "ebay", "amazon", "google"), and ID is an account identifier for given integration (eg. "ebay_2445"). 253 | If a value is to be overwritten throughout the integration (e.g. for all Amazon accounts), the value "0" should be used as the identifier. (e.g. "amazon_0"). 254 | Examples of text field identifiers: 255 | "name" - Default name assigned to the default language. 256 | "name|de" - Name assigned to a particular language. 257 | "name|de|amazon_0" - Name assigned to a specific language for all Amazon accounts. 258 | "name|de|amazon_123" - Name assigned to a specific language for an Amazon account with ID 123. 259 | The list of all text field identifiers can be retrieved with the getInventoryAvailableTextFieldKeys method. 260 | In the case of the name and short additional fields, the character limit for the field value is 200. 261 | When specifying the value of a product feature (field "features"), 262 | provide a list where the key is the name of the parameter (e.g. "Colour") and the value is the value of that parameter (e.g. "White"). 263 | images array An array of product images (maximum 16). Each element of the array is a separate photo. 264 | You can submit a photo in binary format, or a link to the photo. In case of binary format, 265 | the photo should be coded in base64 and at the very beginning of the photo string the prefix "data:" 266 | should be provided. In case of link to the photo, the prefix "url:" must be given before the link. 267 | Example: 268 | images[0] = "url:http://adres.pl/zdjecie.jpg"; 269 | images[1] = "data:4AAQSkZJRgABA[...]"; 270 | links array An array containing product links to external warehouses (e.g. shops, wholesalers). 271 | Each element of the array is a list in which the key is the identifier of the external warehouse in the format: 272 | "[type:shop|warehouse]_[id:int]". (e.g. "shop_2445"). 273 | The warehouse identifiers can be retrieved with the getStoragesList method. The value is an array containing the fields listed below. 274 | | - product_id varchar Product identifier in external warehouse. 275 | | - variant_id (optional) varchar Product variant identifier in the external warehouse. 276 | When assigning a link to a main product, this parameter shall be omitted or a value of 0 provided. 277 | bundle_products array A list containing information about the products included in the bundle, 278 | where the key is the identifier of the product included in the bundle, 279 | and the value is the number of pieces of this product in the bundle. 280 | Subproducts can only be defined if the added/edited product is a bundle (is_bundle = true). 281 | """ 282 | return self.request.make_request('addInventoryProduct', inventory_id=inventory_id, product_id=product_id, 283 | parent_id=parent_id, is_bundle=is_bundle, ean=ean, 284 | sku=sku, tax_rate=tax_rate, weight=weight, height=height, 285 | width=width, length=length, star=star, manufacturer_id=manufacturer_id 286 | , category_id=category_id, prices=prices, stock=stock, locations=locations, 287 | text_fields=text_fields, images=images, links=links, 288 | bundle_products=bundle_products) 289 | 290 | 291 | def delete_inventory_product(self, product_id): 292 | """ 293 | The method allows you to remove the product from BaseLinker catalog. 294 | Keywords: 295 | product_id int: (required) BaseLinker catalogue product identifier 296 | """ 297 | return self.request.make_request('deleteInventoryProduct', product_id=product_id) 298 | 299 | 300 | def get_inventory_products_data(self, inventory_id, products): 301 | """ 302 | The method allows you to retrieve detailed data for selected products from the BaseLinker catalogue. 303 | Keywords: 304 | inventory_id int: (required) Catalog ID. The list of identifiers can be retrieved 305 | by the get_inventories method (inventory_id field). 306 | products array (required) An array of product ID numbers to download 307 | """ 308 | return self.request.make_request('getInventoryProductsData', inventory_id=inventory_id, products=products) 309 | 310 | 311 | def get_inventory_products_list(self, inventory_id, filter_id=None, filter_category_id=None, 312 | filter_ean=None, filter_sku=None, filter_name=None, filter_price_from=None, 313 | filter_stock_from=None, filter_price_to=None, page=None, filter_sort=None): 314 | """ 315 | The method allows to retrieve a basic data of chosen products from BaseLinker catalogs. 316 | 317 | Keywords: 318 | inventory_id int: (required) Catalog ID. The list of identifiers can be retrieved by the get_inventories method 319 | filter_id int (optional) limiting results to a specific product id 320 | filter_category_id int (optional) Retrieving products from a specific category 321 | filter_ean varchar(32) (optional) limiting results to a specific ean 322 | filter_sku varchar(50) (optional) limiting the results to a specific SKU (stock keeping number) 323 | filter_name varchar(200) (optional) item name filter (part of the searched name or an empty field) 324 | filter_price_from float (optional) minimum price limit (not displaying products with lower price) 325 | filter_price_to float (optional) maximum price limit 326 | filter_stock_from int (optional) minimum quantity limit 327 | page int (optional) Results paging (1000 products per page for BaseLinker warehouse) 328 | filter_sort varchar(30) (optional) the value for sorting the product list. Possible values: "id [ASC|DESC]" 329 | """ 330 | return self.request.make_request('getInventoryProductsList', inventory_id=inventory_id, filter_id=filter_id, 331 | filter_category_id=filter_category_id, filter_ean=filter_ean, 332 | filter_sku=filter_sku, filter_name=filter_name, 333 | filter_price_from=filter_price_from, 334 | filter_price_to=filter_price_to, filter_stock_from=filter_stock_from, 335 | page=page, filter_sort=filter_sort) 336 | 337 | 338 | def get_inventory_products_stock(self, inventory_id, page=None): 339 | """ 340 | The method allows you to retrieve detailed data for selected products from the BaseLinker catalogue. 341 | Keywords: 342 | inventory_id int: (required) Catalog ID. The list of identifiers can be retrieved 343 | by the get_inventories method 344 | page int (optional) Results paging (1000 products per page for BaseLinker warehouse) 345 | """ 346 | return self.request.make_request('getInventoryProductsStock', inventory_id=inventory_id, page=page) 347 | 348 | 349 | def update_inventory_products_stock(self, inventory_id, products): 350 | """ 351 | The method allows to update stocks of products (and/or their variants) in BaseLinker catalog. Maximum 1000 products at a time. 352 | Keywords: 353 | inventory_id int: (required) Catalog ID. The list of identifiers can be retrieved 354 | by the get_inventories method 355 | products array (required) An array of products, where the key is a product ID and the value is a list of stocks. 356 | When determining the variant stock, provide variant ID as a product ID. 357 | In the stocks array the key should be the warehouse ID and the value - stock for that warehouse. 358 | The format of the warehouse identifier should be as follows: 359 | "[type:bl|shop|warehouse]_[id:int]". (e.g. "bl_123"). 360 | The list of warehouse identifiers can be retrieved using the get_inventory_warehouses method. 361 | Stocks cannot be assigned to the warehouses created automatically for 362 | purposes of keeping external stocks (shops, wholesalers, etc.). 363 | """ 364 | return self.request.make_request('updateInventoryProductsStock', inventory_id=inventory_id, products=products) 365 | 366 | 367 | def get_inventory_products_prices(self, inventory_id, page=None): 368 | """ 369 | The method allows to retrieve the gross prices of products from BaseLinker catalogues. 370 | Keywords: 371 | inventory_id int: (required) Catalog ID. The list of identifiers can be retrieved 372 | by the get_inventories method 373 | page int (optional) Results paging (1000 products per page for BaseLinker warehouse) 374 | """ 375 | return self.request.make_request('getInventoryProductsPrices', inventory_id=inventory_id, page=page) 376 | 377 | 378 | def update_inventory_products_prices(self, inventory_id, products): 379 | """ 380 | The method allows to retrieve the gross prices of products from BaseLinker catalogues. 381 | Keywords: 382 | inventory_id int: (required) Catalog ID. The list of identifiers can be retrieved 383 | by the get_inventories method 384 | products array (required) Lista z produktami, w której kluczem jest identyfikator produktu, a wartością lista cen. 385 | W przypadku określania cen dla wariantu należy podać identyfikator wariantu jako identyfikator produktu. 386 | W Liście cen kluczem powinien być identyfikator grupy cenowej, a wartością stan magazynowy dla tego magazynu. 387 | Listę grup cenowych mozna pobrać za pomocą metody get_inventory_price_groups. 388 | """ 389 | return self.request.make_request('updateInventoryProductsPrices', inventory_id=inventory_id, products=products) 390 | 391 | 392 | def get_inventory_product_logs(self, product_id, date_from=None, date_to=None, log_type=None, sort=None, page=None): 393 | """ 394 | The method allows to retrieve a list of events related to product change (or their variants) in the BaseLinker catalog. 395 | Keywords: 396 | product_id int: (required) Product identifier. In case of retrieving logs for a variant, 397 | the variant identifier must be provided as the product identifier. 398 | date_from int (optional) Date from which logs are to be retrieved. Unix time stamp format. 399 | date_to int (optional) Date up to which logs are to be retrieved. Unix time stamp format. 400 | log_type int (optional) List of event types you want to retrieve. Available values: 401 | 1 - Change in stock 402 | 2 - Price change 403 | 3 - Product creation 404 | 4 - Product deletion 405 | 5 - Text fields modifications 406 | 6 - Locations modifications 407 | 7 - Modifications of links 408 | 8 - Gallery modifications 409 | 9 - Variant modifications 410 | 10 - Modifications of bundle products 411 | sort int(optional) Type of log sorting. Possible "ASC" values ( ascending from date), "DESC" (descending after the date). 412 | By default the sorting is done in ascending order. 413 | page int (optional) Results paging (100 product editions per page). 414 | """ 415 | return self.request.make_request('getInventoryProductLogs', product_id=product_id, date_from=date_from, 416 | date_to=date_to, log_type=log_type, sort=sort, page=page) -------------------------------------------------------------------------------- /baselinker/request.py: -------------------------------------------------------------------------------- 1 | import json 2 | import requests 3 | 4 | 5 | class Request: 6 | 7 | def __init__(self, api_token): 8 | self.api_url = 'https://api.baselinker.com/connector.php' 9 | if not api_token: 10 | raise ValueError('api_key must be set! Obtain key from: https://panel.baselinker.com/other_api_token.php') 11 | self.api_token = api_token 12 | 13 | 14 | def __get_request_data(self, method_name, parameters=None): 15 | """ 16 | Method that creates body for request 17 | Keywords: 18 | method_name (str): (required) Name of the method to invoke. 19 | parameters (dict): (optional) Dictionary of parameters specified by user. 20 | Returns: 21 | request_data(json): Data used inside body of request returned as a json string. 22 | """ 23 | request_data = {'method': method_name} 24 | if parameters: 25 | request_data['parameters'] = json.dumps(parameters) 26 | return request_data 27 | 28 | def __get_request_headers(self): 29 | """ 30 | Method that creates request header 31 | Returns: 32 | headers(dict): Header needed for request containing api token. 33 | """ 34 | headers = {'X-BLToken': self.api_token} 35 | return headers 36 | 37 | def make_request(self, method_name, **kwargs): 38 | """ 39 | Method that sends request to api endpoint. 40 | Keywords: 41 | method_name (str): (required) Name of the method to invoke. 42 | (**kwargs): (required) Parameters specified by user. 43 | Returns: 44 | content(json): Method returns content of the response formatted as json string. 45 | """ 46 | requests_data = self.__get_request_data(method_name, kwargs) 47 | headers = self.__get_request_headers() 48 | try: 49 | with requests.Session() as s: 50 | response = s.post(self.api_url, data=requests_data, headers=headers) 51 | content = json.loads(response.content.decode("utf-8")) 52 | return content 53 | 54 | except requests.RequestException as e: 55 | print(e) 56 | raise e 57 | -------------------------------------------------------------------------------- /main.py: -------------------------------------------------------------------------------- 1 | from baselinker import Baselinker 2 | 3 | API_TOKEN = '1011-10210-OYC7U14WRGM51M3WD5OKJ9TZPSO60MXQ09MJL92BGZ71Z0ME9DERL4INTVCTT3DS' 4 | 5 | 6 | def run(): 7 | # Create a baselinker client instance 8 | baselinker = Baselinker(API_TOKEN) 9 | # Prints 100 orders from baselinker 10 | print(baselinker.orders.get_orders()) 11 | 12 | 13 | if __name__ == '__main__': 14 | run() 15 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | 2 | [project] 3 | name = "baselinker" 4 | dependencies = [ 5 | "certifi==2021.10.8", 6 | "charset-normalizer==2.0.12", 7 | "idna==3.3", 8 | "requests==2.27.1", 9 | "urllib3==1.26.9", 10 | ] 11 | dynamic = ["version"] -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | certifi==2021.10.8 2 | charset-normalizer==2.0.12 3 | idna==3.3 4 | requests==2.27.1 5 | urllib3==1.26.9 6 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michalkulisiewicz/python-baselinker/a1c23579908293726041489eda387a538fe614f5/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_baselinker.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from unittest.mock import MagicMock 3 | from baselinker import Baselinker 4 | 5 | 6 | class TestBaselinker(unittest.TestCase): 7 | def setUp(self): 8 | self.request_mock = MagicMock() 9 | self.orders_mock = MagicMock() 10 | self.external_storages_mock = MagicMock() 11 | self.product_catalog_mock = MagicMock() 12 | 13 | self.baselinker = Baselinker(api_token='my_token') 14 | self.baselinker.request = self.request_mock 15 | self.baselinker.orders = self.orders_mock 16 | self.baselinker.external_storages = self.external_storages_mock 17 | self.baselinker.product_catalog = self.product_catalog_mock 18 | 19 | def test_baselinker_initialization(self): 20 | self.assertEqual(self.baselinker.api_token, 'my_token') 21 | self.assertIsInstance(self.baselinker.request, MagicMock) 22 | self.assertIsInstance(self.baselinker.orders, MagicMock) 23 | self.assertIsInstance(self.baselinker.external_storages, MagicMock) 24 | self.assertIsInstance(self.baselinker.product_catalog, MagicMock) 25 | -------------------------------------------------------------------------------- /tests/test_external_storages.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from unittest.mock import MagicMock 3 | 4 | from baselinker.external_storages import ExternalStorages 5 | 6 | 7 | class TestExternalStorages(unittest.TestCase): 8 | 9 | def setUp(self): 10 | self.mock_request = MagicMock() 11 | self.external_storages = ExternalStorages(api_token="my_token") 12 | self.external_storages.request = self.mock_request 13 | 14 | def test_get_external_storages_list(self): 15 | expected_params = {} 16 | self.mock_request.make_request.return_value = {'success': True, 'data': {'external_storages': []}} 17 | result = self.external_storages.get_external_storages_list() 18 | self.mock_request.make_request.assert_called_with('getExternalStoragesList', **expected_params) 19 | self.assertTrue(result['success']) 20 | 21 | def test_get_external_storage_categories(self): 22 | expected_params = {'storage_id': 'shop_123'} 23 | self.mock_request.make_request.return_value = {'success': True, 'data': {'categories': []}} 24 | result = self.external_storages.get_external_storage_categories(storage_id='shop_123') 25 | self.mock_request.make_request.assert_called_with('getExternalStorageCategories', **expected_params) 26 | self.assertTrue(result['success']) 27 | 28 | def test_get_external_storage_products_data(self): 29 | expected_params = {'storage_id': 'shop_123', 'products': [1, 2, 3]} 30 | self.mock_request.make_request.return_value = {'success': True, 'data': {'products_data': []}} 31 | result = self.external_storages.get_external_storage_products_data(storage_id='shop_123', products=[1, 2, 3]) 32 | self.mock_request.make_request.assert_called_with('getExternalStorageProductsData', **expected_params) 33 | self.assertTrue(result['success']) 34 | 35 | def test_get_external_storage_products_list(self): 36 | expected_params = { 37 | 'storage_id': 'shop_123', 38 | 'filter_category_id': 'category_1', 39 | 'filter_sort': None, 40 | 'filter_id': None, 41 | 'filter_ean': None, 42 | 'filter_sku': None, 43 | 'filter_name': None, 44 | 'filter_price_from': None, 45 | 'filter_price_to': None, 46 | 'filter_quantity_from': None, 47 | 'filter_quantity_to': None, 48 | 'filter_available': None, 49 | 'page': 1 50 | } 51 | self.mock_request.make_request.return_value = {'success': True, 'data': {'products_list': []}} 52 | result = self.external_storages.get_external_storage_products_list( 53 | storage_id='shop_123', filter_category_id='category_1', page=1 54 | ) 55 | self.mock_request.make_request.assert_called_with('getExternalStorageProductsList', **expected_params) 56 | self.assertTrue(result['success']) 57 | 58 | def test_get_external_storage_products_quantity(self): 59 | expected_params = {'storage_id': 'shop_123', 'page': 1} 60 | self.mock_request.make_request.return_value = {'success': True, 'data': {'products_quantity': []}} 61 | result = self.external_storages.get_external_storage_products_quantity(storage_id='shop_123', page=1) 62 | self.mock_request.make_request.assert_called_with('getExternalStorageProductsQuantity', **expected_params) 63 | self.assertTrue(result['success']) 64 | 65 | def test_get_external_storage_products_prices(self): 66 | expected_params = {'storage_id': 'shop_123', 'page': 1} 67 | self.mock_request.make_request.return_value = {'success': True, 'data': {'products_prices': []}} 68 | result = self.external_storages.get_external_storage_products_prices(storage_id='shop_123', page=1) 69 | self.mock_request.make_request.assert_called_with('getExternalStorageProductsPrices', **expected_params) 70 | self.assertTrue(result['success']) 71 | 72 | def test_update_external_storage_products_quantity(self): 73 | expected_params = {'storage_id': 'shop_123', 'products': [[1, 0, 10], [2, 0, 20]]} 74 | self.mock_request.make_request.return_value = {'success': True, 'data': {'update_result': []}} 75 | result = self.external_storages.update_external_storage_products_quantity( 76 | storage_id='shop_123', products=[[1, 0, 10], [2, 0, 20]] 77 | ) 78 | self.mock_request.make_request.assert_called_with('updateExternalStorageProductsQuantity', **expected_params) 79 | self.assertTrue(result['success']) 80 | -------------------------------------------------------------------------------- /tests/test_orders.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from unittest.mock import MagicMock 3 | from baselinker.orders import Orders 4 | 5 | 6 | class TestOrders(unittest.TestCase): 7 | def setUp(self): 8 | self.api_token = "my_token" 9 | self.orders = Orders(self.api_token) 10 | self.orders.request.make_request = MagicMock(return_value={"success": True}) 11 | 12 | def test_get_journal_list(self): 13 | result = self.orders.get_journal_list(last_log_id=1, logs_types=["type1", "type2"], order_id=123) 14 | self.orders.request.make_request.assert_called_with('getJournalList', last_log_id=1, 15 | logs_types=["type1", "type2"], order_id=123) 16 | self.assertTrue(result["success"]) 17 | 18 | def test_get_order_extra_fields(self): 19 | result = self.orders.get_order_extra_fields() 20 | self.orders.request.make_request.assert_called_with('getOrderExtraFields') 21 | self.assertTrue(result["success"]) 22 | 23 | def test_get_orders(self): 24 | result = self.orders.get_orders(order_id=123, date_confirmed_from=123456789, date_from=987654321, id_from=1, 25 | get_unconfirmed_orders=True, status_id=2, filter_email="test@example.com") 26 | self.orders.request.make_request.assert_called_with('getOrders', order_id=123, date_confirmed_from=123456789, 27 | date_from=987654321, id_from=1, get_unconfirmed_orders=True, 28 | status_id=2, filter_email="test@example.com") 29 | self.assertTrue(result["success"]) 30 | 31 | def test_get_order_sources(self): 32 | result = self.orders.get_order_sources() 33 | self.orders.request.make_request.assert_called_with('getOrderSources') 34 | self.assertTrue(result["success"]) 35 | 36 | def test_get_order_transaction_details(self): 37 | result = self.orders.get_order_transaction_details(order_id=123) 38 | self.orders.request.make_request.assert_called_with('getOrderTransactionDetails', order_id=123) 39 | self.assertTrue(result["success"]) 40 | 41 | def test_get_orders_by_email(self): 42 | result = self.orders.get_orders_by_email(email="test@example.com") 43 | self.orders.request.make_request.assert_called_with('getOrdersByEmail', email="test@example.com") 44 | self.assertTrue(result["success"]) 45 | 46 | def test_get_orders_by_phone(self): 47 | result = self.orders.get_orders_by_phone(phone="123456789") 48 | self.orders.request.make_request.assert_called_with('getOrdersByPhone', phone="123456789") 49 | self.assertTrue(result["success"]) 50 | 51 | def test_add_invoice(self): 52 | result = self.orders.add_invoice(order_id=123, series_id=1) 53 | self.orders.request.make_request.assert_called_with('addInvoice', order_id=123, series_id=1) 54 | self.assertTrue(result["success"]) 55 | 56 | def test_get_invoices(self): 57 | result = self.orders.get_invoices(invoice_id=1, order_id=123, date_from=987654321, id_from=1, series_id=2, 58 | get_external_invoices=True) 59 | self.orders.request.make_request.assert_called_with('getInvoices', invoice_id=1, order_id=123, 60 | date_from=987654321, 61 | id_from=1, series_id=2, get_external_invoices=True) 62 | self.assertTrue(result["success"]) 63 | 64 | def test_get_series(self): 65 | result = self.orders.get_series() 66 | self.orders.request.make_request.assert_called_with('getSeries') 67 | self.assertTrue(result["success"]) 68 | 69 | def test_get_order_status_list(self): 70 | result = self.orders.get_order_status_list() 71 | self.orders.request.make_request.assert_called_with('getOrderStatusList') 72 | self.assertTrue(result["success"]) 73 | 74 | def test_get_order_payments_history(self): 75 | result = self.orders.get_order_payments_history(order_id=123, show_full_history=True) 76 | self.orders.request.make_request.assert_called_with('getOrderPaymentsHistory', order_id=123, 77 | show_full_history=True) 78 | self.assertTrue(result["success"]) 79 | 80 | def test_get_new_receipts(self): 81 | result = self.orders.get_new_receipts(series_id=1) 82 | self.orders.request.make_request.assert_called_with('getNewReceipts', series_id=1) 83 | self.assertTrue(result["success"]) 84 | 85 | def test_get_receipt(self): 86 | result = self.orders.get_receipt(receipt_id=1, order_id=None) 87 | self.orders.request.make_request.assert_called_with('getReceipt', receipt_id=1, order_id=None) 88 | self.assertTrue(result["success"]) 89 | 90 | def test_set_order_fields(self): 91 | result = self.orders.set_order_fields(order_id=123, admin_comments="Admin comments", 92 | user_comments="User comments", 93 | payment_method="Credit Card", payment_method_cod=None, email=None, 94 | phone=None, 95 | user_login=None, delivery_method=None, delivery_price=None, 96 | delivery_fullname=None, delivery_company=None, delivery_address=None, 97 | delivery_postcode=None, delivery_city=None, delivery_country_code=None, 98 | delivery_point_id=None, delivery_point_name=None, 99 | delivery_point_address=None, 100 | delivery_point_postcode=None, delivery_point_city=None, 101 | invoice_fullname=None, 102 | invoice_company=None, invoice_nip=None, invoice_address=None, 103 | invoice_postcode=None, 104 | invoice_city=None, invoice_country_code=None, want_invoice=None, 105 | extra_field_1=None, extra_field_2=None, pick_state=None, pack_state=None) 106 | self.orders.request.make_request.assert_called_with('setOrderFields', order_id=123, 107 | admin_comments="Admin comments", 108 | user_comments="User comments", 109 | payment_method="Credit Card", payment_method_cod=None, 110 | email=None, phone=None, 111 | user_login=None, delivery_method=None, delivery_price=None, 112 | delivery_fullname=None, delivery_company=None, 113 | delivery_address=None, 114 | delivery_postcode=None, delivery_city=None, 115 | delivery_country_code=None, 116 | delivery_point_id=None, delivery_point_name=None, 117 | delivery_point_address=None, 118 | delivery_point_postcode=None, delivery_point_city=None, 119 | invoice_fullname=None, 120 | invoice_company=None, invoice_nip=None, 121 | invoice_address=None, invoice_postcode=None, 122 | invoice_city=None, invoice_country_code=None, 123 | want_invoice=None, 124 | extra_field_1=None, extra_field_2=None, pick_state=None, 125 | pack_state=None) 126 | self.assertTrue(result["success"]) 127 | 128 | def test_add_order_product(self): 129 | result = self.orders.add_order_product(order_id=123, storage="shop", storage_id="store123", product_id="ABC123", 130 | name="Product A", price_brutto=10.99, quantity=2) 131 | self.orders.request.make_request.assert_called_with('addOrderProduct', order_id=123, storage="shop", 132 | storage_id="store123", 133 | product_id="ABC123", variant_id=None, auction_id=None, 134 | name="Product A", sku=None, 135 | ean=None, attributes=None, price_brutto=10.99, 136 | tax_rate=None, quantity=2, weight=None) 137 | self.assertTrue(result["success"]) 138 | 139 | def test_set_order_product_fields(self): 140 | result = self.orders.set_order_product_fields(order_id=123, order_product_id=456, name='Updated Product A', 141 | price_brutto=12.99) 142 | self.orders.request.make_request.assert_called_with('setOrderProductFields', order_id=123, order_product_id=456, 143 | storage=None, storage_id=None, product_id=None, 144 | variant_id=None, auction_id=None, 145 | name='Updated Product A', sku=None, ean=None, 146 | attributes=None, price_brutto=12.99, 147 | tax_rate=None, quantity=None, weight=None) 148 | self.assertTrue(result["success"]) 149 | 150 | def test_delete_order_product(self): 151 | result = self.orders.delete_order_product(order_id=123, order_product_id=456) 152 | self.orders.request.make_request.assert_called_with('deleteOrderProduct', order_id=123, order_product_id=456) 153 | self.assertTrue(result["success"]) 154 | 155 | def test_set_order_payment(self): 156 | result = self.orders.set_order_payment(order_id=123, payment_done=20.0, payment_date=987654321, 157 | payment_comment="Payment received") 158 | self.orders.request.make_request.assert_called_with('setOrderPayment', order_id=123, payment_done=20.0, 159 | payment_date=987654321, payment_comment="Payment received") 160 | self.assertTrue(result["success"]) 161 | 162 | def test_set_order_status(self): 163 | result = self.orders.set_order_status(order_id=123, status_id=2) 164 | self.orders.request.make_request.assert_called_with('setOrderStatus', order_id=123, status_id=2) 165 | self.assertTrue(result["success"]) 166 | 167 | def test_set_order_receipt(self): 168 | result = self.orders.set_order_receipt(receipt_id=1, receipt_nr="R123", date=987654321, printer_error=False) 169 | self.orders.request.make_request.assert_called_with('setOrderReceipt', receipt_id=1, receipt_nr="R123", 170 | date=987654321, printer_error=False) 171 | self.assertTrue(result["success"]) 172 | 173 | def test_add_order_invoice_file(self): 174 | result = self.orders.add_order_invoice_file(invoice_id=1, file="base64_encoded_file", 175 | external_invoice_number="INV001") 176 | self.orders.request.make_request.assert_called_with('addOrderInvoiceFile', invoice_id=1, 177 | file="base64_encoded_file", 178 | external_invoice_number="INV001") 179 | self.assertTrue(result["success"]) 180 | -------------------------------------------------------------------------------- /tests/test_product_catalog.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | from unittest.mock import MagicMock 3 | from baselinker.product_catalog import ProductCatalog 4 | 5 | 6 | class TestProductCatalog(unittest.TestCase): 7 | 8 | def setUp(self): 9 | self.mock_request = MagicMock() 10 | self.product_catalog = ProductCatalog(api_token="my_token") 11 | self.product_catalog.request = self.mock_request 12 | 13 | def test_add_inventory_price_group(self): 14 | expected_params = {'price_group_id': 1, 'name': 'Test Group', 'description': 'Test Description', 15 | 'currency': 'USD'} 16 | self.mock_request.make_request.return_value = {'success': True, 'data': {}} 17 | result = self.product_catalog.add_inventory_price_group(price_group_id=1, name='Test Group', 18 | description='Test Description', currency='USD') 19 | self.mock_request.make_request.assert_called_with('addInventoryPriceGroup', **expected_params) 20 | self.assertTrue(result['success']) 21 | 22 | def test_delete_inventory_price_group(self): 23 | expected_params = {'price_group_id': 1} 24 | self.mock_request.make_request.return_value = {'success': True, 'data': {}} 25 | result = self.product_catalog.delete_inventory_price_group(price_group_id=1) 26 | self.mock_request.make_request.assert_called_with('deleteInventoryPriceGroup', **expected_params) 27 | self.assertTrue(result['success']) 28 | 29 | def test_get_inventory_price_groups(self): 30 | self.mock_request.make_request.return_value = {'success': True, 'data': {'price_groups': []}} 31 | result = self.product_catalog.get_inventory_price_groups() 32 | self.mock_request.make_request.assert_called_with('getInventoryPriceGroups') 33 | self.assertTrue(result['success']) 34 | 35 | def test_add_inventory_warehouse(self): 36 | expected_params = {'warehouse_id': 1, 'name': 'Test Warehouse', 'description': 'Test Description', 37 | 'stock_edition': True} 38 | self.mock_request.make_request.return_value = {'success': True, 'data': {}} 39 | result = self.product_catalog.add_inventory_warehouse(warehouse_id=1, name='Test Warehouse', 40 | description='Test Description', stock_edition=True) 41 | self.mock_request.make_request.assert_called_with('addInventoryWarehouse', **expected_params) 42 | self.assertTrue(result['success']) 43 | 44 | def test_delete_inventory_warehouse(self): 45 | expected_params = {'warehouse_id': 1} 46 | self.mock_request.make_request.return_value = {'success': True, 'data': {}} 47 | result = self.product_catalog.delete_inventory_warehouse(warehouse_id=1) 48 | self.mock_request.make_request.assert_called_with('deleteInventoryWarehouse', **expected_params) 49 | self.assertTrue(result['success']) 50 | 51 | def test_get_inventory_warehouses(self): 52 | self.mock_request.make_request.return_value = {'success': True, 'data': {'warehouses': []}} 53 | result = self.product_catalog.get_inventory_warehouses() 54 | self.mock_request.make_request.assert_called_with('getInventoryWarehouses') 55 | self.assertTrue(result['success']) 56 | 57 | def test_add_inventory(self): 58 | inventory_id = 1 59 | name = "Test Catalog" 60 | description = "Test Catalog Description" 61 | languages = ["en", "pl"] 62 | default_language = "en" 63 | price_groups = [1, 2] 64 | default_price_group = 1 65 | warehouses = ["shop_1", "warehouse_2"] 66 | default_warehouse = "shop_1" 67 | reservations = True 68 | 69 | self.product_catalog.add_inventory( 70 | inventory_id=inventory_id, 71 | name=name, 72 | description=description, 73 | languages=languages, 74 | default_language=default_language, 75 | price_groups=price_groups, 76 | default_price_group=default_price_group, 77 | warehouses=warehouses, 78 | default_warehouse=default_warehouse, 79 | reservations=reservations 80 | ) 81 | 82 | self.mock_request.make_request.assert_called_once_with( 83 | 'addInventory', 84 | inventory_id=inventory_id, 85 | name=name, 86 | description=description, 87 | languages=languages, 88 | default_language=default_language, 89 | price_groups=price_groups, 90 | default_price_group=default_price_group, 91 | warehouses=warehouses, 92 | default_warehouse=default_warehouse, 93 | reservations=reservations 94 | ) 95 | 96 | def test_delete_inventory(self): 97 | inventory_id = 1 98 | 99 | self.product_catalog.delete_inventory(inventory_id=inventory_id) 100 | 101 | self.mock_request.make_request.assert_called_once_with( 102 | 'deleteInventory', 103 | inventory_id=inventory_id 104 | ) 105 | 106 | def test_get_inventories(self): 107 | self.product_catalog.get_inventories() 108 | 109 | self.mock_request.make_request.assert_called_once_with('getInventories') 110 | 111 | def test_add_inventory_category(self): 112 | inventory_id = 1 113 | category_id = 2 114 | name = "Test Category" 115 | parent_id = 0 116 | 117 | self.product_catalog.add_inventory_category( 118 | inventory_id=inventory_id, 119 | category_id=category_id, 120 | name=name, 121 | parent_id=parent_id 122 | ) 123 | 124 | self.mock_request.make_request.assert_called_once_with( 125 | 'addInventoryCategory', 126 | inventory_id=inventory_id, 127 | category_id=category_id, 128 | name=name, 129 | parent_id=parent_id 130 | ) 131 | 132 | def test_delete_inventory_category(self): 133 | category_id = 1 134 | 135 | self.product_catalog.delete_inventory_category(category_id=category_id) 136 | 137 | self.mock_request.make_request.assert_called_once_with( 138 | 'deleteInventoryCategory', 139 | category_id=category_id 140 | ) 141 | 142 | def test_get_inventory_categories(self): 143 | inventory_id = 1 144 | 145 | self.product_catalog.get_inventory_categories(inventory_id=inventory_id) 146 | 147 | self.mock_request.make_request.assert_called_once_with( 148 | 'getInventoryCategories', 149 | inventory_id=inventory_id 150 | ) 151 | 152 | def test_add_inventory_manufacturer(self): 153 | manufacturer_id = 1 154 | name = "Test Manufacturer" 155 | 156 | self.product_catalog.add_inventory_manufacturer( 157 | manufacturer_id=manufacturer_id, 158 | name=name 159 | ) 160 | 161 | self.mock_request.make_request.assert_called_once_with( 162 | 'addInventoryManufacturer', 163 | manufacturer_id=manufacturer_id, 164 | name=name 165 | ) 166 | 167 | def test_delete_inventory_manufacturer(self): 168 | manufacturer_id = 1 169 | 170 | self.product_catalog.delete_inventory_manufacturer(manufacturer_id=manufacturer_id) 171 | 172 | self.mock_request.make_request.assert_called_once_with( 173 | 'deleteInventoryManufacturer', 174 | manufacturer_id=manufacturer_id 175 | ) 176 | 177 | def test_get_inventory_manufacturers(self): 178 | self.product_catalog.get_inventory_manufacturers() 179 | 180 | self.mock_request.make_request.assert_called_once_with('getInventoryManufacturers') 181 | 182 | def test_get_inventory_extra_fields(self): 183 | self.mock_request.make_request.return_value = {'success': True, 'data': {'extra_fields': []}} 184 | result = self.product_catalog.get_inventory_extra_fields() 185 | self.mock_request.make_request.assert_called_with('getInventoryExtraFields') 186 | self.assertTrue(result['success']) 187 | 188 | def test_get_inventory_integrations(self): 189 | self.mock_request.make_request.return_value = {'success': True, 'data': {'integrations': []}} 190 | result = self.product_catalog.get_inventory_integrations(inventory_id=1) 191 | self.mock_request.make_request.assert_called_with('getInventoryIntegrations', inventory_id=1) 192 | self.assertTrue(result['success']) 193 | 194 | def test_get_inventory_available_text_field_keys(self): 195 | self.mock_request.make_request.return_value = {'success': True, 'data': {'text_field_keys': []}} 196 | result = self.product_catalog.get_inventory_available_text_field_keys(inventory_id=1) 197 | self.mock_request.make_request.assert_called_with('getInventoryAvailableTextFieldKeys', inventory_id=1) 198 | self.assertTrue(result['success']) 199 | 200 | def test_add_inventory_product(self): 201 | expected_params = {'inventory_id': 1, 'product_id': 2, 'parent_id': 0, 'is_bundle': False, 202 | 'ean': '1234567890123', 'sku': 'TEST123', 'tax_rate': 20.0, 'weight': 1.5, 203 | 'height': 10.0, 'width': 5.0, 'length': 8.0, 'star': 4, 'manufacturer_id': 3, 204 | 'category_id': 4, 'prices': {1: 100.0, 2: 90.0}, 'stock': {'bl_1': 50, 'shop_2': 30}, 205 | 'locations': {'bl_1': 'A-5-2', 'shop_2': 'B-3-1'}, 'text_fields': {'name': 'Test Product'}, 206 | 'images': ['url:http://example.com/image.jpg'], 'links': ['shop_2_123'], 207 | 'bundle_products': {}} 208 | self.mock_request.make_request.return_value = {'success': True, 'data': {}} 209 | result = self.product_catalog.add_inventory_product(inventory_id=1, product_id=2, parent_id=0, is_bundle=False, 210 | ean='1234567890123', sku='TEST123', tax_rate=20.0, 211 | weight=1.5, height=10.0, width=5.0, length=8.0, star=4, 212 | manufacturer_id=3, category_id=4, 213 | prices={1: 100.0, 2: 90.0}, 214 | stock={'bl_1': 50, 'shop_2': 30}, 215 | locations={'bl_1': 'A-5-2', 'shop_2': 'B-3-1'}, 216 | text_fields={'name': 'Test Product'}, 217 | images=['url:http://example.com/image.jpg'], 218 | links=['shop_2_123'], bundle_products={}) 219 | self.mock_request.make_request.assert_called_with('addInventoryProduct', **expected_params) 220 | self.assertTrue(result['success']) 221 | 222 | def test_delete_inventory_product(self): 223 | expected_params = {'product_id': 1} 224 | self.mock_request.make_request.return_value = {'success': True, 'data': {}} 225 | result = self.product_catalog.delete_inventory_product(product_id=1) 226 | self.mock_request.make_request.assert_called_with('deleteInventoryProduct', **expected_params) 227 | self.assertTrue(result['success']) 228 | 229 | def test_get_inventory_products_data(self): 230 | expected_params = {'inventory_id': 1, 'products': [1, 2, 3]} 231 | self.mock_request.make_request.return_value = {'success': True, 'data': {'products_data': []}} 232 | result = self.product_catalog.get_inventory_products_data(inventory_id=1, products=[1, 2, 3]) 233 | self.mock_request.make_request.assert_called_with('getInventoryProductsData', **expected_params) 234 | self.assertTrue(result['success']) 235 | 236 | def test_get_inventory_products_list(self): 237 | expected_params = {'inventory_id': 1, 'filter_id': 2, 'filter_category_id': 3, 'filter_ean': '1234567890123', 238 | 'filter_sku': 'TEST123', 'filter_name': 'Test', 'filter_price_from': 50.0, 239 | 'filter_stock_from': 20, 'filter_price_to': 100.0, 'page': 1, 'filter_sort': 'id ASC'} 240 | self.mock_request.make_request.return_value = {'success': True, 'data': {'products_list': []}} 241 | result = self.product_catalog.get_inventory_products_list(inventory_id=1, filter_id=2, filter_category_id=3, 242 | filter_ean='1234567890123', filter_sku='TEST123', 243 | filter_name='Test', filter_price_from=50.0, 244 | filter_stock_from=20, filter_price_to=100.0, 245 | page=1, filter_sort='id ASC') 246 | self.mock_request.make_request.assert_called_with('getInventoryProductsList', **expected_params) 247 | self.assertTrue(result['success']) 248 | 249 | def test_get_inventory_products_stock(self): 250 | expected_params = {'inventory_id': 1, 'page': 1} 251 | self.mock_request.make_request.return_value = {'success': True, 'data': {'products_stock': []}} 252 | result = self.product_catalog.get_inventory_products_stock(inventory_id=1, page=1) 253 | self.mock_request.make_request.assert_called_with('getInventoryProductsStock', **expected_params) 254 | self.assertTrue(result['success']) 255 | 256 | def test_update_inventory_products_stock(self): 257 | expected_params = {'inventory_id': 1, 258 | 'products': {1: {'bl_1': 50, 'shop_2': 30}, 2: {'bl_1': 20, 'shop_2': 10}}} 259 | self.mock_request.make_request.return_value = {'success': True, 'data': {}} 260 | result = self.product_catalog.update_inventory_products_stock(inventory_id=1, 261 | products={1: {'bl_1': 50, 'shop_2': 30}, 262 | 2: {'bl_1': 20, 'shop_2': 10}}) 263 | self.mock_request.make_request.assert_called_with('updateInventoryProductsStock', **expected_params) 264 | self.assertTrue(result['success']) 265 | 266 | def test_get_inventory_products_prices(self): 267 | expected_params = {'inventory_id': 1, 'page': 1} 268 | self.mock_request.make_request.return_value = {'success': True, 'data': {'products_prices': []}} 269 | result = self.product_catalog.get_inventory_products_prices(inventory_id=1, page=1) 270 | self.mock_request.make_request.assert_called_with('getInventoryProductsPrices', **expected_params) 271 | self.assertTrue(result['success']) 272 | 273 | def test_update_inventory_products_prices(self): 274 | expected_params = {'inventory_id': 1, 'products': {1: {1: 100.0, 2: 90.0}, 2: {1: 80.0, 2: 70.0}}} 275 | self.mock_request.make_request.return_value = {'success': True, 'data': {}} 276 | result = self.product_catalog.update_inventory_products_prices(inventory_id=1, 277 | products={1: {1: 100.0, 2: 90.0}, 278 | 2: {1: 80.0, 2: 70.0}}) 279 | self.mock_request.make_request.assert_called_with('updateInventoryProductsPrices', **expected_params) 280 | self.assertTrue(result['success']) 281 | 282 | def test_get_inventory_product_logs(self): 283 | expected_params = {'product_id': 1, 'date_from': 1640908800, 'date_to': 1640995199, 'log_type': 1, 284 | 'sort': 'ASC', 'page': 1} 285 | self.mock_request.make_request.return_value = {'success': True, 'data': {'product_logs': []}} 286 | result = self.product_catalog.get_inventory_product_logs(product_id=1, date_from=1640908800, date_to=1640995199, 287 | log_type=1, sort='ASC', page=1) 288 | self.mock_request.make_request.assert_called_with('getInventoryProductLogs', **expected_params) 289 | self.assertTrue(result['success']) 290 | -------------------------------------------------------------------------------- /tests/test_request.py: -------------------------------------------------------------------------------- 1 | import unittest 2 | import requests 3 | from unittest.mock import patch 4 | from baselinker import Request 5 | 6 | 7 | class TestRequest(unittest.TestCase): 8 | 9 | def setUp(self): 10 | self.api_token = 'my_token' 11 | self.request = Request(api_token=self.api_token) 12 | 13 | def test_init_with_valid_api_token(self): 14 | request = Request(api_token=self.api_token) 15 | self.assertEqual(request.api_token, self.api_token) 16 | self.assertEqual(request.api_url, 'https://api.baselinker.com/connector.php') 17 | 18 | def test_init_with_empty_api_token(self): 19 | with self.assertRaises(ValueError): 20 | Request(api_token='') 21 | 22 | def test_get_request_data_without_parameters(self): 23 | method_name = 'test_method' 24 | expected_request_data = {'method': method_name} 25 | 26 | request_data = self.request._Request__get_request_data(method_name) 27 | 28 | self.assertEqual(request_data, expected_request_data) 29 | 30 | def test_get_request_data_with_parameters(self): 31 | method_name = 'test_method' 32 | parameters = {'param1': 'value1', 'param2': 'value2'} 33 | expected_request_data = {'method': method_name, 'parameters': '{"param1": "value1", "param2": "value2"}'} 34 | 35 | request_data = self.request._Request__get_request_data(method_name, parameters) 36 | 37 | self.assertEqual(request_data, expected_request_data) 38 | 39 | def test_get_request_headers(self): 40 | expected_headers = {'X-BLToken': self.api_token} 41 | 42 | headers = self.request._Request__get_request_headers() 43 | 44 | self.assertEqual(headers, expected_headers) 45 | 46 | @patch('requests.Session.post') 47 | def test_make_request_success(self, mock_post): 48 | method_name = 'test_method' 49 | parameters = {'param1': 'value1', 'param2': 'value2'} 50 | expected_request_data = {'method': method_name, 'parameters': '{"param1": "value1", "param2": "value2"}'} 51 | expected_headers = {'X-BLToken': self.api_token} 52 | expected_response_content = '{"status": "success", "result": "Test Result"}' 53 | mock_post.return_value.content.decode.return_value = expected_response_content 54 | 55 | response = self.request.make_request(method_name, **parameters) 56 | 57 | mock_post.assert_called_once_with(self.request.api_url, data=expected_request_data, headers=expected_headers) 58 | self.assertEqual(response, {'status': 'success', 'result': 'Test Result'}) 59 | 60 | @patch('requests.Session.post', side_effect=requests.RequestException('Test Error')) 61 | @patch('builtins.print') 62 | def test_make_request_failure(self, mock_print, mock_post): 63 | method_name = 'test_method' 64 | parameters = {'param1': 'value1', 'param2': 'value2'} 65 | 66 | with self.assertRaises(requests.RequestException): 67 | self.request.make_request(method_name, **parameters) 68 | --------------------------------------------------------------------------------