commit d70da1d39d7941dfa70e1d9276b7e1a1158cbe0a Author: Igor Raznatovic Date: Sat Jan 27 22:32:07 2024 +0100 Rewritten from python 2.7 to python 3.11 diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..26d3352 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,3 @@ +# Default ignored files +/shelf/ +/workspace.xml diff --git a/.idea/inspectionProfiles/profiles_settings.xml b/.idea/inspectionProfiles/profiles_settings.xml new file mode 100644 index 0000000..105ce2d --- /dev/null +++ b/.idea/inspectionProfiles/profiles_settings.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..e144d96 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,7 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..205c29e --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/mssql2mysql.iml b/.idea/mssql2mysql.iml new file mode 100644 index 0000000..2c80e12 --- /dev/null +++ b/.idea/mssql2mysql.iml @@ -0,0 +1,10 @@ + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/abacus2woo.py b/abacus2woo.py new file mode 100644 index 0000000..91804b6 --- /dev/null +++ b/abacus2woo.py @@ -0,0 +1,269 @@ +import ast +import sqlite3 +import sys +import time +import pyodbc +from woocommerce import API + +# import all_from_woo + +conn = sqlite3.connect('imd.db') # intermediary db +c = conn.cursor() + +# Create table in SLQLite - we use this for syncing +c.execute('''CREATE TABLE IF NOT EXISTS products (id INTEGER NOT NULL PRIMARY KEY ON CONFLICT IGNORE, aid INTEGER, + sifra TEXT, naziv TEXT, price REAL, qty INTEGER, snc INTEGER, grupa INTEGER)''') +# all_from_woo.initSQLite() + +# Connect to WooCommerce API +wcapi = API( + url="https://www.rasterdoo.com/", + consumer_key="ck_b3169b1723f6f39a965ed04ecfa77860fb89bbf5", + consumer_secret="cs_a83f0217ed8d6191ab7de9df06a0c2b652f6bd57", + wp_api=True, + timeout=360, + version="wc/v2" +) +print("Resting for for a bit...") +# time.sleep(50) + +print("Connecting to MS SQL server") +''' +cnxn = pyodbc.connect('Driver={SQL Server};' + 'Server=SERVER-BS;' + 'Database=abacus_raster;' + 'Trusted_Connection=yes;') +''' +cnxn = pyodbc.connect('DRIVER={SQL Server};' + 'SERVER=raster.mywire.org,1433;' + 'DATABASE=abacus_raster;' + 'UID=sa;' + 'PWD=!Pos456!;' + 'TDS_Version=7.3;') + +cursor = cnxn.cursor() + +# Prvi artikal pocetnog stanja 2019 se vodi pod brojem "306570", za 2020 pod brojem "396498". +# Ako racunamo lager za artikle od njega dobicemo pravo stanje za 2019. Ukoliko ovo ne uradimo, +# lager se racuna od pocetka rada sa Abacusom - i vodi do gresaka. +# prvo nadjemo id_document pocetnog stanja (prvog dokumenta u godini) +# /****** Script for SelectTopNRows command from SSMS ******/ +# SELECT id_dokument, id_vrsta, broj_dok, datum +# FROM abacus_raster.dbo.artikal_dokument +# WHERE (datum > CONVERT(DATETIME, '2020-12-31 00:00:00', 102)) +# +# A onda nadjemo artikal u lageru - Stari kod: +# SELECT id_artikal, multiply * kol AS qty +# FROM abacus_raster.dbo.artikal_dnevnik +# WHERE (multiply <> 0) AND id_dnevnik >=396498. +cursor.execute('''SELECT a.id_artikal, + SUM (a.qty) AS stanje + INTO tmplager + FROM (SELECT artikal_dnevnik.id_artikal, +artikal_dnevnik.multiply*artikal_dnevnik.kol AS 'qty' +FROM abacus_raster.dbo.artikal_dnevnik artikal_dnevnik, abacus_raster.dbo.artikal_dokument artikal_dokument +WHERE artikal_dnevnik.id_dokument = artikal_dokument.id_dokument AND ((artikal_dokument.datum_fak>={d '2024-01-01'})) AND (artikal_dokument.id_vrsta <> 18) ) \ + AS a INNER JOIN + abacus_raster.dbo.artikal ON a.id_artikal = abacus_raster.dbo.artikal.id_artikal + GROUP BY a.id_artikal ''') + +cursor.execute('''SELECT abacus_raster.dbo.artikal_cijenik.id_artikal, abacus_raster.dbo.artikal_cijenik.cijena, + abacus_raster.dbo.artikal.naziv, abacus_raster.dbo.artikal.sifra, + tmplager.stanje, abacus_raster.dbo.artikal.id_grupa, tmplager.id_artikal AS tid +FROM abacus_raster.dbo.artikal_cijenik INNER JOIN + abacus_raster.dbo.artikal INNER JOIN + tmplager ON abacus_raster.dbo.artikal.id_artikal = tmplager.id_artikal ON + abacus_raster.dbo.artikal_cijenik.id_artikal = abacus_raster.dbo.artikal.id_artikal +WHERE (abacus_raster.dbo.artikal_cijenik.id_mjesto = 2) AND abacus_raster.dbo.artikal.id_grupa <> 11''') +# Save products from Abacus in SQLite table +row = cursor.fetchone() +print("Abacus row in works:"), row # +snc = 0 +for row in cursor: + while row is not None: + # print("Abacus row entered for loop and it is not None") + # input("Press Enter to continue...") # + aid = row[0] + aprice = float(row[1]) + aname = row[2] + asifra = row[3] + aqty = int(row[4]) + agroup = row[5] + print("MS Sql group value:", agroup) + # This dictionary matches abacus product category id to equivalent id in woocommerce + groupdict = { + 1: 21472, 2: 21473, 4: 21474, 5: 21475, 10: 21476, 11: 21477, 13: 21477, 15: 21477, 19: 21478, 24: 21479, + 30: 21480, 32: 21481, 33: 21482, 37: 21483, 38: 21484, 44: 21485, 45: 21486, 47: 21477, 48: 21477 + } + print("Coverted to Woocommerce group it becomes wgroup:", groupdict[agroup]) + if agroup in groupdict: + wgroup = groupdict[agroup] + print("Group in abacusu is from dictionary:", agroup, " - while woocommerce now holds:", wgroup) # + else: + wgroup = 3557 + print("Group ", agroup, "does not exist in dictionary - we put it in woocommerce group -other-:", wgroup) # + + # Test id Woo product exists in SQLite table + q = (asifra,) + c.execute('SELECT * FROM products WHERE sifra=?', q) + print("Abacus data to be saved in SQLite:", aid, aprice, aname, asifra, aqty, wgroup) + # input("Press Enter to continue...") # + # We test SqlLite rows so that we update the table only with products that changed + trow = c.fetchone() + if trow is not None: + tnaziv = trow[3] + tprice = trow[4] + tqty = trow[5] + tgroup = trow[7] + # print("There is an SQLite trow we need to check for an update: "), tnaziv, tprice, tqty, tgroup + if aprice != tprice: + snc = 1 + print("bad price") + if aqty != tqty: + snc = 1 + print("bad qty") + if aname != tnaziv: + snc = 1 + print("bad naziv") + if wgroup != tgroup: + snc = 1 + print(wgroup, "bad tgroup: ", tgroup) + if snc == 1: + # print("=======") + c.execute('''UPDATE products SET aid = ?, price = ?, qty =?, naziv = ?, + snc = ?, grupa = ? WHERE sifra = ?''', + (aid, aprice, aqty, aname, snc, wgroup, asifra)) + conn.commit() + c.execute('SELECT * FROM products WHERE sifra=?', q) + trow = c.fetchone() + print(trow) + print("=======") + # input("Press Enter to continue...") + else: + print("If sqlite trow does not exist (is ", trow, "), then first create one in Woo", aid, asifra, aname, + aprice, aqty, wgroup) + # input("Press Enter to continue...") # + aprice = str(aprice) + data = { + "sku": asifra, + "name": aname, + "regular_price": aprice, + "description": "", + "managing_stock": "true", + "in_stock": "true", + "status": "publish", + "stock_quantity": aqty, + "categories": [ + { + "id": wgroup + } + ], + } + w = wcapi.post("products", data).json() + + + # w = wcapi.get("products/categories").json() + + class ListStream: + def __init__(self): + self.data = [] + + def write(self, s): + self.data.append(s) + + + sys.stdout = x = ListStream() + print(w) + sys.stdout = sys.__stdout__ + wresponse = (x.data[0]) + print("Full woo response: ", wresponse) + # print("wresponse:", wresponse[38:53]) + if wresponse[38:53] == "duplicirani SKU": + # if wresponse[26:40] == "duplicated SKU": + d = ast.literal_eval(wresponse) + errd = d.get('data') + print(errd) + rogueone = errd.get('resource_id') + print("We have a duplicated product in Woo: ", rogueone) + c.execute('INSERT INTO products VALUES (?,?,?,?,?,?,?,?)', + (rogueone, aid, asifra, aname, aprice, aqty, 1, wgroup)) # if no product it will create + print("Inserting rogueone in SQLlite data:", rogueone, aid, asifra, aname, aprice, aqty, 1, wgroup) + conn.commit() + # rgpath = "\"products/" + str(rogueone) + "\"" + # print rgpath + # rg = wcapi.get(rogueone).json() + # print("The rogueone:", rg) + else: + print("Since trow was NONE, we had to create it on woo, get the id (woo), then save it (SQLite):", + type(w), w) + wid = w.get('id') + wsku = w.get('sku') + wname = w.get('name') + wprice = w.get('price') + wqty = w.get('qty') + if wname is None: + print("Why problems? ...:", wid, aid, wsku, wname, wprice, wqty) + else: + c.execute('INSERT INTO products VALUES (?,?,?,?,?,?,?,"")', + (wid, aid, wsku, wname, wprice, wqty, 1)) # if no product it will create + conn.commit() + row = cursor.fetchone() + # print("Resting for for a bit...") + # time.sleep(5) +conn.close() + +# Update Woo with our SQLite table +conn = sqlite3.connect('imd.db') # intermediary db +c = conn.cursor() +c.execute('''SELECT * FROM products WHERE snc = 1 ORDER BY naziv''') +trow = c.fetchone() + +print("Selected for update:", trow) +# cleantrow is not None: +grupa = 0 +while trow is not None: + id = trow[0] + tnaziv = trow[3] + tprice = str(trow[4]) + tqty = trow[5] + tgroup = trow[7] + # print type(id), type(tnaziv), type(tprice), type(tqty), "Grupa:", type(tgroup), tgroup + # input("Press enter ...") + data = { + "id": id, + "manage_stock": "true", + "stock_quantity": tqty, + "name": tnaziv, + "status": "publish", + "regular_price": tprice, + "categories": [ + { + "id": tgroup + } + ], + } + # print data + uplink = "products/" + str(id) + # print uplink + up = wcapi.put((uplink), data).json() + print(up) + # input("Press enter ...") + if trow is None: + break + else: + trow = c.fetchone() + print("___________________________________________") + print("Selected next for update:", trow) +c.execute('UPDATE products SET snc = 0') +conn.commit() + +# Drop the temporary table + +print("Preparing to drop the tmptable") +cursor.execute('''drop table tmplager''') +print("Dropped the tmptable") +cursor.close() + +del cursor +conn.close() # close sqlite connection +print("All done!") diff --git a/all_from_woo.py b/all_from_woo.py new file mode 100644 index 0000000..e92980e --- /dev/null +++ b/all_from_woo.py @@ -0,0 +1,78 @@ +def initSQLite(): + # The following will download all existing woocommerce products and save them in SQLite. + # From that point the synchronization can start in local. + from woocommerce import API + import sqlite3 + import pyodbc + + conn = sqlite3.connect('imd.db') # intermediary db + c = conn.cursor() + # Create table in SLQLite - we use this for syncing + c.execute('DROP TABLE products') + c.execute('''CREATE TABLE IF NOT EXISTS products (id INTEGER NOT NULL PRIMARY KEY ON CONFLICT IGNORE, aid INTEGER, + sifra TEXT, naziv TEXT, price REAL, qty INTEGER, snc INTEGER, grupa INTEGER)''') + + + # Connect to WooCommerce API + wcapi = API( + url="https://www.rasterdoo.com/", + consumer_key="ck_b3169b1723f6f39a965ed04ecfa77860fb89bbf5", + consumer_secret="cs_a83f0217ed8d6191ab7de9df06a0c2b652f6bd57", + wp_api=True, + version="wc/v2" + ) + r = wcapi.get("products") # get woo web page where products are + h = int(r.headers['X-WP-TotalPages']) # in the header we see number of tot pages + print("Total pages:", h) + + for page_no in range(1, h+1): # lets go through pages + goto_page = 'products?page=' + str(page_no) + print(page_no, "(", h, ")") + r = wcapi.get(goto_page) + page_txt = r.json() # contents of one page ar stored in variable + # We fill our SQLite table with Woo Products + for product in page_txt: # first we go through products in this page + wsku = product.get('sku') + wid = product.get('id') + wname = product.get('name') + wprice = product.get('regular_price') + wqty = product.get('stock_quantity') + if wqty is None: + wqty = 0 + # FIX THIS! - It does not update - just inserts if new ... + c.execute('INSERT INTO products VALUES (?,?,?,?,?,?,?,?)', + (wid, "", wsku, wname, wprice, wqty, 0)) # if no product it will create + c.execute('UPDATE products SET aid = ?, sifra = ?, naziv = ?, price = ?, qty = ?, snc = ? WHERE id = ?', + ("", wsku, wname, wprice, wqty, 0, wid, "")) + print("Insert woocommerce data to SQLlite:", wid, "", wsku, wname, wprice, wqty, 0) + conn.commit() + + print("") + print("Fixing some problems with decimals...") + c.execute('''UPDATE products SET price = replace( price, ',', '.' ) WHERE price LIKE ?''', ('%,%',)) + conn.commit() + + # Sometimes woo has a product, but abacus does not, meaning we don't have them anymore. SO, qty needs to be 0. + print("Checking if products on woo exist on abacus") + # list of woo products + c.execute('''SELECT sifra, naziv, qty FROM products''') + trow = c.fetchone() + # find them on abacus + cnxn = pyodbc.connect('Driver={SQL Server};' + 'Server=SERVER-BS;' + 'Database=abacus_raster;' + 'Trusted_Connection=yes;') + cursor = cnxn.cursor() + + while trow is not None: + sifra = trow[0] + naziv = trow[1] + cursor.execute('''SELECT id_artikal, sifra, naziv FROM abacus_raster.dbo.artikal WHERE sifra = ?''', (sifra, )) + row = cursor.fetchone() + if row is None: + print(sifra, naziv, " - does not exist on abacus, at least we should set qty to 0") + c.execute('''UPDATE products SET qty = 0 WHERE sifra = ?''', (sifra,)) + conn.commit() + trow = c.fetchone() + + conn.close() diff --git a/imd.db b/imd.db new file mode 100644 index 0000000..938d121 Binary files /dev/null and b/imd.db differ