- Custom modules: hazard_shopify, hazard_inventory_report, hazard_website - Third-party modules: bi_sql_editor, query_deluxe, sql_request_abstract, l10n_mx_sat_sync_itadmin_ee - Docker setup: Odoo 16 + PostgreSQL 17 - Utility scripts: backup_hazard.sh, update_module.py - Agent configuration: .agents/AGENTS.md - Security: Enterprise modules, credentials, backups and production data excluded via .gitignore
93 lines
3.4 KiB
Python
93 lines
3.4 KiB
Python
import csv
|
|
import json
|
|
from collections import defaultdict
|
|
import xmlrpc.client
|
|
import requests
|
|
|
|
ODOO_URL = 'http://localhost:16001'
|
|
ODOO_DB = 'hazard_new'
|
|
ODOO_USER = 'admin'
|
|
ODOO_PASS = 'admin'
|
|
|
|
def get_shopify_credentials():
|
|
common = xmlrpc.client.ServerProxy(f'{ODOO_URL}/xmlrpc/2/common')
|
|
uid = common.authenticate(ODOO_DB, ODOO_USER, ODOO_PASS, {})
|
|
models = xmlrpc.client.ServerProxy(f'{ODOO_URL}/xmlrpc/2/object')
|
|
|
|
config_ids = models.execute_kw(ODOO_DB, uid, ODOO_PASS, 'shopify.config', 'search', [[]])
|
|
config = models.execute_kw(ODOO_DB, uid, ODOO_PASS, 'shopify.config', 'read', [config_ids[0]], {'fields': ['shop_url', 'access_token']})[0]
|
|
return config.get('shop_url'), config.get('access_token')
|
|
|
|
def get_collection_products(shop_url, access_token, collection_id):
|
|
query = """
|
|
query getProducts($id: ID!, $cursor: String) {
|
|
collection(id: $id) {
|
|
products(first: 250, after: $cursor) {
|
|
pageInfo {
|
|
hasNextPage
|
|
endCursor
|
|
}
|
|
edges {
|
|
node {
|
|
legacyResourceId
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
"""
|
|
url = f"https://{shop_url}/admin/api/2023-10/graphql.json"
|
|
headers = {
|
|
'X-Shopify-Access-Token': access_token,
|
|
'Content-Type': 'application/json',
|
|
}
|
|
product_ids = set()
|
|
cursor = None
|
|
has_next_page = True
|
|
while has_next_page:
|
|
variables = {"id": f"gid://shopify/Collection/{collection_id}"}
|
|
if cursor: variables["cursor"] = cursor
|
|
resp = requests.post(url, headers=headers, json={'query': query, 'variables': variables})
|
|
data = resp.json().get('data', {}).get('collection', {}).get('products', {})
|
|
if not data: break
|
|
for edge in data.get('edges', []):
|
|
product_ids.add(str(edge['node']['legacyResourceId']))
|
|
page_info = data.get('pageInfo', {})
|
|
has_next_page = page_info.get('hasNextPage', False)
|
|
cursor = page_info.get('endCursor')
|
|
return product_ids
|
|
|
|
def get_product_skus(shop_url, access_token, product_ids):
|
|
skus = set()
|
|
url = f"https://{shop_url}/admin/api/2023-10/products.json"
|
|
headers = {'X-Shopify-Access-Token': access_token}
|
|
for pid in product_ids:
|
|
r = requests.get(f"{url}?ids={pid}", headers=headers)
|
|
for p in r.json().get('products', []):
|
|
for v in p.get('variants', []):
|
|
skus.add(v.get('sku'))
|
|
return skus
|
|
|
|
shop_url, access_token = get_shopify_credentials()
|
|
padel_products = get_collection_products(shop_url, access_token, '487990952129')
|
|
padel_skus = get_product_skus(shop_url, access_token, padel_products)
|
|
|
|
filename = 'orders_export_1 (8)(1).csv'
|
|
sales_by_source = defaultdict(float)
|
|
|
|
with open(filename, 'r', encoding='utf-8-sig') as f:
|
|
reader = csv.DictReader(f)
|
|
for row in reader:
|
|
if row.get('Lineitem sku') in padel_skus:
|
|
if row.get('Financial Status') in ['paid', 'partially_refunded', 'refunded']:
|
|
source = row.get('Source', 'unknown')
|
|
price = float(row.get('Lineitem price') or 0)
|
|
qty = int(row.get('Lineitem quantity') or 0)
|
|
discount = float(row.get('Lineitem discount') or 0)
|
|
sales_by_source[source] += (price * qty) - discount
|
|
|
|
total = sum(sales_by_source.values())
|
|
print(f"Total from CSV: {total}")
|
|
for k,v in sales_by_source.items():
|
|
print(f" {k}: {v}")
|