Compare commits
27 Commits
main
...
75a71953f1
| Author | SHA1 | Date | |
|---|---|---|---|
| 75a71953f1 | |||
| 448ba351a7 | |||
| 474b9caf22 | |||
| 6683576a70 | |||
| 98a9a2cd61 | |||
| a5b6c572d2 | |||
| c5cd5ded3a | |||
| 4f325898f9 | |||
| 82c67434e2 | |||
| f0fce37a0c | |||
| 52f54d086f | |||
| 07735b5e2c | |||
| b5a3b3f1d1 | |||
| 3468d5e640 | |||
| 0b7a69c691 | |||
| 560a0248a5 | |||
| 7f567954fa | |||
| 21313f053b | |||
| 57bb4fd5e4 | |||
| cba9d37cdb | |||
| cc2d364059 | |||
| 85fc486e9a | |||
| f0b248eb6e | |||
| 9fe15529a8 | |||
| b7a4df1e30 | |||
| 4d98d631f7 | |||
| 1d2ee676ff |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -5,3 +5,5 @@ __pycache__/
|
||||
.git/
|
||||
.env
|
||||
test.*
|
||||
test/
|
||||
apks/
|
||||
9
app.py
9
app.py
@@ -23,7 +23,14 @@ server_session = Session(app)
|
||||
CORS(app, supports_credentials=True)
|
||||
jwt = JWTManager(app)
|
||||
blueprints = [
|
||||
(routes.auth_blueprint, '/auth')
|
||||
(routes.auth_blueprint, '/auth'),
|
||||
(routes.orders_blueprint, '/orders'),
|
||||
(routes.barcode_blueprint, '/barcode'),
|
||||
(routes.printing_blueprint, '/printing'),
|
||||
(routes.assembly_blueprint, '/assembly'),
|
||||
(routes.general_blueprint, '/general'),
|
||||
(routes.application_blueprint, '/application'),
|
||||
(routes.sipro_blueprint, '/sipro'),
|
||||
]
|
||||
|
||||
for blueprint, url_prefix in blueprints:
|
||||
|
||||
11
auxiliary.py
Normal file
11
auxiliary.py
Normal file
@@ -0,0 +1,11 @@
|
||||
def to_nested_dict(row):
|
||||
result = {}
|
||||
for key, value in row._mapping.items():
|
||||
keys = key.split('.')
|
||||
current_level = result
|
||||
for part in keys[:-1]:
|
||||
if part not in current_level:
|
||||
current_level[part] = {}
|
||||
current_level = current_level[part]
|
||||
current_level[keys[-1]] = value
|
||||
return result
|
||||
@@ -17,5 +17,5 @@ class FlaskConfig:
|
||||
SESSION_PERMANENT = False
|
||||
SESSION_USE_SIGNER = True
|
||||
SESSION_REDIS = redis.from_url('redis://127.0.0.1:6379')
|
||||
|
||||
JWT_ACCESS_TOKEN_EXPIRES = 86400
|
||||
# SQLALCHEMY_ECHO = True
|
||||
|
||||
13
constants.py
Normal file
13
constants.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from pathlib import Path
|
||||
import os
|
||||
import sys
|
||||
|
||||
APP_PATH = os.path.dirname(sys.executable) if getattr(sys, 'frozen', False) else os.path.dirname(__file__)
|
||||
|
||||
LOGGER_NAME = 'assemblr'
|
||||
LOG_FILE = Path(APP_PATH) / Path(f'{LOGGER_NAME}.log')
|
||||
MAX_LOG_FILE_SIZE_BYTES = 400 * 1024 ** 2
|
||||
APKS_PATH = os.path.join(APP_PATH, 'apks')
|
||||
|
||||
if not os.path.isdir(APKS_PATH):
|
||||
os.mkdir(APKS_PATH)
|
||||
10
database/enums.py
Normal file
10
database/enums.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from enum import unique, IntEnum
|
||||
|
||||
|
||||
@unique
|
||||
class AssemblyState(IntEnum):
|
||||
NOT_STARTED = 0,
|
||||
ASSEMBLING_PRODUCTS = 1,
|
||||
ALL_PRODUCTS_ASSEMBLED = 2,
|
||||
CONFIRMED = 3,
|
||||
ENDED = 4
|
||||
@@ -13,8 +13,32 @@ class User(db.Model):
|
||||
sipro_id = db.Column(db.Integer, nullable=True, comment='ID пользователя в SIPRO')
|
||||
|
||||
|
||||
class Assembly(db.Model):
|
||||
__tablename__ = 'assemblies'
|
||||
id = db.Column(db.Integer, primary_key=True, comment='ID сборки')
|
||||
|
||||
created_at = db.Column(db.DateTime, nullable=True, comment='Дата и время начала сборки')
|
||||
ended_at = db.Column(db.DateTime, nullable=True, comment='Дата и время конца сборки')
|
||||
|
||||
user_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
|
||||
user = db.relationship('User', backref='assemblies')
|
||||
|
||||
order_id = db.Column(db.Integer, nullable=False, comment='ID заказа в базе данных', index=True)
|
||||
is_active = db.Column(db.Boolean, nullable=False, comment='Активная ли сборка')
|
||||
state = db.Column(db.Integer, nullable=False, comment='Состояние сборки')
|
||||
|
||||
|
||||
class Barcode(db.Model):
|
||||
__tablename__ = 'barcodes'
|
||||
id = db.Column(db.Integer, primary_key=True, comment='ID пользователя')
|
||||
denco_article = db.Column(db.Integer, nullable=False, comment='Артикул', index=True)
|
||||
barcode = db.Column(db.String, nullable=False, comment='Баркод', index=True)
|
||||
|
||||
|
||||
class Application(db.Model):
|
||||
__tablename__ = 'applications'
|
||||
id = db.Column(db.Integer, primary_key=True, comment='')
|
||||
name = db.Column(db.String(40), nullable=False)
|
||||
version = db.Column(db.String(10), nullable=False)
|
||||
uploaded = db.Column(db.DateTime, nullable=False)
|
||||
filename = db.Column(db.String, nullable=False)
|
||||
|
||||
34
logger.py
Normal file
34
logger.py
Normal file
@@ -0,0 +1,34 @@
|
||||
from logging.handlers import RotatingFileHandler
|
||||
from singleton import Singleton
|
||||
|
||||
import logging
|
||||
import constants
|
||||
|
||||
|
||||
class Logger(metaclass=Singleton):
|
||||
def __init__(self):
|
||||
self.logger = logging.getLogger(constants.LOGGER_NAME)
|
||||
self.logger.setLevel(logging.DEBUG)
|
||||
|
||||
file_handler = RotatingFileHandler(constants.LOG_FILE,
|
||||
maxBytes=constants.MAX_LOG_FILE_SIZE_BYTES,
|
||||
encoding='UTF-8',
|
||||
backupCount=1)
|
||||
file_handler.setLevel(logging.DEBUG)
|
||||
|
||||
console_handler = logging.StreamHandler()
|
||||
console_handler.setLevel(logging.INFO)
|
||||
|
||||
formatter = logging.Formatter(fmt='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
|
||||
datefmt='%m-%d %H:%M')
|
||||
file_handler.setFormatter(formatter)
|
||||
console_handler.setFormatter(formatter)
|
||||
|
||||
self.logger.addHandler(file_handler)
|
||||
self.logger.addHandler(console_handler)
|
||||
|
||||
def get_logger(self):
|
||||
return self.logger
|
||||
|
||||
|
||||
logger_instance = Logger().get_logger()
|
||||
@@ -4,6 +4,7 @@ flask-session
|
||||
flask-cors
|
||||
flask-migrate
|
||||
Flask-JWT-Extended
|
||||
gunicorn
|
||||
|
||||
# Working with database
|
||||
redis
|
||||
|
||||
@@ -1 +1,8 @@
|
||||
from routes.auth import auth_blueprint
|
||||
from routes.orders import orders_blueprint
|
||||
from routes.barcode import barcode_blueprint
|
||||
from routes.printing import printing_blueprint
|
||||
from routes.assembly import assembly_blueprint
|
||||
from routes.general import general_blueprint
|
||||
from routes.application import application_blueprint
|
||||
from routes.sipro import sipro_blueprint
|
||||
|
||||
70
routes/application.py
Normal file
70
routes/application.py
Normal file
@@ -0,0 +1,70 @@
|
||||
import datetime
|
||||
import os.path
|
||||
import uuid
|
||||
|
||||
from flask import Blueprint, request, send_file
|
||||
from flask_jwt_extended import verify_jwt_in_request
|
||||
|
||||
import database
|
||||
from constants import APKS_PATH
|
||||
|
||||
application_blueprint = Blueprint('application', __name__)
|
||||
|
||||
|
||||
@application_blueprint.before_request
|
||||
def auth():
|
||||
API_KEY = 'AF9A20DD9264C134CDA0ADACED834368'
|
||||
if request.headers.get('Authorization') != API_KEY and (not verify_jwt_in_request(optional=True)):
|
||||
return {'error': 'Unauthorized'}, 401
|
||||
|
||||
|
||||
@application_blueprint.get('<string:application_name>/download/<string:version>')
|
||||
def download_version(application_name: str, version: str):
|
||||
application = database.Application.query.filter_by(version=version,
|
||||
name=application_name).first()
|
||||
if not application:
|
||||
return {
|
||||
"error": f"Application version '{version}' not found."
|
||||
}, 404
|
||||
|
||||
file_path = os.path.join(APKS_PATH, application.filename)
|
||||
version_safe = version.replace('.', '_')
|
||||
print(file_path)
|
||||
return send_file(file_path,
|
||||
download_name=f'{application_name}_{version_safe}.apk',
|
||||
as_attachment=True,
|
||||
mimetype='application/vnd.android.package-archive')
|
||||
|
||||
|
||||
@application_blueprint.get('<string:application_name>/version')
|
||||
def get_version(application_name: str):
|
||||
version = (database.Application.query.
|
||||
filter_by(name=application_name).
|
||||
order_by(database.Application.uploaded.desc()).
|
||||
with_entities(database.Application.version).
|
||||
limit(1).
|
||||
scalar())
|
||||
return {'latest_version': version}
|
||||
|
||||
|
||||
@application_blueprint.post('<string:application_name>/upload')
|
||||
def upload(application_name: str):
|
||||
file = request.files.get('file')
|
||||
version = request.form.get('version')
|
||||
if (not file) or (not version):
|
||||
return {"error": "Invalid form data. There is no file or version field"}, 400
|
||||
version = version.strip()
|
||||
application = (database.Application.query.
|
||||
filter_by(version=version).
|
||||
with_entities(database.Application.id).first())
|
||||
if application:
|
||||
return {"error": f"Specified version ({version}) already uploaded"}, 400
|
||||
filename = uuid.uuid4().hex + '.apk'
|
||||
file.save(os.path.join(APKS_PATH, filename))
|
||||
application = database.Application(name=application_name,
|
||||
version=version,
|
||||
uploaded=datetime.datetime.now(),
|
||||
filename=filename)
|
||||
database.db.session.add(application)
|
||||
database.db.session.commit()
|
||||
return {'filename': filename, 'version': version}
|
||||
181
routes/assembly.py
Normal file
181
routes/assembly.py
Normal file
@@ -0,0 +1,181 @@
|
||||
import datetime
|
||||
|
||||
from flask import Blueprint, request, jsonify
|
||||
from flask_jwt_extended import get_jwt_identity
|
||||
|
||||
import database
|
||||
from database.enums import AssemblyState
|
||||
from routes.utils import jwt_protect_blueprint
|
||||
import sipro.api.orders
|
||||
|
||||
assembly_blueprint = jwt_protect_blueprint(Blueprint('assembly', __name__))
|
||||
|
||||
|
||||
@assembly_blueprint.post('/create')
|
||||
def create_assembly():
|
||||
try:
|
||||
data: dict = request.json
|
||||
order_id: int = data.get('orderId')
|
||||
user_id = get_jwt_identity()
|
||||
existing_assembly = database.Assembly.query.filter_by(order_id=order_id).first()
|
||||
if existing_assembly:
|
||||
response = {
|
||||
'ok': False,
|
||||
'message': 'Сборка этого товара уже была запущена',
|
||||
'assemblyId': existing_assembly.id,
|
||||
'statusCode': 'ASSEMBLY_ALREADY_EXISTS'
|
||||
}
|
||||
return jsonify(response)
|
||||
active_assembly = database.Assembly.query.filter(database.Assembly.user_id == user_id,
|
||||
database.Assembly.is_active == True).first()
|
||||
if active_assembly:
|
||||
response = {
|
||||
'ok': False,
|
||||
'message': 'Вы не можете запустить сборку заказа, так как у вас уже есть активная сборка',
|
||||
'assemblyId': active_assembly.id,
|
||||
'statusCode': 'USER_ALREADY_HAS_ACTIVE_ASSEMBLY'
|
||||
}
|
||||
return jsonify(response)
|
||||
assembly = database.Assembly(user_id=user_id,
|
||||
order_id=order_id,
|
||||
state=AssemblyState.NOT_STARTED,
|
||||
created_at=datetime.datetime.now(),
|
||||
is_active=True)
|
||||
database.db.session.add(assembly)
|
||||
database.db.session.commit()
|
||||
response = {
|
||||
'ok': True,
|
||||
'message': 'Сборка успешно запущена!',
|
||||
'assemblyId': assembly.id,
|
||||
'statusCode': 'CREATED'
|
||||
}
|
||||
return jsonify(response)
|
||||
except Exception as e:
|
||||
response = {
|
||||
'ok': False,
|
||||
'message': f'Неизвестная ошибка: {e}',
|
||||
'assemblyId': -1,
|
||||
'statusCode': 'INVALID_EXCEPTION'
|
||||
}
|
||||
return jsonify(response)
|
||||
|
||||
|
||||
@assembly_blueprint.post('/close')
|
||||
def close_assembly():
|
||||
args = request.json
|
||||
assembly_id = args.get('assemblyId')
|
||||
if not assembly_id or (not isinstance(assembly_id, int)):
|
||||
response = {
|
||||
'ok': False,
|
||||
'message': 'Неверно указан ID сборки'
|
||||
}
|
||||
return jsonify(response)
|
||||
assembly = database.db.session.get(database.Assembly, assembly_id)
|
||||
if not assembly:
|
||||
response = {
|
||||
'ok': False,
|
||||
'message': 'Указанная сборка не найдена'
|
||||
}
|
||||
return jsonify(response)
|
||||
assembly.is_active = False
|
||||
assembly.ended_at = datetime.datetime.now()
|
||||
database.db.session.commit()
|
||||
order_id = assembly.order_id
|
||||
return sipro.api.orders.close_order(order_id)
|
||||
|
||||
|
||||
@assembly_blueprint.post('/cancel')
|
||||
def cancel_assembly():
|
||||
try:
|
||||
user_id = get_jwt_identity()
|
||||
assembly = database.Assembly.query.filter(database.Assembly.user_id == user_id,
|
||||
database.Assembly.is_active == True).first()
|
||||
if not assembly:
|
||||
response = {
|
||||
'ok': False,
|
||||
'message': 'У вас нет активных сборок'
|
||||
}
|
||||
return jsonify(response)
|
||||
database.db.session.delete(assembly)
|
||||
database.db.session.commit()
|
||||
response = {
|
||||
'ok': True,
|
||||
'message': 'Сборка успешно отменена!'
|
||||
}
|
||||
return jsonify(response)
|
||||
except Exception as e:
|
||||
response = {
|
||||
'ok': False,
|
||||
'message': f'Неизвестная ошибка: {e}'
|
||||
}
|
||||
return jsonify(response)
|
||||
|
||||
|
||||
@assembly_blueprint.get('/hasActive')
|
||||
def user_has_active_assembly():
|
||||
user_id = get_jwt_identity()
|
||||
assemblies_count = database.Assembly.query.filter(database.Assembly.user_id == user_id,
|
||||
database.Assembly.is_active == True).count()
|
||||
return jsonify(has=assemblies_count > 0)
|
||||
|
||||
|
||||
@assembly_blueprint.get('/getActive')
|
||||
def get_active_assembly():
|
||||
user_id = get_jwt_identity()
|
||||
assembly = database.Assembly.query.filter(database.Assembly.user_id == user_id,
|
||||
database.Assembly.is_active == True).first()
|
||||
response = {
|
||||
'databaseId': assembly.id,
|
||||
'createdAt': str(assembly.created_at),
|
||||
'endedAt': str(assembly.ended_at),
|
||||
'orderId': assembly.order_id,
|
||||
'isActive': assembly.is_active,
|
||||
'state': assembly.state
|
||||
}
|
||||
return jsonify(response)
|
||||
|
||||
|
||||
@assembly_blueprint.post('/confirm')
|
||||
def confirm_assembly():
|
||||
user_id = get_jwt_identity()
|
||||
args: dict = request.json
|
||||
assembly_id = args.get('assemblyId')
|
||||
if not assembly_id or (not isinstance(assembly_id, int)):
|
||||
response = {
|
||||
'ok': False,
|
||||
'message': 'ID сборки указан неверно'
|
||||
}
|
||||
return jsonify(response)
|
||||
assembly = database.db.session.get(database.Assembly, assembly_id)
|
||||
if not assembly:
|
||||
response = {
|
||||
'ok': False,
|
||||
'message': 'Неудалось найти указанную сборку'
|
||||
}
|
||||
return jsonify(response)
|
||||
if assembly.user_id != user_id:
|
||||
response = {
|
||||
'ok': False,
|
||||
'message': 'Вы не можете закрыть сборку чужого пользователя'
|
||||
}
|
||||
return jsonify(response)
|
||||
order_id = assembly.order_id
|
||||
return sipro.api.orders.ship_order(order_id)
|
||||
|
||||
|
||||
@assembly_blueprint.post('/updateState')
|
||||
def update_assembly_state():
|
||||
try:
|
||||
args = request.json
|
||||
state = args.get('state')
|
||||
assembly_id = args.get('assemblyId')
|
||||
rows_to_update = [{
|
||||
'id': assembly_id,
|
||||
'state': state
|
||||
}]
|
||||
database.db.session.bulk_update_mappings(database.Assembly, rows_to_update)
|
||||
database.db.session.commit()
|
||||
return jsonify(ok=True)
|
||||
except Exception as e:
|
||||
print('Error while updating')
|
||||
return jsonify(ok=False)
|
||||
@@ -1,5 +1,5 @@
|
||||
from flask import Blueprint, request, jsonify
|
||||
from flask_jwt_extended import create_access_token, jwt_required, get_jwt_identity
|
||||
from flask_jwt_extended import create_access_token
|
||||
from werkzeug.security import generate_password_hash, check_password_hash
|
||||
|
||||
from database import User, db
|
||||
@@ -12,7 +12,7 @@ def register_endpoint():
|
||||
data = request.json
|
||||
login = data.get('login')
|
||||
password = data.get('password')
|
||||
password_hash = generate_password_hash(password, method='sha256')
|
||||
password_hash = generate_password_hash(password)
|
||||
|
||||
new_user = User(login=login,
|
||||
password_hash=password_hash)
|
||||
@@ -29,16 +29,9 @@ def login_endpoint():
|
||||
login = data.get('login')
|
||||
user = User.query.filter_by(login=login).first()
|
||||
if not user:
|
||||
return jsonify(ok=False), 401
|
||||
return jsonify(ok=False, accessToken=''), 401
|
||||
password = data.get('password')
|
||||
if not check_password_hash(user.password_hash, password):
|
||||
return jsonify(ok=False), 401
|
||||
return jsonify(ok=False, accessToken=''), 401
|
||||
access_token = create_access_token(identity=user.id)
|
||||
return jsonify(access_token=access_token)
|
||||
|
||||
|
||||
@auth_blueprint.get('/protected')
|
||||
@jwt_required()
|
||||
def protected_endpoint():
|
||||
print(type(get_jwt_identity()))
|
||||
return 'test'
|
||||
return jsonify(ok=True, accessToken=access_token)
|
||||
|
||||
13
routes/barcode.py
Normal file
13
routes/barcode.py
Normal file
@@ -0,0 +1,13 @@
|
||||
from flask import Blueprint, jsonify, request
|
||||
from routes.utils import jwt_protect_blueprint
|
||||
import sipro.api.barcode
|
||||
|
||||
barcode_blueprint = jwt_protect_blueprint(Blueprint('barcode', __name__))
|
||||
|
||||
|
||||
@barcode_blueprint.get('/searchProducts')
|
||||
def search_product():
|
||||
args = request.args
|
||||
barcode = args.get('barcode')
|
||||
response = sipro.api.barcode.get_products_by_barcode(barcode)
|
||||
return response
|
||||
15
routes/general.py
Normal file
15
routes/general.py
Normal file
@@ -0,0 +1,15 @@
|
||||
from flask import Blueprint, jsonify, request
|
||||
from routes.utils import jwt_protect_blueprint
|
||||
import sipro.api.general
|
||||
|
||||
general_blueprint = jwt_protect_blueprint(Blueprint('general', __name__))
|
||||
|
||||
|
||||
@general_blueprint.get('/getShippingWarehouses')
|
||||
def get_shipping_warehouses():
|
||||
return sipro.api.general.get_shipping_warehouses()
|
||||
|
||||
|
||||
@general_blueprint.get('/getCities')
|
||||
def get_cities():
|
||||
return sipro.api.general.get_cities()
|
||||
42
routes/orders.py
Normal file
42
routes/orders.py
Normal file
@@ -0,0 +1,42 @@
|
||||
from flask import Blueprint, jsonify, request
|
||||
from routes.utils import jwt_protect_blueprint
|
||||
import sipro.api.orders
|
||||
|
||||
orders_blueprint = jwt_protect_blueprint(Blueprint('orders', __name__))
|
||||
|
||||
|
||||
@orders_blueprint.get('/<int:order_id>')
|
||||
def get_order(order_id: int):
|
||||
return jsonify(id=order_id)
|
||||
|
||||
|
||||
@orders_blueprint.get('/getOrders')
|
||||
def get_orders():
|
||||
args = request.args
|
||||
order_by = args.get('orderBy')
|
||||
desc = int(args.get('desc'))
|
||||
page = int(args.get('page'))
|
||||
shipment_date = args.get('shipmentDate')
|
||||
status = args.get('status')
|
||||
shipment_warehouse_id = int(args.get('shipmentWarehouseId'))
|
||||
response = sipro.api.orders.get_orders(order_by,
|
||||
desc,
|
||||
page,
|
||||
shipment_date,
|
||||
status,
|
||||
shipment_warehouse_id)
|
||||
return jsonify(response)
|
||||
|
||||
|
||||
@orders_blueprint.get('/getByProductId')
|
||||
def get_orders_by_supplier_product_id():
|
||||
params = dict(request.args)
|
||||
# product_id = args.get('productId')
|
||||
return sipro.api.orders.get_orders_from_barcode(params=params)
|
||||
|
||||
|
||||
@orders_blueprint.get('/getOrderById')
|
||||
def get_order_by_id():
|
||||
args = request.args
|
||||
order_id = args.get('orderId')
|
||||
return sipro.api.orders.get_order_by_id(order_id)
|
||||
17
routes/printing.py
Normal file
17
routes/printing.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from flask import Blueprint, request, send_file
|
||||
from routes.utils import jwt_protect_blueprint
|
||||
import sipro.api.printing
|
||||
|
||||
printing_blueprint = jwt_protect_blueprint(Blueprint('printing', __name__))
|
||||
|
||||
|
||||
@printing_blueprint.get('/getLabel')
|
||||
def get_label():
|
||||
args = request.args
|
||||
order_id = args.get('orderId')
|
||||
data = sipro.api.printing.get_label(order_id)
|
||||
data.seek(0)
|
||||
return send_file(data,
|
||||
as_attachment=True,
|
||||
download_name='label.pdf',
|
||||
mimetype='application/pdf')
|
||||
53
routes/sipro.py
Normal file
53
routes/sipro.py
Normal file
@@ -0,0 +1,53 @@
|
||||
from enum import StrEnum
|
||||
|
||||
from flask import Blueprint, request
|
||||
from sqlalchemy import func
|
||||
|
||||
import auxiliary
|
||||
import database
|
||||
|
||||
sipro_blueprint = Blueprint('sipro', __name__)
|
||||
|
||||
|
||||
class ExpandParam(StrEnum):
|
||||
USER = 'user'
|
||||
|
||||
|
||||
@sipro_blueprint.before_request
|
||||
def auth():
|
||||
API_KEY = '5D809ED08080B5F204443B31374BD6A5'
|
||||
if request.headers.get('Authorization') != API_KEY:
|
||||
return {'error': 'Unauthorized'}, 401
|
||||
|
||||
|
||||
@sipro_blueprint.post('/assemblyInfo')
|
||||
def assembly_info():
|
||||
params: dict = request.json
|
||||
expand_param = params.get('expand')
|
||||
order_ids = params.get('orderIds')
|
||||
expand_list = expand_param if expand_param else []
|
||||
datetime_format = 'YYYY-MM-DD, HH24:MI'
|
||||
entity_list = [
|
||||
database.Assembly.id.label('id'),
|
||||
database.Assembly.order_id.label('order_id'),
|
||||
func.to_char(database.Assembly.created_at, datetime_format).label('created_at'),
|
||||
func.to_char(database.Assembly.ended_at, datetime_format).label('ended_at'),
|
||||
database.Assembly.is_active.label('is_active'),
|
||||
database.Assembly.state.label('state')
|
||||
]
|
||||
query = database.Assembly.query
|
||||
for expand in expand_list:
|
||||
match expand:
|
||||
case ExpandParam.USER:
|
||||
query = query.join(database.User)
|
||||
entity_list.extend([
|
||||
database.User.id.label('user.id'),
|
||||
database.User.login.label('user.login'),
|
||||
])
|
||||
|
||||
if order_ids:
|
||||
query = query.filter(database.Assembly.order_id.in_(order_ids))
|
||||
query = query.with_entities(*entity_list)
|
||||
result = query.all()
|
||||
json_result = [auxiliary.to_nested_dict(row) for row in result]
|
||||
return json_result
|
||||
12
routes/utils.py
Normal file
12
routes/utils.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from functools import wraps
|
||||
|
||||
from flask import Blueprint
|
||||
from flask_jwt_extended import verify_jwt_in_request
|
||||
|
||||
|
||||
def jwt_protect_blueprint(blueprint) -> Blueprint:
|
||||
@blueprint.before_request
|
||||
def require_token():
|
||||
verify_jwt_in_request()
|
||||
|
||||
return blueprint
|
||||
@@ -19,3 +19,7 @@ MY_HOST = os.environ.get('MY_HOST')
|
||||
|
||||
# Flask settings
|
||||
SECRET_KEY = os.environ.get('SECRET_KEY')
|
||||
|
||||
# Sipro settings
|
||||
SIPRO_API_URL = os.environ.get('SIPRO_API_URL')
|
||||
SIPRO_API_TOKEN = os.environ.get('SIPRO_API_TOKEN')
|
||||
|
||||
7
singleton.py
Normal file
7
singleton.py
Normal file
@@ -0,0 +1,7 @@
|
||||
class Singleton(type):
|
||||
_instances = {}
|
||||
|
||||
def __call__(cls, *args, **kwargs):
|
||||
if cls not in cls._instances:
|
||||
cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
|
||||
return cls._instances[cls]
|
||||
1
sipro/__init__.py
Normal file
1
sipro/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
from sipro import api
|
||||
0
sipro/api/__init__.py
Normal file
0
sipro/api/__init__.py
Normal file
10
sipro/api/barcode.py
Normal file
10
sipro/api/barcode.py
Normal file
@@ -0,0 +1,10 @@
|
||||
from sipro.api.client import get_client
|
||||
|
||||
client = get_client()
|
||||
router = '/barcode'
|
||||
|
||||
|
||||
def get_products_by_barcode(barcode: str) -> list[dict]:
|
||||
method = f'{router}/getProducts?barcode={barcode}'
|
||||
response = client.method('GET', method)
|
||||
return response
|
||||
42
sipro/api/client.py
Normal file
42
sipro/api/client.py
Normal file
@@ -0,0 +1,42 @@
|
||||
import requests
|
||||
|
||||
import settings
|
||||
from logger import logger_instance
|
||||
|
||||
|
||||
class SiproClient:
|
||||
_instance = None
|
||||
|
||||
def __new__(cls, *args, **kwargs):
|
||||
if not cls._instance:
|
||||
cls._instance = super(SiproClient, cls).__new__(cls)
|
||||
return cls._instance
|
||||
|
||||
def __init__(self, api_url: str, token: str):
|
||||
if not hasattr(self, 'initialized'):
|
||||
self.api_url = api_url
|
||||
self.token = token
|
||||
self.initialized = True
|
||||
logger_instance.info('SiproClient successfully initialized')
|
||||
|
||||
def method(self, http_method: str, method: str, data: dict = None, raw=False, params: dict = None):
|
||||
url = self.api_url + '/assemblr' + method
|
||||
headers = {'Authorization': self.token}
|
||||
response = requests.request(http_method, url, headers=headers, json=data, params=params)
|
||||
if raw:
|
||||
return response
|
||||
return response.json()
|
||||
|
||||
def ping(self) -> str:
|
||||
return self.method('GET', '/ping').get('response')
|
||||
|
||||
|
||||
sipro_config = {
|
||||
'api_url': settings.SIPRO_API_URL,
|
||||
'token': settings.SIPRO_API_TOKEN,
|
||||
}
|
||||
sipro_client = SiproClient(**sipro_config)
|
||||
|
||||
|
||||
def get_client() -> SiproClient:
|
||||
return sipro_client
|
||||
12
sipro/api/general.py
Normal file
12
sipro/api/general.py
Normal file
@@ -0,0 +1,12 @@
|
||||
from sipro.api.client import get_client
|
||||
|
||||
client = get_client()
|
||||
router = '/general'
|
||||
|
||||
|
||||
def get_shipping_warehouses():
|
||||
return client.method('GET', f'{router}/getShippingWarehouses')
|
||||
|
||||
|
||||
def get_cities():
|
||||
return client.method('GET', f'{router}/getCities')
|
||||
42
sipro/api/orders.py
Normal file
42
sipro/api/orders.py
Normal file
@@ -0,0 +1,42 @@
|
||||
from sipro.api.client import get_client
|
||||
|
||||
client = get_client()
|
||||
router = '/orders'
|
||||
|
||||
|
||||
def get_orders_by_product_id(supplier_product_id: str) -> list[dict]:
|
||||
method = f'{router}/getByProductId?productId={supplier_product_id}'
|
||||
response = client.method('GET', method)
|
||||
return response
|
||||
|
||||
|
||||
def get_order_by_id(order_id) -> dict:
|
||||
method = f'{router}/getOrderById?orderId={order_id}'
|
||||
response = client.method('GET', method)
|
||||
return response
|
||||
|
||||
|
||||
def ship_order(order_id: int) -> dict:
|
||||
method = f'{router}/shipOrder'
|
||||
data = {'orderId': order_id}
|
||||
response = client.method('POST', method, data=data)
|
||||
return response
|
||||
|
||||
|
||||
def close_order(order_id: int) -> dict:
|
||||
method = f'{router}/closeOrder'
|
||||
data = {'orderId': order_id}
|
||||
response = client.method('POST', method, data=data)
|
||||
return response
|
||||
|
||||
|
||||
def get_orders(order_by: str, desc: int, page: int, shipment_date: str, status: str, shipment_warehouse_id: int):
|
||||
method = f'{router}/getOrders?orderBy={order_by}&desc={desc}&page={page}&shipmentDate={shipment_date}&status={status}&shipmentWarehouseId={shipment_warehouse_id}'
|
||||
response = client.method('GET', method)
|
||||
return response
|
||||
|
||||
|
||||
def get_orders_from_barcode(params: dict):
|
||||
method = f'{router}/getByProductId'
|
||||
response = client.method('GET', method, params=params)
|
||||
return response
|
||||
16
sipro/api/printing.py
Normal file
16
sipro/api/printing.py
Normal file
@@ -0,0 +1,16 @@
|
||||
from io import BytesIO
|
||||
|
||||
from flask import send_file
|
||||
from requests import Response
|
||||
|
||||
from sipro.api.client import get_client
|
||||
|
||||
client = get_client()
|
||||
router = '/printing'
|
||||
|
||||
|
||||
def get_label(order_product_id: str) -> BytesIO:
|
||||
method = f'{router}/getLabel?orderId={order_product_id}'
|
||||
response: Response = client.method('GET', method, raw=True)
|
||||
data = BytesIO(response.content)
|
||||
return data
|
||||
@@ -10,7 +10,6 @@ class BarcodesSynchronizer:
|
||||
existing_barcodes = database.Barcode.query.all()
|
||||
denco_articles = list(set([barcode.denco_article for barcode in existing_barcodes]))
|
||||
|
||||
|
||||
query_string = 'SELECT product, barcode FROM modx_0_connections WHERE barcode != "" LIMIT 100'
|
||||
for denco_article, barcodes_string in self.mariadb_connector.select(query_string):
|
||||
barcodes = barcodes_string.split(',')
|
||||
|
||||
Reference in New Issue
Block a user