diff --git a/src/OpenIntranet/handlers/__init__.py b/src/OpenIntranet/handlers/__init__.py index 576da76f..adf4da0d 100644 --- a/src/OpenIntranet/handlers/__init__.py +++ b/src/OpenIntranet/handlers/__init__.py @@ -138,6 +138,11 @@ def get_current_user(self): class BaseHandler(tornado.web.RequestHandler): def prepare(self): + + user_agent = self.request.headers["User-Agent"] + self.OIAPP = True if user_agent == 'OpenIntranetApp' else False + print("Iam running in app", self.OIAPP) + login = self.get_secure_cookie("user") if login: login = str(login, encoding="utf-8") diff --git a/src/OpenIntranet/plugins/__init__.py b/src/OpenIntranet/plugins/__init__.py index 47a28790..561b004e 100644 --- a/src/OpenIntranet/plugins/__init__.py +++ b/src/OpenIntranet/plugins/__init__.py @@ -55,17 +55,6 @@ def repl(f): return layer -''' -@parametrized -def perm_validator(fn, permissions = [], sudo=True): - print("Validace opravneni.....") - print(permissions, sudo, fn.__dict__) - print(fn) - - return fn -''' - - def save_file(db, original_filename): path = os.path.dirname(original_filename) file = os.path.basename(original_filename) @@ -116,6 +105,7 @@ def database_init(): def get_company_info(database): return database.intranet.find_one({"_id": "company_info"}) or {} + def get_default_warehouse(database): default = database.intranet.find_one({"_id": "default_warehouse"}) # TODO: umoznit uzivatelum mit vlastni vychozi sklad @@ -213,6 +203,13 @@ class BaseHandler(tornado.web.RequestHandler): role_module = [] def prepare(self): + + + user_agent = self.request.headers["User-Agent"] + self.in_app = True if 'Electron' in user_agent else False + print("Iam running in app", self.in_app) + + login = self.get_secure_cookie("user") if login: login = str(login, encoding="utf-8") @@ -256,6 +253,7 @@ def get_template_namespace(self): ns = super(BaseHandler, self).get_template_namespace() ns.update({ 'intranet_title': tornado.options.options.intranet_name, + 'in_app': self.in_app, }) return ns diff --git a/src/OpenIntranet/plugins/android_reader_test.py b/src/OpenIntranet/plugins/android_reader_test.py index de2f1703..bf25ede5 100644 --- a/src/OpenIntranet/plugins/android_reader_test.py +++ b/src/OpenIntranet/plugins/android_reader_test.py @@ -19,8 +19,8 @@ def get_plugin_handlers(): plugin_name = get_plugin_info()["name"] return [ - (r'/%s' % plugin_name, hand_bi_home), - (r'/%s/' % plugin_name, hand_bi_home), + (r'/{}'.format(plugin_name), hand_bi_home), + (r'/{}/'.format(plugin_name), hand_bi_home), ] @@ -30,7 +30,7 @@ def get_plugin_info(): "entrypoints": [ { "title": "Android čtečka", - "url": "/payment", + "url": "/android_barcode", "icon": "android", } ], @@ -40,7 +40,7 @@ def get_plugin_info(): class hand_bi_home(BaseHandler): def get(self, data=None): - roles = self.authorized(['andorid'], sudo=False) + roles = self.authorized(['andorid'], sudo=True) print(">>>>>", roles) self.render("android_barcode.home.hbs", title="UST intranet", parent=self) diff --git a/src/OpenIntranet/plugins/property_manager/__init__.py b/src/OpenIntranet/plugins/property_manager/__init__.py new file mode 100644 index 00000000..6357178e --- /dev/null +++ b/src/OpenIntranet/plugins/property_manager/__init__.py @@ -0,0 +1,24 @@ +from .backend.property_manager import * +#from .. import BaseHandler + + +def get_plugin_handlers(): + plugin_name = get_plugin_info()["name"] + + return [ + (r'/{}'.format(plugin_name), HomeHandler), + (r'/{}/'.format(plugin_name), HomeHandler), + ] + + +def get_plugin_info(): + return { + "name": "property_manager", + "entrypoints": [ + { + "title": "Property manager", + "url": "/property_manager", + "icon": "bi-pc-display", + } + ] + } diff --git a/src/OpenIntranet/plugins/property_manager/backend/__init__.py b/src/OpenIntranet/plugins/property_manager/backend/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/src/OpenIntranet/plugins/property_manager/backend/property_manager.py b/src/OpenIntranet/plugins/property_manager/backend/property_manager.py new file mode 100644 index 00000000..852df8d3 --- /dev/null +++ b/src/OpenIntranet/plugins/property_manager/backend/property_manager.py @@ -0,0 +1,40 @@ +#!/usr/bin/python3 +# -*- coding: utf-8 -*- +import json +from datetime import datetime, timedelta + +import bson.json_util +import tornado +import tornado.options +from bson import ObjectId +from dateutil.relativedelta import relativedelta +from tornado.web import HTTPError + +from plugins import BaseHandler, password_hash +from plugins import BaseHandlerOwnCloud +from plugins.helpers import database_user as udb +from plugins.helpers import str_ops +from plugins.helpers.contract_generation import generate_contract +from plugins.helpers.doc_keys import CONTRACT_DOC_KEYS +from plugins.helpers.emails import generate_validation_token, generate_validation_message, send_email +from plugins.helpers.exceptions import BadInputHTTPError, MissingInfoHTTPError, ForbiddenHTTPError +from plugins.helpers.mdoc_ops import find_type_in_addresses, update_workspans_contract_id +from plugins.helpers.owncloud_utils import get_file_url, generate_contracts_directory_path, \ + generate_documents_directory_path +from plugins.users.backend.helpers.api import ApiJSONEncoder + + + +""" +Role: +základní uživatel nemá speciální roli jelikož všichni jsou základními uživateli +users-proprety_manager - spravce majetku +users-sudo - Admin +""" + +ROLE_SUDO = "users-sudo" + +class HomeHandler(BaseHandler): + def get(self): + + self.render("../plugins/property_manager/frontend/property_manager.overview.hbs") diff --git a/src/OpenIntranet/plugins/property_manager/frontend/property_manager.overview.hbs b/src/OpenIntranet/plugins/property_manager/frontend/property_manager.overview.hbs new file mode 100644 index 00000000..2f4f62ce --- /dev/null +++ b/src/OpenIntranet/plugins/property_manager/frontend/property_manager.overview.hbs @@ -0,0 +1,68 @@ +{% extends "base.hbs" %} +{% block title %} | Produkce, uvod{% end %} +{% block body %} + +{% import datetime %} + + + +
+ +
+ + + +
+
+ +
+
Seznam majetku
+ +
+ +
+ +
+ +
+ +
+ + +
+
+ + + +{% end %} diff --git a/src/OpenIntranet/plugins/store/data_import.py b/src/OpenIntranet/plugins/store/data_import.py index a4d16f81..28250b95 100644 --- a/src/OpenIntranet/plugins/store/data_import.py +++ b/src/OpenIntranet/plugins/store/data_import.py @@ -51,10 +51,13 @@ def api_call(action, params, token, app_secret, show_header=False): return json.loads(html); -def mouser_api_call(action, mouser_key, body): +def mouser_api_call(action, mouser_key, body = {}, method = "POST", debug = True): api_url = "https://api.mouser.com/api/v1/" + action + "?apiKey=" + mouser_key - response = request.Request(url=api_url, data=bytes(json.dumps(body), encoding='utf8'), headers={'Content-Type': 'application/json'}, method='POST' ) - response = json.loads(request.urlopen(response).read())['SearchResults']['Parts'][0] + print("Mouser api call", api_url) + print(body) + response = request.Request(url=api_url, data=bytes(json.dumps(body), encoding='utf8'), headers={'Content-Type': 'application/json'}, method=method ) + response = json.loads(request.urlopen(response).read()) + print(response) return response @@ -231,7 +234,7 @@ def get(self, component_id = None): } } - product = mouser_api_call('search/partnumber', key, body) + product = mouser_api_call('search/partnumber', key, body)['SearchResults']['Parts'][0] # self.write({'files': product_files['Data'], 'parameters': parameters['Data'], 'products': products}) self.render('store/store.api.importer.mouser.data.hbs',product=product) @@ -302,7 +305,7 @@ def get(self): } } - product = mouser_api_call('search/partnumber', key, body) + product = mouser_api_call('search/partnumber', key, body)['SearchResults']['Parts'][0] # self.write({'files': product_files['Data'], 'parameters': parameters['Data'], 'products': products}) self.render('store/store.api.importer.mouser.data.hbs',product=product) diff --git a/src/OpenIntranet/plugins/store/orders.py b/src/OpenIntranet/plugins/store/orders.py index 2cd782dd..f3d79669 100644 --- a/src/OpenIntranet/plugins/store/orders.py +++ b/src/OpenIntranet/plugins/store/orders.py @@ -11,11 +11,16 @@ import bson from bson import ObjectId +from .orders_helper import * + def get_plugin_handlers(): plugin_name = 'store' return [ (r'/{}/orders'.format(plugin_name), orders_home), + (r'/{}/order/(.*)/parameters/save'.format(plugin_name), order_parameters_save), + (r'/{}/order/(.*)/update_from_api'.format(plugin_name), order_update_from_api), + (r'/{}/order/(.*)'.format(plugin_name), order_edit), (r'/{}/orders/orderlist'.format(plugin_name), orders_get_orderlist), (r'/{}/orders/order_row'.format(plugin_name), orders_order_row), (r'/{}/orders/order_row/select_supplier'.format(plugin_name), orders_order_setsupplier), @@ -24,59 +29,118 @@ def get_plugin_handlers(): class orders_home(BaseHandler): - def get(self): - self.render("store/orders/orders.home.hbs") + def get(self): + self.render("store/orders/orders.home.hbs") class orders_get_orderlist(BaseHandler): - def get(self): + def get(self): + + wait_list = list(self.mdb.stock_operation.aggregate([ + {"$match": {"type": "order"}}, + {"$sort": {"cid": 1}}, + {"$lookup": {"from": "stock", "localField": "cid", "foreignField": "_id", "as": "component_info"}}, + {"$group": {'_id': "$supplier", "order_rows": {"$push": "$$ROOT"}}}, + {"$sort": {"_id": 1}} + ])) + + + order_list = list(self.mdb.orders.aggregate([ + + ])) + + + self.render("store/orders/orders.orderlist.hbs", order_list=order_list, wait_list=wait_list, + OrderStatusText=OrderStatusText) + + +class order_edit(BaseHandler): + def get(self, order_id): + if order_id == 'new': + order_id = create_empty_order(self.mdb) + self.redirect('/store/order/{}'.format(order_id)) + + order_id = bson.ObjectId(order_id) + if order_id: + self.write("OK..."+str(order_id)) + order_details = self.mdb.orders.aggregate([ + {"$match": {'_id': order_id}}, + ]) + self.render('store/orders/order.edit.hbs', order = list(order_details)[0]) - wait_list = list(self.mdb.stock_operation.aggregate([ - {"$match": {"type": "order"}}, - {"$sort": {"cid": 1}}, - {"$lookup": {"from": "stock", "localField": "cid", "foreignField": "_id", "as": "component_info"}}, - {"$group": {'_id': "$supplier", "order_rows": {"$push": "$$ROOT"}}}, - {"$sort": {"_id": 1}} - ])) +class order_parameters_save(BaseHandler): + def post(self, order_id): + order_id = bson.ObjectId(order_id) - self.render("store/orders/orders.orderlist.hbs", wait_list=wait_list) + supplier = self.get_argument('order_supplier', None) + identificator = self.get_argument('order_identificator', None) + self.mdb.orders.update_one({'_id': order_id}, {"$set":{'order_supplier': supplier, 'order_identificator': identificator}}) + self.write("ok") class orders_order_row(BaseHandler): - def get(self): - order_row = ObjectId(self.get_argument('order_row')) + def get(self): + order_row = ObjectId(self.get_argument('order_row')) - row = list(self.mdb.stock_operation.aggregate([ - {"$match": {"_id": order_row}}, - {"$lookup": {"from": "stock", "localField": "cid", "foreignField": "_id", "as": "component_info"}}, - ])) + row = list(self.mdb.stock_operation.aggregate([ + {"$match": {"_id": order_row}}, + {"$lookup": {"from": "stock", "localField": "cid", "foreignField": "_id", "as": "component_info"}}, + ])) - self.render("store/orders/orders.componet_view.hbs", row=row[0]) + self.render("store/orders/orders.componet_view.hbs", row=row[0]) class orders_order_setsupplier(BaseHandler): - def post(self): - order_row = ObjectId(self.get_argument('order_row')) - supplier_i = int(self.get_argument('supplier')) + def post(self): + order_row = ObjectId(self.get_argument('order_row')) + supplier_i = int(self.get_argument('supplier')) + + row = list(self.mdb.stock_operation.aggregate([ + {"$match": {"_id": order_row}}, + {"$lookup": {"from": "stock", "localField": "cid", "foreignField": "_id", "as": "component_info"}}, + {"$project": {"_id":1, "component_info.supplier":1, "cid": 1}} + ]))[0] + supplier = row['component_info'][0]['supplier'][supplier_i] + out = self.mdb.stock_operation.update_one({'_id': row['_id']}, {"$set": {"supplier_id": supplier_i, "supplier": supplier['supplier'], "supplier_symbol": supplier['symbol'] }}) + + self.write(str(out)) + + + + + +class order_update_from_api(BaseHandler): + def get(self, order_id): + order_id = bson.ObjectId(order_id) + api_data = order_update_data_from_api(self.mdb, order_id) + #api_data = json.loads(api_data) + + ## TODO check if currency is CZK (or preset currency) + + for i, item in enumerate(api_data.get('OrderLines', [])): + print(i, item) + + row_data = { + 'order_id': order_id, + 'supplier_symbol': item.get('MouserPartNumber', None), + 'manufacturer_symbol': item.get('MfrPartNumber', None), + 'count': item.get('Quantity', None), + 'unit_price': item.get('UnitPrice', None), + 'api': True + } - row = list(self.mdb.stock_operation.aggregate([ - {"$match": {"_id": order_row}}, - {"$lookup": {"from": "stock", "localField": "cid", "foreignField": "_id", "as": "component_info"}}, - {"$project": {"_id":1, "component_info.supplier":1, "cid": 1}} - ]))[0] - supplier = row['component_info'][0]['supplier'][supplier_i] - out = self.mdb.stock_operation.update_one({'_id': row['_id']}, {"$set": {"supplier_id": supplier_i, "supplier": supplier['supplier'], "supplier_symbol": supplier['symbol'] }}) + self.mdb.orders_items.insert_one(row_data) - self.write(str(out)) + self.write("OK") class orders_add_to_orderlist(BaseHandler): - def post(self): + def post(self): - cid = ObjectId(self.get_argument('cid')) - count = float(self.get_argument('count', 0)) - origin = self.get_argument('origin', None) - description = self.get_argument('description', None) + cid = ObjectId(self.get_argument('cid')) + count = float(self.get_argument('count', 0)) + origin = self.get_argument('origin', None) + description = self.get_argument('description', None) - add_component_to_orderlist(self.mdb, self.logged, count=count, cid=cid, description=description, warehouse=self.get_warehouse()['_id'], origin=origin) + add_component_to_orderlist(self.mdb, self.logged, count=count, cid=cid, description=description, warehouse=self.get_warehouse()['_id'], origin=origin) - self.write("OK") + self.write("OK") diff --git a/src/OpenIntranet/plugins/store/orders_helper.py b/src/OpenIntranet/plugins/store/orders_helper.py new file mode 100644 index 00000000..69b9ea1a --- /dev/null +++ b/src/OpenIntranet/plugins/store/orders_helper.py @@ -0,0 +1,59 @@ +from enum import Enum +import datetime +import bson +from .data_import import mouser_api_call +import json + + +class OrderStatus(Enum): + NEW = 1 + READY_TO_REVIEW = 2 + REVIEW_DONE = 3 + ORDER_ORDERED = 4 + ORDER_COMPLETE = 5 + +OrderStatusText = ["Neznámý stav", "Nová objednávka", "Připraveno ke kontrole", "Kontrola splněna", "Objednávka odeslána", "Objednávka dokončena"] + + +class OrderDirecton(Enum): + OUTCOMMING = 1 + INCOMMING = 2 + + +def create_empty_order(db): + order_id = bson.ObjectId() + content = { + "_id": order_id, + "order_status" : OrderStatus.NEW.value, + "order_supplier" : None, + "order_supplier_info" : "", + "total_price" : "", + "order_identificator": "", + "payments" : [], + "items" : [], + "delivery": [], + "documents": [], + "date_created" : datetime.datetime.now(), + "date_send" : None, + "direction": OrderDirecton.OUTCOMMING, + } + + db.orders.insert_one(content) + return order_id + + +def order_update_data_from_api(db, order_id): + print("UPDATE ORDER DATA FROM API...") + + order_data = dict(db.orders.find_one({'_id': order_id})) + print("ORDER DATA", order_data) + supplier = order_data.get('order_supplier', None) + + if supplier.lower() == 'mouser': + key = db.intranet_plugins.find_one({'_id': 'store'})['data']['data_import']['mouser_api_key_account'] + #data_import_info = self.mdb.intranet_plugins.find_one({'_id': 'store'})['data']['data_import'] + + body = {} + + order_info = mouser_api_call('order/{}'.format(order_data.get('order_identificator')), key, body, method="GET") + return order_info \ No newline at end of file diff --git a/src/OpenIntranet/static/bootstrap-icons b/src/OpenIntranet/static/bootstrap-icons index 25787cde..ec0e5863 160000 --- a/src/OpenIntranet/static/bootstrap-icons +++ b/src/OpenIntranet/static/bootstrap-icons @@ -1 +1 @@ -Subproject commit 25787cde29cb242261bc34714eda28c5350316a4 +Subproject commit ec0e5863162905fb35695e1c3612b6184ff476ce diff --git a/src/OpenIntranet/templates/android_barcode.home.hbs b/src/OpenIntranet/templates/android_barcode.home.hbs index 35b87588..4f911211 100644 --- a/src/OpenIntranet/templates/android_barcode.home.hbs +++ b/src/OpenIntranet/templates/android_barcode.home.hbs @@ -1,96 +1,65 @@ -{% extends "base.hbs" %} +{# {% extends "base.hbs" %} {% block title %}| TME catalog{%end%} -{% block body %} - - - - - - - -
- IP:
- - - - -
- -
- -
- - + - window.addEventListener("connect", init, false); + +
............
- - -{% end %}{# block body#} +{#{% end %}#}{# block body#} diff --git a/src/OpenIntranet/templates/base.hbs b/src/OpenIntranet/templates/base.hbs index aeb4a401..7a522bf8 100644 --- a/src/OpenIntranet/templates/base.hbs +++ b/src/OpenIntranet/templates/base.hbs @@ -120,7 +120,8 @@ {%block head %}{%end%} - + +{%if not in_app %} {% block top-nav %} -{% end %} +{% end %}{% end %}
{% block body %} No content {%end%} diff --git a/src/OpenIntranet/templates/store/orders/order.edit.hbs b/src/OpenIntranet/templates/store/orders/order.edit.hbs new file mode 100644 index 00000000..8b9b113a --- /dev/null +++ b/src/OpenIntranet/templates/store/orders/order.edit.hbs @@ -0,0 +1,227 @@ +{% extends "../../base.hbs" %} +{% block title %}UST intranet | Objednávky {%end%} +{% block body %} + +
+

Náhled objednávky

+ + {{order}} + +
+

+ Tady bude seznam zaklanich parametru o objednavce. Kdy byla vytvorena, kdo je dodavatelem, +

+

+ + Mají tady být asi zase 3 sloupce. Další sloupce mají zobrazovat informace o dodávkách součástek (tj. jestli jsou už dodané nebo ještě ne. A popřípadě mít možnost to rozdělit na samostatné dodávky). Pak část, co se týče účetnictví (kolik chtějí zaplatit, kolik se zaplatilo, jakým způsobem, ..., jaký je přeplatek/nedoplatek). Měla by to být tabulka, kde bude typ/název dokladu. Pak bude sloupec bilance, který bude ukazovat kterým směrem je požadavek. A na spodním řádku bude součet. Tj. něco jako "Doplatek". + + A pak bude samotný sloupec, který bude obsahovat soubory přiřazené k této objednávce. Tam se budou dávat objednávky, faktury, dodáky,... Popřípadě i nějaké strojové zpracoatelné soubory. +

+ +
+ +
+
+
+
Objednávka
+
+ +
+
+ + +
+ +
+ + + Označení objednávky dodavatele +
+ + + +
+ +
+
+
+ + +
+
+
Účetnictví
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Faktura 2020-10-10 - 1343 Czk Ne
Platba 2020-10-10 1343 Czk Ano (Fio)
Celková cena 1343 Czk
Placeno 1343 Czk
Bilance 0 Czk
+
+
+ +
+
+
Dokumenty
+ + + + + + + + + + + + + + + + + + + + + + +
Název dokumentuDatum vytvořeníTyp dokumentu
Objednávka2020-10-22pdf
Faktura2020-10-22pdf
Dodací list2020-10-22pdf
+ +
+
+
+
+ +

+ + Tady pak bude přehled položek v objednávce. + Bude zde název položky na faktuře. Pak bude sloupec, který říká, jestli je položka do skladu nebo ne (odkaz na součástku a pak na sáček, kam to bylo zařázené). Další slupec bude říkat počet objednaných komponent. Další sloupec bude obsahovat dodané počty ks. + + Pokud nedojde k dodání celé položky najednou, tak při zapsání menšího čísla se vytvoří další položka, která bude rozdílem požadovaného počtu. +

+ + + + + + + + + + + + + + + + + + + + + + + + + +
+ # + + Položka (název dodavatele) + + Celková cena (cena za ks) + + Skladová položka/sáček + + Objednaný počet (násobnost) + + Dodáno + + Stav +
+ 1 + + SPS30-suuo (Senzor SPS30) + + 2400 czk (24.0czk) + + Bez skladové položky + + 1000 ks (1x) + + 100 ks + + Dokončeno +
+ +
+ +
+ + + + + + + + + +{%end%} \ No newline at end of file diff --git a/src/OpenIntranet/templates/store/orders/orders.home.hbs b/src/OpenIntranet/templates/store/orders/orders.home.hbs index 4ab53113..f12a63fa 100644 --- a/src/OpenIntranet/templates/store/orders/orders.home.hbs +++ b/src/OpenIntranet/templates/store/orders/orders.home.hbs @@ -9,7 +9,6 @@

- +

Order list

-
-
- -
- -
-