commit fae4a6fcf03e5c921f867a05452064c7221ec179 Author: Ashibaya Date: Wed Jun 9 16:25:48 2021 +0900 Last worked version diff --git a/app.py b/app.py new file mode 100644 index 0000000..899c14f --- /dev/null +++ b/app.py @@ -0,0 +1,167 @@ +from sqlalchemy.orm import sessionmaker +from dbmodels import DBEngine, FZ54, metadata, FZ54Details, Sell, PayloadTable, ErrorTable, DocTable, AtolTable +from atol import Atol +from basemodels import Error, SellBaseModel, DocModel +from time import sleep +from datetime import datetime +import json + + +def db_init(): + server = '172.27.31.195:1433' + user = 'sa' + password = '159357' + dbname = 'fz54' + db = DBEngine(server, dbname, user, password) + dbname = 'fz54_details' + dbinfo = DBEngine(server, dbname, user, password) + return db, dbinfo + + +def main(): + db_fz, db_fzdetails = db_init() + models = FZ54(db_fz) + metadata.create_all(db_fz.engine) + details = FZ54Details(db_fzdetails) + return models, details + + +def sell(fz, fzd): + res = Sell(fz, fzd) + dicts = res.make() + return dicts + + +def clear_dict(d): + if d is None: + return None + elif isinstance(d, list): + return list(filter(lambda x: x is not None, map(clear_dict, d))) + elif not isinstance(d, dict): + return d + else: + r = dict( + filter(lambda x: x[1] is not None, + map(lambda x: (x[0], clear_dict(x[1])), + d.items()))) + if not bool(r): + return None + return r + + +def connection_db(models): + Session = sessionmaker(autocommit=False, bind=models.db.engine) + return Session() + + +def sell_items(sell_base_models, models, token): + for sell_base_model in sell_base_models: + sbm = clear_dict(sell_base_model.dict()) + ext_id = sbm.get('external_id') + session1 = Atol(token) + sss = connection_db(models) + try: + sell = session1.set_sell(sbm) + sell['external_id'] = ext_id + err_sell = sell.pop('error') + doc_model = clear_dict(sell) + try: + atol = AtolTable(**doc_model) + sss.add(atol) + sss.commit() + except Exception as e: + sss.rollback() + except Exception: + error = err_sell + error['external_id'] = ext_id + err = ErrorTable(**error) + zz = connection_db(models) + try: + zz.add(err) + zz.commit() + except Exception as e: + zz.rollback() + finally: + zz.close() + finally: + sss.close() + sleep(2) + + +def get_receipt(uuid, ext_id, models, atol_session): + sell = atol_session.get_reciepts(uuid) + session = connection_db(models) + try: + + doc_dict = DocModel(**sell) + doc_model = doc_dict.dict() + error = doc_model.pop('error') + payload = doc_model.pop('payload') + if type(error) is dict: + try: + error['external_id'] = ext_id + err = ErrorTable(**error) + except Exception as e: + print(e) + if type(payload) is dict: + try: + payload['external_id'] = ext_id + pay = PayloadTable(**payload) + except Exception as e: + print(e) + doc = DocTable(**doc_model) + if type(payload) is dict: + session.add(doc) + session.add(pay) + session.commit() + elif type(error) is dict: + session.add(doc) + session.add(err) + session.commit() + print(doc_dict) + except Exception as e: + print(e) + print(doc_dict) + error = sell.get('error') + error['external_id'] = ext_id + err = ErrorTable(**error) + ss = session + try: + ss.add(err) + ss.commit() + except Exception as e: + print(e) + ss.rollback() + finally: + session.close() + + +def remove_tuple(lst): + return [item[0] for item in lst] + + +if __name__ == "__main__": + starttime = datetime.now() + models, details = main() + token = models.get_token() + sell_models = sell(models, details) + sell_base_models = SellBaseModel().parse(db_dict_list=sell_models) + inst_list_query = 'SELECT external_id FROM sell WHERE external_id NOT IN(\ + SELECT external_id FROM atol_receipt )\ + ORDER BY [timestamp]' + inst_list = connection_db(models).execute(inst_list_query).fetchall() + sell_base_models = [model for model in sell_base_models if model.dict().get( + 'external_id') in remove_tuple(inst_list)] + sell_items(sell_base_models, models, token.get('token')) + endfirsttime = datetime.now() + deltatime = starttime - endfirsttime + print(f'прошло {deltatime.seconds}') + receipt_list = connection_db(models).execute( + "SELECT external_id, uuid FROM atol_receipt WHERE external_id not in (select external_id FROM docs)") + for rec in list(receipt_list): + sleep(2) + ext_id, uuid = rec + get_receipt(uuid, ext_id, models, Atol(token.get('token'))) + donetime = datetime.now() + deltaendtime = starttime - donetime + print(f'прошло {deltaendtime.seconds}') diff --git a/atol.py b/atol.py new file mode 100644 index 0000000..6189d2b --- /dev/null +++ b/atol.py @@ -0,0 +1,51 @@ +import requests +import json + +#{'uuid': '0dc8ac69-74e3-4657-b590-d1efebc84594', 'status': 'wait', 'error': None, 'timestamp': '13.04.2021 14:43:07'} + + +class Atol: + def __init__(self, token): + self.token = token + # Вызовы функций + self.load_info() + # self.get_token() + + def load_info(self): + self.url = "https://online.atol.ru/possystem/v4/" + self.group_id = 'jkhsakha-ru_3289' + + def get_headers(self): + headers_dict = { + "Content-type": "application/json", + "charset": "utf-8", + "Token": self.token, } + return headers_dict + + def get_request(self, method, url, data): + if method == "post": + r = requests.post(self.url+url, data=json.dumps(data), + headers=self.get_headers()) + else: + r = requests.get(self.url+url, headers=self.get_headers()) + r.encoding = "utf-8" + return json.loads(r.text) + + def get_token(self): + d = {"login": self.login, "pass": self.password} + self.token, _, self.timestamp = self.get_request( + "post", "getToken", d).values() + print(self.token) + + def set_sell(self, reciept): + s = self.get_request('post', self.group_id+'/sell', reciept) + return s + + def get_reciepts(self, uuid): + r = self.get_request('get', self.group_id+'/' + 'report/'+uuid, None) + return r + + +if __name__ == "__main__": + session1 = Atol() + print(f"token = '{session1.token},\ntimestamp= '{session1.timestamp}'") diff --git a/basemodels.py b/basemodels.py new file mode 100644 index 0000000..8a532db --- /dev/null +++ b/basemodels.py @@ -0,0 +1,209 @@ +from typing import List, Optional, Dict +from pydantic import BaseModel, Field, confloat, constr + + +class SumNumberTwoFormat(BaseModel): + __root__: Optional[confloat(ge=0.0, le=100000000.0, multiple_of=0.01)] + + +class NumberPrice(BaseModel): + __root__: confloat(ge=0.0, le=42949673.0, multiple_of=0.01) + + +class PhoneNumber(BaseModel): + __root__: constr(regex=r'^([^\s\\]{0,17}|\+[^\s\\]{1,18})$') + + +class NumberTwoFormat(BaseModel): + __root__: confloat(ge=0.0, le=100000000.0, multiple_of=0.01) + + +class NumberThreeFormat(BaseModel): + __root__: confloat(ge=0.0, le=100000.0, multiple_of=0.001) + + +class Service(BaseModel): + callback_url: Optional[constr(max_length=256)] = None + + +class Warnings(BaseModel): + callback_url: str = None + + +class PayingAgent(BaseModel): + operation: Optional[str] = None + phones: Optional[List[PhoneNumber]] = None + + +class SupplierInfo(BaseModel): + phones: Optional[List[PhoneNumber]] = None + + +class ReceivePaymentsOperator(BaseModel): + phones: Optional[List[PhoneNumber]] = None + + +class MoneyTransferOperator(BaseModel): + phones: Optional[List[PhoneNumber]] = None + name: Optional[str] = None + address: Optional[str] = None + inn: Optional[constr(regex=r'(^[0-9]{10}$)|(^[0-9]{12}$)')] = None + + +class Company(BaseModel): + email: Optional[constr(max_length=64)] = None + sno: Optional[str] = None + inn: constr(max_length=12) + payment_address: constr(max_length=256) + + +class ClientItem(BaseModel): + email: constr(max_length=64) = None + phone: Optional[constr(max_length=64)] = None + + +class AdditionalUserProps(BaseModel): + name: constr(max_length=64) + value: constr(max_length=256) + + +class AgentInfo(BaseModel): + type: Optional[str] = None + paying_agent: Optional[PayingAgent] = None + receive_payments_operator: Optional[ReceivePaymentsOperator] = None + money_transfer_operator: Optional[MoneyTransferOperator] = None + + +class Error(BaseModel): + error_id: Optional[str] = None + code: int + text: str + type: Optional[str] = None + + +class Payload(BaseModel): + fiscal_receipt_number: int + shift_number: int + receipt_datetime: str + total: float + fn_number: str + ecr_registration_number: str + fiscal_document_number: int + fiscal_document_attribute: int + fns_site: str + + +class Vat(BaseModel): + type: Optional[str] = None + sum: Optional[SumNumberTwoFormat] = None + + +class CorrectionInfo(BaseModel): + type: str + base_date: str + base_number: str + + +class Payment(BaseModel): + type: int + sum: SumNumberTwoFormat + + +class Item(BaseModel): + name: str + price: NumberPrice + quantity: NumberThreeFormat + sum: SumNumberTwoFormat + measurement_unit: Optional[constr(max_length=16)] = None + payment_method: Optional[str] = None + payment_object: Optional[str] = None + nomenclature_code: Optional[str] = None + vat: Optional[Vat] = None + agent_info: Optional[AgentInfo] = None + supplier_info: Optional[SupplierInfo] = None + user_data: Optional[constr(max_length=64)] = None + excise: Optional[confloat(ge=0.0)] = None + country_code: Optional[constr( + regex=r'^[0-9]*$', min_length=1, max_length=3)] = None + declaration_number: Optional[constr(min_length=1, max_length=32)] = None + + +class Receipt(BaseModel): + client: ClientItem + company: Company + agent_info: Optional[AgentInfo] = None + supplier_info: Optional[SupplierInfo] = None + items: List[Item] = Field(..., min_items=1) + payments: List[Payment] = Field(..., max_items=10, min_items=1) + vats: Optional[List[Vat]] = Field(None, max_items=6, min_items=1) + total: NumberTwoFormat + additional_check_props: Optional[constr(max_length=16)] = None + cashier: Optional[constr(max_length=64)] = None + additional_user_props: Optional[AdditionalUserProps] = None + + +class Correction(BaseModel): + company: Company + cashier: Optional[constr(max_length=64)] = None + correction_info: CorrectionInfo + payments: List[int] = Field(..., max_items=10, min_items=1) + vats: List[Vat] = Field(..., max_items=6, min_items=1) + + +class DocModel(BaseModel): + uuid: str + timestamp: str + group_code: str + daemon_code: str + device_code: str + external_id: Optional[str] = None + callback_url: Optional[str] = None + status: Optional[str] = None + error: Error = None + warnings: Optional[Warnings] = None + payload: Payload = None + + +class SellModel(BaseModel): + external_id: constr(max_length=128) + receipt: Receipt + service: Optional[Service] = None + timestamp: str + + +class CorrectionModel(BaseModel): + timestamp: str + external_id: constr(max_length=128) + service: Optional[Service] = None + correction: Correction + + +class SellBaseModel(BaseModel): + + def parse(self, db_dict_list): + sell_list = [] + for db_dict in db_dict_list: + receipt = db_dict['reciept'] + sell = db_dict['sell'] + vats = db_dict['vats'] + payments = db_dict['payments'] + items = db_dict['items'] + company = db_dict['company'] + client = db_dict['client'] + items_model = [Item(**item) for item in items] + payments_model = [Payment(**payment) for payment in payments] + vats_model = [Vat(**vat) for vat in vats] + + receipt['client'] = ClientItem( + **client if client.get('phone') != None else {'email': 'test@test.ru'}) + receipt['vats'] = vats_model + receipt['items'] = items_model + receipt['payments'] = payments_model + receipt['company'] = Company(**company) + receipt_model = Receipt(**receipt) + sell['receipt'] = receipt_model + sell['timestamp'] = sell['timestamp'].strftime( + '%d.%m.%Y %H:%M:%S') + sellmodel = SellModel(**sell) + sell_list.append(sellmodel) + return sell_list diff --git a/dbmodels.py b/dbmodels.py new file mode 100644 index 0000000..1b3712d --- /dev/null +++ b/dbmodels.py @@ -0,0 +1,396 @@ +from requests.sessions import session +from basemodels import Company +from sqlalchemy.ext.declarative import declarative_base +from sqlalchemy import create_engine, Table, Column, engine +from sqlalchemy.sql.sqltypes import DateTime +from sqlalchemy.orm import sessionmaker +from sqlalchemy.types import BigInteger, Integer, String, Numeric +import datetime +import pyodbc + +Base = declarative_base() +metadata = Base.metadata + + +class DBEngine: + def __init__(self, server, dbname, user, password): + self.dbname = dbname + self.user = user + self.password = password + self.server = server + self.get_mssql_engine() + + def get_mssql_engine(self): + query = f'mssql+pyodbc://{self.user}:{self.password}@{self.server}/{self.dbname}?driver=SQL+Server' + self.engine = create_engine(query) + + +class DBModel(DBEngine): + def __init__(self, db: DBEngine, tablename, *args): + self.engine = db.engine + self.filter_args = args + self.table = self.set_table(tablename) + self.data = self.get_dict_with_columns() + + def set_table(self, tablename): + table = Table( + tablename, + metadata, + autoload_with=self.engine + ) + return table + + def result_proxy_to_Dict(self, results) -> dict: + d, a = {}, [] + for rowproxy in results: + # rowproxy.items() returns an array like [(key0, value0), (key1, value1)] + for column, value in rowproxy.items(): + # build up the dictionary + d = {**d, **{column: value}} + a.append(d) + return a + + def get_dict_with_columns(self): + filter_filter = self.filter_args if len(self.filter_args) > 0 else True + table_values = self.engine.execute( + self.table.select().where(filter_filter)) + return self.result_proxy_to_Dict(table_values) + + +class FZ54: + def __init__(self, db: DBEngine): + self.db = db + self.datas = {} + self.dicts = {} + self.get_datas() + + def get_datas(self): + tables = { + 'agents': ('id_agent', 'all'), + 'agents_to_kvar': ('kod_postav', 'id_agent'), + 'company_sno': ('id', 'name'), + 'operation': ('id', 'name'), + 'operator': ('id_operator', 'name'), + 'payment_method': ('id', 'name'), + 'payment_object': ('id', 'name'), + 'payment_object_vat_type': ('id', 'name'), + 'payment_type': ('id', 'name'), + 'providers': ('id_provider', 'all'), + 'providers_to_kvar': ('kod_postav', 'id_provider'), + 'services': ('id_service', 'all'), + 'units': ('id_unit', 'sname'), + } + self.datas = {tablename: DBModel( + self.db, tablename).data for tablename in tables.keys()} + self.dicts = { + table[0]: self.make_dict(table) + for table in tables.items() + } + + def get_token(self): + ss = self.db.engine.execute("SELECT token FROM dbo.vAtolToken") + return {'token': list(ss)[0][0]} + + def make_dict(self, dic): + tablename, values = dic + key, value = values + return {row.get(key): row if value == 'all' else row.get(value) + for row in self.datas.get(tablename)} + + +class FZ54Details: + def __init__(self, db: DBEngine): + self.db = db + self.datas = {} + self.get_datas() + + def get_datas(self): + tables = ['payment_details'] + self.datas = {tablename: DBModel( + self.db, tablename).data for tablename in tables} + + def get_group(self, ids): + dd = {} + for id in ids: + ext_id = id.get('external_id') + values = dd.get(ext_id) + if values is None: + res_val = [] + else: + res_val = values + res_val.append(id) + dd[ext_id] = res_val + return dd + + +class RecieptTable(Base): + __table__ = Table( + 'reciepts', + metadata, + Column('external_id', String(length=128), primary_key=True), + Column('company', Integer), + Column('agent_info', Integer), + Column('supplier_info', Integer), + Column('items', String(length=128)), + Column('payments', String(length=128)), + Column('vats', String(length=128)), + Column('total', Numeric(12, 2)), + Column('additional_check_props', String), + Column('cashier', Integer), + Column('additional_user_props', Integer) + ) + + +class ItemsTable(Base): + __table__ = Table( + 'items', + metadata, + Column('id', BigInteger, primary_key=True), + Column('external_id', String(length=128)), + Column('name', String(length=255)), + Column('price', Numeric(12, 2)), + Column('quantity', Numeric(10, 2)), + Column('sum', Numeric(12, 2)), + Column('measurement_unit', String(length=16)), + Column('payment_method', String(length=10)), + Column('payment_object', String(length=10)), + Column('nomenclature_code', String(length=150)), + Column('vat', String(length=10)), + Column('agent_info', Integer), + Column('supplier_info', Integer), + Column('user_data', String(length=64)), + Column('excise', Numeric(10, 2)), + Column('country_code', String(length=3)), + Column('declaration_number', String(length=32)) + ) + + +class PaymentTable(Base): + __table__ = Table( + 'payments', + metadata, + Column('id', BigInteger, primary_key=True), + Column('external_id', String(length=128)), + Column('type', Integer), + Column('sum', Numeric(12, 2)), + ) + + +class CorrectionInfoTable(Base): + __table__ = Table( + 'correction_info', + metadata, + Column('external_id', String(length=128), primary_key=True), + Column('type', String(length=10)), + Column('base_date', String(length=128)), + Column('base_number', String(length=128)), + ) + + +class VatTable(Base): + __table__ = Table( + 'vats', + metadata, + Column('id', BigInteger, primary_key=True), + Column('external_id', String(length=128)), + Column('type', String(length=10)), + Column('sum', Numeric(12, 2)), + ) + + +class ErrorTable(Base): + __table__ = Table( + 'errors', + metadata, + Column('external_id', String(length=128), primary_key=True), + Column('error_id', String(length=128)), + Column('code', Integer), + Column('text', String(length=250)), + Column('type', String(length=10)), + ) + + +class AgentInfoTable(Base): + __table__ = Table( + 'agent_info', + metadata, + Column('external_id', String(length=128), primary_key=True), + Column('type', String(length=10)), + Column('paying_agent', Integer), + Column('recieve_payments_operator', Integer), + Column('money_tranfer_operator', Integer), + ) + + +class DocTable(Base): + __table__ = Table( + 'docs', + metadata, + Column('uuid', String(length=128), primary_key=True), + Column('timestamp', String(length=128)), + Column('group_code', String(length=128)), + Column('daemon_code', String(length=128)), + Column('device_code', String(length=128)), + Column('external_id', String(length=128)), + Column('callback_url', String(length=128)), + Column('status', String(length=128)), + Column('warnings', String(length=128)), + ) + + +class AtolTable(Base): + __table__ = Table( + 'atol_receipt', + metadata, + Column('uuid', String(length=128), primary_key=True), + Column('timestamp', String(length=128)), + Column('external_id', String(length=128)), + Column('status', String(length=128)), + ) + + +class PayloadTable(Base): + __table__ = Table( + 'payloads', + metadata, + Column('external_id', String(128), primary_key=True), + Column('fiscal_receipt_number', BigInteger), + Column('shift_number', BigInteger), + Column('receipt_datetime', String(length=128)), + Column('total', Numeric(12, 2)), + Column('fn_number', String(length=128)), + Column('ecr_registration_number', String(length=128)), + Column('fiscal_document_number', BigInteger), + Column('fiscal_document_attribute', BigInteger), + Column('fns_site', String(length=128)), + ) + + +class SellTable(Base): + __table__ = Table( + 'sell', + metadata, + Column('external_id', String(length=128), primary_key=True), + Column('service', String(length=16)), + Column('reciept', String(length=128)), + Column('timestamp', DateTime) + ) + + +class Sell: + def __init__(self, FZ54, FZ54Details): + self.fz = FZ54 + self.fzd = FZ54Details + self.Session = sessionmaker(bind=self.fz.db.engine) + + def make(self): + ids = self.fzd.datas['payment_details'] + ids_filtered = [id for id in ids if id['month'] == 3] + dd = self.fzd.get_group(ids_filtered) + timestamp = datetime.datetime(2021, 3, 10, 0, 0, 0) + res = [] + for ext_id, value in dd.items(): + ext_id = ext_id.lower() + val = value[0] + total = 0.0 + timestamp = timestamp + datetime.timedelta(seconds=1) + vats = {} + payments = {} + val_items = [] + + for i in value: + + it = self.fz.dicts['services'].get(i.get('id_item')) + summa = i['summa'] + vat = i['vat'] + method = i['payment_method'] + total += summa + if vat > 1: + summ_vat = vats.get( + self.fz.dicts['payment_object_vat_type'].get(vat)) + vats[self.fz.dicts['payment_object_vat_type'].get( + vat)] = summ_vat + round(summa*0.2, 2) if type(summ_vat) is float else 0.0 + round(summa*0.2, 2) + + if method in (5, 6, 7): + summ_pay3 = payments.get(3) + payments[3] = summ_pay3 + \ + summa if type(summ_pay3) is float else 0.0 + summa + if method in (1, 2, 3): + summ_pay2 = payments.get(2) + payments[2] = summ_pay2 + \ + summa if type(summ_pay2) is float else 0.0 + summa + if method in (4, 5): + summ_pay1 = payments.get(1) + payments[1] = summ_pay1 + \ + summa if type(summ_pay1) is float else 0.0 + summa + + item = ItemsTable(**{ + 'external_id': ext_id, + 'name': it.get('sname'), + 'price': i['price'], + 'quantity': i['quantity'], + 'sum': summa, + 'measurement_unit': self.fz.dicts['units'].get(it['id_unit']), + 'payment_method': self.fz.dicts['payment_method'].get(method), + 'payment_object': self.fz.dicts['payment_object'].get(i['payment_object']), + 'vat': self.fz.dicts['payment_object_vat_type'].get(vat), + 'agent_info': i['agent_type'], + 'supplier_info': None if i['agent_type'] == None else i['supplier_info'] + + }) + val_items.append(item) + + vts = [VatTable(**{'external_id': ext_id, 'type': key, 'sum': round(value, 2)}) + for key, value in vats.items()] + + pmnts = [PaymentTable(**{'external_id': ext_id, 'type': key, 'sum': round(value, 2)}) + for key, value in payments.items()] + sell = SellTable(**{'external_id': ext_id, 'service': None, + 'reciept': ext_id, 'timestamp': timestamp}) + + comp = self.fz.dicts['providers'].get(val['id_company']) + company = {} + company['inn'] = comp.get('inn') + company['email'] = 'ocnkp@jkhsakha.ru' + company['payment_address'] = 'http://jkhsakha.ru/' + company['sno'] = 'osn' + phone = val.get('phone') + reciept = RecieptTable(** + { + 'external_id': ext_id, + 'company': val['id_company'], + 'agent_info': val['agent_type'], + 'supplier_info': None if val['agent_type'] == None else val['supplier_info'], + 'items': ext_id, + 'payments': ext_id, + 'vats': ext_id, + 'total': round(total, 2) + }) + """ + session = self.Session() + for vt in vts: + session.add(vt) + for vali in val_items: + session.add(vali) + for pmnt in pmnts: + session.add(pmnt) + session.add(sell) + session.add(reciept) + session.commit() + """ + mod_val_item = [] + for val_item in val_items: + dict_val = val_item.__dict__ + dict_val['vat'] = {'type': dict_val.get('vat')} + mod_val_item.append(dict_val) + row_dict = { + 'reciept': reciept.__dict__, + 'sell': sell.__dict__, + 'vats': [vt.__dict__ for vt in vts], + 'payments': [pmnt.__dict__ for pmnt in pmnts], + 'items': mod_val_item, + 'company': company, + 'client': {'phone': phone} + } + res.append(row_dict) + return res