From 26ce8c87c621f06a460b08b00517472e96b7193c Mon Sep 17 00:00:00 2001 From: Jadowyne Ulve Date: Sun, 27 Apr 2025 12:54:32 -0500 Subject: [PATCH] Setup to update items to new schema --- application/items/__init__.py | 0 .../__pycache__/__init__.cpython-312.pyc | Bin 0 -> 177 bytes .../database_items.cpython-312.pyc | Bin 0 -> 2885 bytes .../__pycache__/items_API.cpython-312.pyc | Bin 0 -> 37493 bytes application/items/database_items.py | 42 ++ application/items/items_API.py | 561 ++++++++++++++++++ database.log | 32 +- webserver.py | 5 +- 8 files changed, 637 insertions(+), 3 deletions(-) create mode 100644 application/items/__init__.py create mode 100644 application/items/__pycache__/__init__.cpython-312.pyc create mode 100644 application/items/__pycache__/database_items.cpython-312.pyc create mode 100644 application/items/__pycache__/items_API.cpython-312.pyc create mode 100644 application/items/database_items.py create mode 100644 application/items/items_API.py diff --git a/application/items/__init__.py b/application/items/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/application/items/__pycache__/__init__.cpython-312.pyc b/application/items/__pycache__/__init__.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..9e439974b790f8d932921ce4bc400a0b2388228a GIT binary patch literal 177 zcmX@j%ge<81P@aAGC=fW5P=Rpvj9b=GgLBYGWxA#C}INgK7-W!3P`nz@k^~JNzTbH zO^I>NPf1NmEKV&3ij@Z>=9Lsx#uVfim!ub^78mB|7F3pGYY$@H3}L@qo9c#(2^)LLN$`JnlF2b2z z!$TiU3-JMrX?smfqw$5LFExE=0h2cA%hB3$UFlPw`gW)ZDZccZy<0det)_E#-@lpt z<~K7tzwdtZ`#lI+^H|BqBR4{S5XWg;!FYZSj9DZjnTn$^YJj4M?TWhwT(+hMXqlI3 zg^?MBm086-;&cq8vo|luH!;B&kKp5|XN>YuDoP)gFt{V!C2+$!IFBMrDhW z)WmfK>k?gnL6iVL;y;hL2qc{%LDEL^YMXpm@Jw2!40IPDe@dH-OxrdCHh?t&s>umR z-<)NX*_On1WHyEj`X2Q$)q+gk;76UUDH`g`1|tMR7$rzwX$E6Z4*@r66oN6{yj?7v zET+fs;3mU`j~Jc~^odt;4I{~QWj{iuSNA(ljFvdomWI4#Tb^SXUT5YR*R2KynQqey zGcblra~U4f{V#}FgOc5QAj!~nlEmugtc1AFPXl zpXr@I_2?MVs0r{d!wymCR_!Ex8BI{H##6|N>$@BT4G4!!U(w!FJ3U+4@EMfBJS?0w znMFpX@1`$R&=b@|A+yN!Y?SnMp6d+v2M-0iF7%!c-e?=taWtVtB|WAlwBV)pIxlnv z^Qj(e+K5k!@gd*wCHI6kETbwefNaJvy_#s7?MxyaJCabop z_3>m}>4@2Dh~iWitVYEt%PT9Igkwpvw8ArLLa~I-NlD2E{T)#~dNrykofxZF^kZ^Q zFiQjw2eL}#LjPa^9S!%|5e!3B8NOe!+b}! zylS>~ruKGS4)Oj&>H2J0<*awcI~PvZJSh{iHGx@U#<+cRn)$7==FYoWY53RRNG39P zZ_i5T>~iSrclF;lEi`5NF8&g_lnIVZcY}bDUvSxA!!%&XC+oh?%~KCNY4b^0OLlkd z=e|3>yCvys%e%#CcD)?t%~haXb#qFlvVOe^V672se?9Y{P>fm^NIwC$u}(wS#{Kd| zHS$$GLli8GFJIyJE%W=*YBXz1F|K7@_?vZ{kZ-;{@2=mp>5u*fdF zb0FNnFTQmQ?4?pK#FqjB3|VSmNknAB$AzV%9ErEE;WOOQDVp@2A@VULbR4E1dulow z>Bo&M*xM=sVPd-?`~+w9e=CB?zP19;MHRqpxL;ENID>nG3c&qm1%Nt(_d^8;7IE56 ze9!I3_q@ZrCJTA;JkLYLdxd=sb|*%Dm2YqDa`<*TKNsF z+kuCLn}W^5po@5z>A^facq6bAPugtTib<^%y_o!y!K9{Q9}$E=FxfCnu-Wv7NoB<4 zM34%G2{aL^Ah9=@3|A4CR1eFYrz{*Gky;}567d!Yjm)-1Pk0}=R>^iI!SxU%k7=tQ zb|#Wj8BZ$^iNAEJE9dg=NE*Bl*hU&||NhsJ#%f*TO5N$@y3=1v->Q$)AM~H=`lq|{ zEKwJl?ppUll3WSe6#y;}0&57wx{JgR2)Om&0|5wumoF;b=+>Wy1%6i5-iE#^Z*Qg- zn6v)&26nMr0B12EKzy-*Z9gI`iX4eFv+XCj#TJ^xPuj9g2shG8Y)v>sFNIjJMXFyE zEN>Bi=TC6+kS$u%vBlc;%wn#pYTV{+d(Z7R;yFM<_PK2EZDXoD9#_s_5n|*|qt*@r z7>lBQN1><4{}h$3xhtubxyl>@l^(a%y!@PaYlljqht45TX~kCa{V#}z4?>#10lXQB AasU7T literal 0 HcmV?d00001 diff --git a/application/items/__pycache__/items_API.cpython-312.pyc b/application/items/__pycache__/items_API.cpython-312.pyc new file mode 100644 index 0000000000000000000000000000000000000000..80b1df0a0bbe049031e6b1624c4f9dbc4babc1ca GIT binary patch literal 37493 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 literal 0 HcmV?d00001 diff --git a/application/items/database_items.py b/application/items/database_items.py new file mode 100644 index 0000000..7dcabfa --- /dev/null +++ b/application/items/database_items.py @@ -0,0 +1,42 @@ +from application import postsqldb +import config +import psycopg2 + + +def getTransactions(site:str, payload: tuple, convert:bool=True): + database_config = config.config() + sql = f"SELECT * FROM {site}_transactions WHERE logistics_info_id=%s LIMIT %s OFFSET %s;" + sql_count = f"SELECT COUNT(*) FROM {site}_transactions WHERE logistics_info_id=%s;" + recordset = () + count = 0 + try: + with psycopg2.connect(**database_config) as conn: + with conn.cursor() as cur: + cur.execute(sql, payload) + rows = cur.fetchall() + if rows and convert: + recordset = [postsqldb.tupleDictionaryFactory(cur.description, row) for row in rows] + if rows and not convert: + recordset = rows + cur.execute(sql_count, (payload[0],)) + count = cur.fetchone()[0] + return recordset, count + except Exception as error: + postsqldb.DatabaseError(error, payload, sql) + +def getTransaction(site:str, payload: tuple, convert:bool=True): + database_config = config.config() + sql = f"SELECT * FROM {site}_transactions WHERE id=%s;" + record = () + try: + with psycopg2.connect(**database_config) as conn: + with conn.cursor() as cur: + cur.execute(sql, payload) + rows = cur.fetchone() + if rows and convert: + record = postsqldb.tupleDictionaryFactory(cur.description, rows) + if rows and not convert: + record = rows + return record + except Exception as error: + postsqldb.DatabaseError(error, payload, sql) \ No newline at end of file diff --git a/application/items/items_API.py b/application/items/items_API.py new file mode 100644 index 0000000..17f644e --- /dev/null +++ b/application/items/items_API.py @@ -0,0 +1,561 @@ +from flask import Blueprint, request, render_template, redirect, session, url_for, send_file, jsonify, Response +import psycopg2, math, json, datetime, main, copy, requests, process, database, pprint, MyDataclasses +from config import config, sites_config +from main import unfoldCostLayers +from user_api import login_required +import application.postsqldb as db +from application.items import database_items + +items_api = Blueprint('items_api', __name__) + +@items_api.route("/item//itemLink/") +@login_required +def itemLink(parent_id, id): + sites = [site[1] for site in main.get_sites(session['user']['sites'])] + return render_template("items/itemlink.html", current_site=session['selected_site'], sites=sites, proto={'referrer': request.referrer}, id=id) + +@items_api.route("/item/getTransactions", methods=["GET"]) +def getTransactions(): + """ GET a subquery of transactions by passing a logistics_info_id, limit, and page + --- + responses: + 200: + description: transactions received successfully. + """ + if request.method == "GET": + recordset = [] + count = 0 + logistics_info_id = int(request.args.get('logistics_info_id', 1)) + page = int(request.args.get('page', 1)) + limit = int(request.args.get('limit', 50)) + site_name = session['selected_site'] + offset = (page - 1) * limit + recordset, count = database_items.getTransactions(site_name, (logistics_info_id, limit, offset)) + return jsonify({"transactions": recordset, "end": math.ceil(count/limit), "error": False, "message": ""}) + return jsonify({"transactions": recordset, "end": math.ceil(count/limit), "error": True, "message": f"method {request.method} is not allowed."}) + +@items_api.route("/item/getTransaction", methods=["GET"]) +def getTransaction(): + """ GET a transaction from the system by passing an ID + --- + parameters: + - in: query + name: id + schema: + type: integer + minimum: 1 + default: 1 + required: true + description: The transaction.id + responses: + 200: + description: Transaction Object received successfully. + """ + transaction = () + if request.method == "GET": + id = int(request.args.get('id', 1)) + site_name = session['selected_site'] + transaction = database_items.getTransaction(site_name, (id, )) + return jsonify({"transaction": transaction, "error": False, "message": ""}) + return jsonify({"transaction": transaction, "error": True, "message": f"method {request.method} is not allowed."}) + +@items_api.route("/item/getItem") +def get_item(): + id = int(request.args.get('id', 1)) + database_config = config() + site_name = session['selected_site'] + item = [] + with psycopg2.connect(**database_config) as conn: + item = database.getItemAllByID(conn, site_name, payload=(id, ), convert=True) + return jsonify(item=item) + +@items_api.route("/item/getItemsWithQOH", methods=['GET']) +@login_required +def pagninate_items(): + pantry_inventory = [] + count = {'count': 0} + if request.method == "GET": + page = int(request.args.get('page', 1)) + limit = int(request.args.get('limit', 10)) + search_string = str(request.args.get('search_text', "")) + sort = request.args.get('sort', "") + order = request.args.get('order', "") + + view = request.args.get('view', "") + site_name = session['selected_site'] + offset = (page - 1) * limit + if sort == 'total_qoh': + sort_order = f"{sort} {order}" + else: + sort_order = f"{site_name}_items.{sort} {order}" + print(sort_order) + database_config = config() + with psycopg2.connect(**database_config) as conn: + pantry_inventory, count = database.getItemsWithQOH(conn, site_name, (search_string, limit, offset, sort_order), convert=True) + + return jsonify({'items': pantry_inventory, "end": math.ceil(count['count']/limit), 'error':False, 'message': 'Items Loaded Successfully!'}) + return jsonify({'items': pantry_inventory, "end": math.ceil(count['count']/limit), 'error':True, 'message': 'There was a problem loading the items!'}) + +@items_api.route('/item/getModalItems', methods=["GET"]) +@login_required +def getModalItems(): + recordset = [] + count = {'count': 0} + if request.method == "GET": + page = int(request.args.get('page', 1)) + limit = int(request.args.get('limit', 10)) + search_string = request.args.get('search_string', '') + site_name = session['selected_site'] + offset = (page - 1) * limit + database_config = config() + with psycopg2.connect(**database_config) as conn: + payload = (search_string, limit, offset) + recordset, count = database.getItemsForModal(conn, site_name, payload, convert=True) + return jsonify({"items":recordset, "end":math.ceil(count['count']/limit), "error":False, "message":"items fetched succesfully!"}) + return jsonify({"items":recordset, "end":math.ceil(count['count']/limit), "error":True, "message":"There was an error with this GET statement"}) + +@items_api.route('/item/getPrefixes', methods=["GET"]) +@login_required +def getModalPrefixes(): + recordset = [] + count = {'count': 0} + if request.method == "GET": + page = int(request.args.get('page', 1)) + limit = int(request.args.get('limit', 10)) + site_name = session['selected_site'] + offset = (page - 1) * limit + database_config = config() + with psycopg2.connect(**database_config) as conn: + payload = (limit, offset) + recordset, count = postsqldb.SKUPrefixTable.paginatePrefixes(conn, site_name, payload, convert=True) + return jsonify({"prefixes":recordset, "end":math.ceil(count/limit), "error":False, "message":"items fetched succesfully!"}) + return jsonify({"prefixes":recordset, "end":math.ceil(count/limit), "error":True, "message":"There was an error with this GET statement"}) + + +@items_api.route('/item/getZones', methods=['GET']) +def getZones(): + page = int(request.args.get('page', 1)) + limit = int(request.args.get('limit', 1)) + database_config = config() + site_name = session['selected_site'] + zones = [] + offset = (page - 1) * limit + payload = (limit, offset) + count = 0 + with psycopg2.connect(**database_config) as conn: + zones, count = database.getZonesWithCount(conn, site_name, payload, convert=True) + print(count, len(zones)) + return jsonify(zones=zones, endpage=math.ceil(count[0]/limit)) + + +@items_api.route('/item/getZonesBySku', methods=["GET"]) +def getZonesbySku(): + if request.method == "GET": + page = int(request.args.get('page', 1)) + limit = int(request.args.get('limit', 1)) + item_id = int(request.args.get('item_id')) + database_config = config() + site_name = session['selected_site'] + zones = [] + offset = (page - 1) * limit + payload = (item_id, limit, offset) + count = 0 + with psycopg2.connect(**database_config) as conn: + zones, count = postsqldb.ZonesTable.paginateZonesBySku(conn, site_name, payload) + print(zones, count) + return jsonify(zones=zones, endpage=math.ceil(count/limit)) + +@items_api.route('/item/getLocationsBySkuZone', methods=['get']) +def getLocationsBySkuZone(): + zone_id = int(request.args.get('zone_id', 1)) + part_id = int(request.args.get('part_id', 1)) + page = int(request.args.get('page', 1)) + limit = int(request.args.get('limit', 1)) + + offset = (page-1)*limit + database_config = config() + site_name = session['selected_site'] + locations = [] + count=0 + with psycopg2.connect(**database_config) as conn: + payload = (part_id, zone_id, limit, offset) + locations, count = postsqldb.LocationsTable.paginateLocationsBySkuZone(conn, site_name, payload) + return jsonify(locations=locations, endpage=math.ceil(count/limit)) + + +@items_api.route('/item/getLocations', methods=['get']) +def getLocationsByZone(): + zone_id = int(request.args.get('zone_id', 1)) + part_id = int(request.args.get('part_id', 1)) + page = int(request.args.get('page', 1)) + limit = int(request.args.get('limit', 1)) + + offset = (page-1)*limit + database_config = config() + site_name = session['selected_site'] + locations = [] + count=0 + with psycopg2.connect(**database_config) as conn: + sql = f"SELECT * FROM {site_name}_locations WHERE zone_id=%s LIMIT %s OFFSET %s;" + locations = database.queryTuples(conn, sql, (zone_id, limit, offset), convert=True) + sql = f"SELECT COUNT(*) FROM {site_name}_locations WHERE zone_id=%s;" + count = database.queryTuple(conn, sql, (zone_id, )) + return jsonify(locations=locations, endpage=math.ceil(count[0]/limit)) + +@items_api.route('/item/getBrands', methods=['GET']) +def getBrands(): + page = int(request.args.get('page', 1)) + limit = int(request.args.get('limit', 1)) + offset = (page-1)*limit + database_config = config() + site_name = session['selected_site'] + brands = [] + count = 0 + with psycopg2.connect(**database_config) as conn: + brands, count = database._paginateTableTuples(conn, site_name, f"{site_name}_brands", (limit, offset), convert=True) + return jsonify(brands=brands, endpage=math.ceil(count['count']/limit)) + +@items_api.route('/item/updateItem', methods=['POST']) +def updateItem(): + if request.method == "POST": + id = request.get_json()['id'] + data = request.get_json()['data'] + + database_config = config() + site_name = session['selected_site'] + + transaction_data = {} + for key in data.keys(): + for key_2 in data[key].keys(): + transaction_data[f"{key_2}_new"] = data[key][key_2] + + with psycopg2.connect(**database_config) as conn: + item = database.getItemAllByID(conn, site_name, (id, ), convert=True) + if 'item_info' in data.keys() and data['item_info'] != {}: + for key in data['item_info'].keys(): + transaction_data[f"{key}_old"] = item['item_info'][key] + item_info_id = item['item_info_id'] + item_info = database.__updateTuple(conn, site_name, f"{site_name}_item_info", {'id': item_info_id, 'update': data['item_info']}, convert=True) + if 'food_info' in data.keys() and data['food_info'] != {}: + for key in data['food_info'].keys(): + transaction_data[f"{key}_old"] = item['food_info'][key] + food_info_id = item['food_info_id'] + print(food_info_id, data['food_info']) + food_info = database.__updateTuple(conn, site_name, f"{site_name}_food_info", {'id': food_info_id, 'update': data['food_info']}, convert=True) + if 'logistics_info' in data.keys() and data['logistics_info'] != {}: + for key in data['logistics_info'].keys(): + transaction_data[f"{key}_old"] = item['logistics_info'][key] + logistics_info_id = item['logistics_info_id'] + print(logistics_info_id, data['logistics_info']) + logistics_info = database.__updateTuple(conn, site_name, f"{site_name}_logistics_info", {'id': logistics_info_id, 'update': data['logistics_info']}, convert=True) + if 'item' in data.keys() and data['item'] != {}: + for key in data['item'].keys(): + if key == "brand": + transaction_data[f"{key}_old"] = item['brand']['id'] + else: + transaction_data[f"{key}_old"] = item[key] + item = database.__updateTuple(conn, site_name, f"{site_name}_items", {'id': id, 'update': data['item']}, convert=True) + + trans = MyDataclasses.TransactionPayload( + timestamp=datetime.datetime.now(), + logistics_info_id=item['logistics_info_id'], + barcode=item['barcode'], + name=item['item_name'], + transaction_type="UPDATE", + quantity=0.0, + description="Item was updated!", + user_id=session['user_id'], + data=transaction_data + ) + database.insertTransactionsTuple(conn, site_name, trans.payload()) + + return jsonify(error=False, message="Item updated successfully!") + return jsonify(error=True, message="Unable to save, ERROR!") + +@items_api.route('/item/updateItemLink', methods=['POST']) +def updateItemLink(): + if request.method == "POST": + id = request.get_json()['id'] + conv_factor = request.get_json()['conv_factor'] + barcode = request.get_json()['barcode'] + old_conv_factor = request.get_json()['old_conv'] + + + database_config = config() + site_name = session['selected_site'] + user_id = session['user_id'] + transaction_time = datetime.datetime.now() + with psycopg2.connect(**database_config) as conn: + linkedItem = database.getItemAllByBarcode(conn, site_name, (barcode, ), convert=True) + + transaction = MyDataclasses.TransactionPayload( + timestamp=transaction_time, + logistics_info_id=linkedItem['logistics_info_id'], + barcode=barcode, + name=linkedItem['item_name'], + transaction_type='UPDATE', + quantity=0.0, + description='Link updated!', + user_id=user_id, + data={'new_conv_factor': conv_factor, 'old_conv_factor': old_conv_factor} + ) + + database.__updateTuple(conn, site_name, f"{site_name}_itemlinks", {'id': id, 'update': {'conv_factor': conv_factor}}) + database.insertTransactionsTuple(conn, site_name, transaction.payload()) + return jsonify(error=False, message="Linked Item was updated successfully") + return jsonify(error=True, message="Unable to save this change, ERROR!") + + +@items_api.route('/item/getPossibleLocations', methods=["GET"]) +@login_required +def getPossibleLocations(): + if request.method == "GET": + page = int(request.args.get('page', 1)) + limit = int(request.args.get('limit', 1)) + offset = (page-1)*limit + database_config = config() + site_name = session['selected_site'] + with psycopg2.connect(**database_config) as conn: + locations, count = postsqldb.LocationsTable.paginateLocationsWithZone(conn, site_name, (limit, offset)) + return jsonify(locations=locations, end=math.ceil(count/limit)) + +@items_api.route('/item/getLinkedItem', methods=["GET"]) +@login_required +def getLinkedItem(): + linked_item = {} + if request.method == "GET": + id = int(request.args.get('id', 1)) + database_config = config() + site_name = session['selected_site'] + with psycopg2.connect(**database_config) as conn: + linked_item = database.__selectTuple(conn, site_name, f"{site_name}_itemlinks", (id, ), convert=True) + return jsonify({'linked_item': linked_item, 'error': False, 'message': 'Linked Item added!!'}) + return jsonify({'linked_item': linked_item, 'error': True, 'message': 'These was an error with adding to the linked list!'}) + +@items_api.route('/item/addLinkedItem', methods=["POST"]) +def addLinkedItem(): + if request.method == "POST": + parent_id = request.get_json()['parent_id'] + child_id = request.get_json()['child_id'] + conv_factor = request.get_json()['conv_factor'] + + database_config = config() + site_name = session['selected_site'] + user_id = session['user_id'] + with psycopg2.connect(**database_config) as conn: + print(parent_id, child_id, conv_factor) + parent_item = database.getItemAllByID(conn, site_name, (parent_id, ), convert=True) + child_item = database.getItemAllByID(conn, site_name, (child_id, ), convert=True) + + # i need to transact out ALL locations for child item. + pprint.pprint(child_item) + sum_child_qoh = 0 + for location in child_item['item_locations']: + print(location) + sum_child_qoh += location['quantity_on_hand'] + payload = { + 'item_id': child_item['id'], + 'logistics_info_id': child_item['logistics_info_id'], + 'barcode': child_item['barcode'], + 'item_name': child_item['item_name'], + 'transaction_type': 'Adjust Out', + 'quantity': location['quantity_on_hand'], + 'description': f'Converted to {parent_item['barcode']}', + 'cost': child_item['item_info']['cost'], + 'vendor': 1, + 'expires': False, + 'location_id': location['location_id'] + } + process.postTransaction(conn, site_name, user_id, payload) + + print(sum_child_qoh) + + primary_location = database.selectItemLocationsTuple(conn, site_name, (parent_item['id'], parent_item['logistics_info']['primary_location']['id']), convert=True) + + + payload = { + 'item_id': parent_item['id'], + 'logistics_info_id': parent_item['logistics_info_id'], + 'barcode': parent_item['barcode'], + 'item_name': parent_item['item_name'], + 'transaction_type': 'Adjust In', + 'quantity': (float(sum_child_qoh)*float(conv_factor)), + 'description': f'Converted from {child_item['barcode']}', + 'cost': child_item['item_info']['cost'], + 'vendor': 1, + 'expires': None, + 'location_id': primary_location['location_id'] + } + + pprint.pprint(payload) + result = process.postTransaction(conn, site_name, user_id, payload) + + if result['error']: + return jsonify(result) + + itemLink = MyDataclasses.ItemLinkPayload( + barcode=child_item['barcode'], + link=parent_item['id'], + data=child_item, + conv_factor=conv_factor + ) + + database.insertItemLinksTuple(conn, site_name, itemLink.payload()) + + database.__updateTuple(conn, site_name, f"{site_name}_items", {'id': child_item['id'], 'update': {'row_type': 'link'}}) + + return jsonify({'error': False, 'message': 'Linked Item added!!'}) + return jsonify({'error': True, 'message': 'These was an error with adding to the linked list!'}) + +@items_api.route('/items/addBlankItem', methods=["POST"]) +def addBlankItem(): + if request.method == "POST": + data = { + 'barcode': request.get_json()['barcode'], + 'name': request.get_json()['name'], + 'subtype': request.get_json()['subtype'] + } + pprint.pprint(data) + database_config = config() + site_name = session['selected_site'] + user_id = session['user_id'] + try: + with psycopg2.connect(**database_config) as conn: + process.postNewBlankItem(conn, site_name, user_id, data) + except Exception as error: + conn.rollback() + return jsonify({'error': True, 'message': error}) + return jsonify({'error': False, 'message': 'Item added!!'}) + return jsonify({'error': True, 'message': 'These was an error with adding Item!'}) + +@items_api.route('/items/addSKUPrefix', methods=["POST"]) +def addSKUPrefix(): + if request.method == "POST": + database_config = config() + site_name = session['selected_site'] + try: + with psycopg2.connect(**database_config) as conn: + prefix = postsqldb.SKUPrefixTable.Payload( + request.get_json()['uuid'], + request.get_json()['name'], + request.get_json()['description'] + ) + postsqldb.SKUPrefixTable.insert_tuple(conn, site_name, prefix.payload()) + except Exception as error: + conn.rollback() + return jsonify({'error': True, 'message': error}) + return jsonify({'error': False, 'message': 'Prefix added!!'}) + return jsonify({'error': True, 'message': 'These was an error with adding this Prefix!'}) + +@items_api.route('/item/addConversion', methods=['POST']) +def addConversion(): + if request.method == "POST": + item_id = request.get_json()['parent_id'] + uom_id = request.get_json()['uom_id'] + conv_factor = request.get_json()['conv_factor'] + + database_config = config() + site_name = session['selected_site'] + with psycopg2.connect(**database_config) as conn: + conversion = postsqldb.ConversionsTable.Payload( + item_id, uom_id, conv_factor + ) + postsqldb.ConversionsTable.insert_tuple(conn, site_name, conversion.payload()) + + return jsonify(error=False, message="Conversion was added successfully") + return jsonify(error=True, message="Unable to save this conversion, ERROR!") + +@items_api.route('/item/deleteConversion', methods=['POST']) +def deleteConversion(): + if request.method == "POST": + conversion_id = request.get_json()['conversion_id'] + print(conversion_id) + database_config = config() + site_name = session['selected_site'] + with psycopg2.connect(**database_config) as conn: + postsqldb.ConversionsTable.delete_item_tuple(conn, site_name, (conversion_id,)) + + return jsonify(error=False, message="Conversion was deleted successfully") + return jsonify(error=True, message="Unable to delete this conversion, ERROR!") + +@items_api.route('/item/updateConversion', methods=['POST']) +def updateConversion(): + if request.method == "POST": + conversion_id = request.get_json()['conversion_id'] + update_dictionary = request.get_json()['update'] + + database_config = config() + site_name = session['selected_site'] + with psycopg2.connect(**database_config) as conn: + postsqldb.ConversionsTable.update_item_tuple(conn, site_name, {'id': conversion_id, 'update': update_dictionary}) + return jsonify(error=False, message="Conversion was updated successfully") + return jsonify(error=True, message="Unable to save this conversion, ERROR!") + +@items_api.route('/item/addPrefix', methods=['POST']) +def addPrefix(): + if request.method == "POST": + item_info_id = request.get_json()['parent_id'] + prefix_id = request.get_json()['prefix_id'] + print(item_info_id) + print(prefix_id) + database_config = config() + site_name = session['selected_site'] + with psycopg2.connect(**database_config) as conn: + prefixes = postsqldb.ItemInfoTable.select_tuple(conn, site_name, (item_info_id,))['prefixes'] + print(prefixes) + prefixes.append(prefix_id) + postsqldb.ItemInfoTable.update_tuple(conn, site_name, {'id': item_info_id, 'update':{'prefixes': prefixes}}) + return jsonify(error=False, message="Prefix was added successfully") + return jsonify(error=True, message="Unable to save this prefix, ERROR!") + +@items_api.route('/item/deletePrefix', methods=['POST']) +def deletePrefix(): + if request.method == "POST": + item_info_id = request.get_json()['item_info_id'] + prefix_id = request.get_json()['prefix_id'] + + database_config = config() + site_name = session['selected_site'] + with psycopg2.connect(**database_config) as conn: + prefixes = postsqldb.ItemInfoTable.select_tuple(conn, site_name, (item_info_id,))['prefixes'] + prefixes.remove(prefix_id) + postsqldb.ItemInfoTable.update_tuple(conn, site_name, {'id': item_info_id, 'update':{'prefixes': prefixes}}) + return jsonify(error=False, message="Prefix was deleted successfully") + return jsonify(error=True, message="Unable to delete this prefix, ERROR!") + +@items_api.route('/item/refreshSearchString', methods=['POST']) +def refreshSearchString(): + if request.method == "POST": + item_id = request.get_json()['item_id'] + + database_config = config() + site_name = session['selected_site'] + with psycopg2.connect(**database_config) as conn: + item = postsqldb.ItemTable.getItemAllByID(conn, site_name, (item_id,)) + parameters = [f"id::{item['id']}", f"barcode::{item['barcode']}", f"name::{item['item_name']}", f"brand::{item['brand']['name']}", + f"expires::{item['food_info']['expires']}", f"row_type::{item['row_type']}", f"item_type::{item['item_type']}"] + + for prefix in item['item_info']['prefixes']: + parameters.append(f"prefix::{prefix['name']}") + + search_string = "&&".join(parameters) + postsqldb.ItemTable.update_tuple(conn, site_name, {'id': item_id, 'update':{'search_string': search_string}}) + + return jsonify(error=False, message="Search String was updated successfully") + return jsonify(error=True, message="Unable to update this search string, ERROR!") + +@items_api.route('/item/postNewItemLocation', methods=['POST']) +def postNewItemLocation(): + if request.method == "POST": + item_id = request.get_json()['item_id'] + location_id = request.get_json()['location_id'] + database_config = config() + site_name = session['selected_site'] + with psycopg2.connect(**database_config) as conn: + item_location = postsqldb.ItemLocationsTable.Payload( + item_id, + location_id + ) + postsqldb.ItemLocationsTable.insert_tuple(conn, site_name, item_location.payload()) + return jsonify(error=False, message="Location was added successfully") + return jsonify(error=True, message="Unable to save this location, ERROR!") \ No newline at end of file diff --git a/database.log b/database.log index 1d1e97e..d718389 100644 --- a/database.log +++ b/database.log @@ -1844,4 +1844,34 @@ sql='SELECT *, (SELECT COALESCE(array_agg(row_to_json(g)), '{}') FROM main_recipe_items g WHERE rp_id = main_recipes.id) AS rp_items FROM main_recipes LIMIT %s OFFSET %s;') 2025-04-26 21:18:52.710178 --- ERROR --- DatabaseError(message='duplicate key value violates unique constraint "test_recipe_items_uuid_key"DETAIL: Key (uuid)=(%X0031BMH6V%) already exists.', payload=('%X0031BMH6V%', 3, 'sku', 'Torani Peppermint syrup', 1, 1.0, 2020, '{}'), - sql='INSERT INTO test_recipe_items(uuid, rp_id, item_type, item_name, uom, qty, item_id, links) VALUES (%s, %s, %s, %s, %s, %s, %s, %s) RETURNING *;') \ No newline at end of file + sql='INSERT INTO test_recipe_items(uuid, rp_id, item_type, item_name, uom, qty, item_id, links) VALUES (%s, %s, %s, %s, %s, %s, %s, %s) RETURNING *;') +2025-04-27 12:30:18.960407 --- ERROR --- DatabaseError(message=''int' object does not support indexing', + payload=(1, 50, 0), + sql='SELECT * FROM main_transactions WHERE logistics_info_id=%s LIMIT %s OFFSET %s;') +2025-04-27 12:31:44.319424 --- ERROR --- DatabaseError(message=''int' object does not support indexing', + payload=(1, 50, 0), + sql='SELECT * FROM main_transactions WHERE logistics_info_id=%s LIMIT %s OFFSET %s;') +2025-04-27 12:31:54.929695 --- ERROR --- DatabaseError(message=''int' object does not support indexing', + payload=(1, 50, 0), + sql='SELECT * FROM main_transactions WHERE logistics_info_id=%s LIMIT %s OFFSET %s;') +2025-04-27 12:33:02.937669 --- ERROR --- DatabaseError(message=''int' object does not support indexing', + payload=(1, 50, 0), + sql='SELECT * FROM main_transactions WHERE logistics_info_id=%s LIMIT %s OFFSET %s;') +2025-04-27 12:33:44.164898 --- ERROR --- DatabaseError(message=''int' object is not iterable', + payload=(1, 50, 0), + sql='SELECT * FROM main_transactions WHERE logistics_info_id=%s LIMIT %s OFFSET %s;') +2025-04-27 12:33:59.076867 --- ERROR --- DatabaseError(message=''int' object is not iterable', + payload=(1, 50, 0), + sql='SELECT * FROM main_transactions WHERE logistics_info_id=%s LIMIT %s OFFSET %s;') +2025-04-27 12:34:13.060975 --- ERROR --- DatabaseError(message=''int' object is not iterable', + payload=(1, 50, 0), + sql='SELECT * FROM main_transactions WHERE logistics_info_id=%s LIMIT %s OFFSET %s;') +2025-04-27 12:34:27.956278 --- ERROR --- DatabaseError(message=''int' object does not support indexing', + payload=(1, 50, 0), + sql='SELECT * FROM main_transactions WHERE logistics_info_id=%s LIMIT %s OFFSET %s;') +2025-04-27 12:34:37.123332 --- ERROR --- DatabaseError(message=''int' object does not support indexing', + payload=(1, 50, 0), + sql='SELECT * FROM main_transactions WHERE logistics_info_id=%s LIMIT %s OFFSET %s;') +2025-04-27 12:35:10.632770 --- ERROR --- DatabaseError(message=''int' object does not support indexing', + payload=(1, 50, 0), + sql='SELECT * FROM main_transactions WHERE logistics_info_id=%s LIMIT %s OFFSET %s;') \ No newline at end of file diff --git a/webserver.py b/webserver.py index a29e91c..b21de11 100644 --- a/webserver.py +++ b/webserver.py @@ -1,7 +1,7 @@ import celery.schedules from flask import Flask, render_template, session, request, redirect, jsonify from flask_assets import Environment, Bundle -import api, config, user_api, psycopg2, main, api_admin, item_API, receipts_API, shopping_list_API, group_api +import api, config, user_api, psycopg2, main, api_admin, receipts_API, shopping_list_API, group_api from user_api import login_required, update_session_user from external_API import external_api from workshop_api import workshop_api @@ -9,6 +9,7 @@ import database import postsqldb from webpush import trigger_push_notifications_for_subscriptions from application.recipes import recipes_api +from application.items import items_API from flasgger import Swagger @@ -26,7 +27,7 @@ app.secret_key = '11gs22h2h1a4h6ah8e413a45' app.register_blueprint(api.database_api) app.register_blueprint(user_api.login_app) app.register_blueprint(api_admin.admin_api) -app.register_blueprint(item_API.items_api) +app.register_blueprint(items_API.items_api) app.register_blueprint(external_api) app.register_blueprint(workshop_api) app.register_blueprint(receipts_API.receipt_api)