# This file is part of Tryton. The COPYRIGHT file at the top level of # this repository contains the full copyright notices and license terms. from trytond.model import ModelSQL, ModelView, fields, sequence_ordered, ModelSingleton from trytond.pyson import Eval from trytond.transaction import Transaction from trytond.pool import Pool, PoolMeta from trytond.wizard import Button, StateTransition, StateView, Wizard, StateAction from decimal import getcontext, Decimal, ROUND_HALF_UP import jwt import datetime import logging from trytond.exceptions import UserWarning, UserError import json import shlex from datetime import date logger = logging.getLogger(__name__) keywords = ["duplicate", "create", "add", "invoice", "reception", "pay", "delivery", "match"] class DashboardLoader(Wizard): 'Load Dashboard' __name__ = 'purchase.dashboard.loader' start = StateAction('purchase_trade.act_dashboard_form') def do_start(self, action): pool = Pool() Dashboard = pool.get('purchase.dashboard') user_id = Transaction().user if user_id == 0: user_id = Transaction().context.get('user', user_id) dashboard = Dashboard.search([('user_id', '=', user_id)]) if not dashboard: dashboard, = Dashboard.create([{'user_id': user_id}]) else: dashboard = dashboard[0] action['views'].reverse() return action, {'res_id': [dashboard.id]} class DashboardContext(ModelView): "Dashboard context" __name__ = 'dashboard.context' user = fields.Many2One('res.user', 'User') company = fields.Many2One('company.company', 'Company', required=True) @staticmethod def default_company(): return Transaction().context.get('company') @staticmethod def default_user(): user_id = Transaction().user if user_id == 0: user_id = Transaction().context.get('user', user_id) return user_id class Demos(ModelSQL, ModelView): 'Demos' __name__ = 'demos.demos' title = fields.Char('Title', required=True) category = fields.Char('Category') icon = fields.Char('Icon') # exemple: "🚢" ou "📝" active = fields.Boolean('Active') video_file = fields.Char('File') description = fields.Text('Description') class News(ModelSQL, ModelView): 'News' __name__ = 'news.news' title = fields.Char('Title', required=True) category = fields.Char('Category') icon = fields.Char('Icon') # exemple: "🚢" ou "📝" active = fields.Boolean('Active') publish_date = fields.Date('Publish Date') model = fields.Char('Model') # ex: 'purchase.purchase' model_id = fields.Integer('Record ID') class Dashboard(ModelSQL, ModelView): "Dashbot" __name__ = "purchase.dashboard" stock = fields.One2Many('lot.lot','dashboard',"STOCK") stock2 = fields.One2Many('lot.lot','dashboard',"PRODUCT") stock3 = fields.One2Many('lot.lot','dashboard',"CURVE") transit = fields.One2Many('stock.shipment.in','dashboard',"VESSEL IN TRANSIT") document = fields.One2Many('document.incoming','dashboard',"Documents received") html_content = fields.Function(fields.Text('HTML Content'),'get_news') demos = fields.Function(fields.Text('Demos'),'get_demos') #metabase = fields.Function(fields.Text("",states={'invisible': ~Eval('metabase',False)}),'gen_url') metabase = fields.Text("",states={'invisible': ~Eval('metabase',False)}) chatbot = fields.Text("") input = fields.Text("") bi_id = fields.Integer("") user = fields.Function(fields.Char(""), 'get_user') fake = fields.Char("",readonly=True) res_id = fields.Integer("Res id") action_return = fields.Char("Action return") actions = fields.One2Many('bot.action','dashboard',"Actions") user_request = fields.Char("User request") user_id = fields.Many2One('res.user',"User") tremor = fields.Function(fields.Text(""),'get_tremor') @classmethod def default_bi_id(cls): return 38 @classmethod def default_res_id(cls): return 0 @fields.depends('input') def on_change_input(self): logger.info("ENTERONCHANGE",self.input) Lot = Pool().get('lot.lot') self.metabase = None if self.input is None: self.chatbot = 'chatbot:' + json.dumps([{"type": "bot", "content": "🧠 Hi Admin, how can I help you?"}], ensure_ascii=False) else: dial = json.loads(self.input) last_content = dial[-1]["content"] last_type = dial[-1]["type"] logger.info("ON_CHANGE_INPUT:%s",last_content) message = '' if "display" in last_content.lower() and "clients" in last_content.lower(): dial.append({"type": "bot", "content": "Clients displayed below"}) if self.bi_id != 7: self.bi_id = 7 self.metabase = self.gen_url() elif "display" in last_content.lower() and "sales" in last_content.lower(): dial.append({"type": "bot", "content": "Sales displayed below"}) if self.bi_id != 41: self.bi_id = 41 self.metabase = self.gen_url() elif "display" in last_content.lower() and "products" in last_content.lower(): dial.append({"type": "bot", "content": "Products displayed below"}) if self.bi_id != 38: self.bi_id = 38 self.metabase = self.gen_url() elif "open" in last_content.lower() and "shipment" in last_content.lower(): sh = self.FindInContent('shipment',last_content) if sh: dial.append({"type": "bot", "content": "Ok shipment opened in new tab"}) dial.append({"type": "do", "content": "open,stock.shipment.in," + str(sh.id) + ",0"}) else: dial.append({"type": "bot", "content": "No shipment found"}) elif "where" in last_content.lower() and "vessel" in last_content.lower(): sh = self.FindInContent('shipment',last_content) if sh: dial.append({"type": "bot", "content": "Look at the map below"}) self.metabase = f"imo:{sh.vessel.vessel_imo}" elif "graph" in last_content.lower() and "lot" in last_content.lower(): g,l,i = last_content.split() lot = Lot(i) if lot: dial.append({"type": "bot", "content": "Graph displayed below"}) self.metabase = lot.get_flow_graph() elif "pivot" in last_content.lower() and "lot" in last_content.lower(): g,l,i = last_content.split() lot = Lot(i) if lot: dial.append({"type": "bot", "content": "Pivot displayed below"}) self.metabase = lot.get_pivot() elif "display" in last_content.lower() and "map" in last_content.lower(): dial.append({"type": "bot", "content": "Map displayed below"}) self.metabase = self.get_map() elif any(k in last_content.lower() for k in keywords): logger.info("ACTION_RETURN:%s",self.action_return) if "help" in last_content.lower(): if "duplicate" in last_content.lower(): dial.append({"type": "bot", "content": "duplicate [purchase/sale]* [party]* [qt]* [plan] [period] [price]"}) elif self.action_return: model,res_id, res_nb = self.action_return.split(",") z,keyword = model.split(".") message = { "type": "bot", "content": ( f'Action done. ' f'Open {keyword} {res_nb}' ) } dial.append(message) else: if "match" in last_content.lower(): dial.append({"type": "bot", "content": "Matching done"}) else: dial.append({"type": "bot", "content": "Not done"}) else: dial.append({"type": "bot", "content": "Ok done"}) self.chatbot = 'chatbot:' + json.dumps(dial, ensure_ascii=False) logger.info("EXITONCHANGE",self.chatbot) def get_last_five_fx_rates(self, from_code='USD', to_code='EUR'): """ Retourne (dernier_taux, avant_dernier_taux) pour le couple de devises. """ Currency = Pool().get('currency.currency') CurrencyRate = Pool().get('currency.currency.rate') # Récupérer les devises EUR et USD from_currency = Currency.search([('name', '=', from_code)])[0] to_currency = Currency.search([('name', '=', to_code)])[0] # Recherche des taux de la devise de base (ex: USD) rates = CurrencyRate.search( [('currency', '=', to_currency.id)], order=[('date', 'DESC')], limit=5, ) if not rates: return None, None, None, None, None # Calcul du taux EUR/USD # Si la devise principale de la société est EUR, et que le taux stocké est # "1 USD = X EUR", on veut l'inverse pour avoir EUR/USD f1 = rates[0].rate f2 = rates[1].rate if len(rates) > 1 else None f3 = rates[2].rate if len(rates) > 2 else None f4 = rates[3].rate if len(rates) > 3 else None f5 = rates[4].rate if len(rates) > 4 else None d1 = rates[0].date d2 = rates[1].date if len(rates) > 1 else None d3 = rates[2].date if len(rates) > 2 else None d4 = rates[3].date if len(rates) > 3 else None d5 = rates[4].date if len(rates) > 4 else None # if from_currency != to_currency: # last_rate = 1 / last_rate if last_rate else None # prev_rate = 1 / prev_rate if prev_rate else None return round(1/f1,6), round(1/f2,6) if f2 else None, round(1/f3,6) if f3 else None, round(1/f4,6) if f4 else None, round(1/f5,6) if f5 else None, d1, d2, d3, d4, d5 def get_tremor(self,name): Date = Pool().get('ir.date') Configuration = Pool().get('gr.configuration') config = Configuration.search(['id','>',0])[0] Shipment = Pool().get('stock.shipment.in') DocumentIncoming = Pool().get('document.incoming') Fee = Pool().get('fee.fee') WR = Pool().get('weight.report') if config.automation: shipment = Shipment.search([('state','!=','received')]) shipment_trend = [sh for sh in shipment if sh.create_date == Date.today()] controller = Shipment.search([('controller','!=',None)]) controller_trend = [co for co in controller if co.create_date == Date.today()] instruction = Shipment.search([('result','!=',None)]) instruction_trend = [si for si in instruction if si.create_date == Date.today()] id_received = Shipment.search([('returned_id','!=',None)]) id_received_trend = [i for i in id_received if i.create_date == Date.today()] wr = WR.search([('id','>',0)]) wr_trend = [w for w in wr if w.create_date == Date.today()] so = Fee.search(['id','=',25]) so_trend = [s for s in so if s.create_date == Date.today()] di = DocumentIncoming.search(['id','>',0]) di_trend = [d for d in di if d.create_date == Date.today()] return ( config.dashboard + "/dashboard/index.html?shipment=" + str(len(shipment)) + "&shipment_trend=" + str(len(shipment_trend)) + "&controller=" + str(len(controller)) + "&controller_trend=" + str(len(controller_trend)) + "&instruction=" + str(len(instruction)) + "&instruction_trend=" + str(len(instruction_trend)) + "&wr=" + str(len(wr)) + "&wr_trend=" + str(len(wr_trend)) + "&so=" + str(len(so)) + "&so_trend=" + str(len(so_trend)) + "&di=" + str(len(di)) + "&di_trend=" + str(len(di_trend)) + "&id_received=" + str(len(id_received)) + "&id_received_trend=" + str(len(id_received_trend))) f1,f2,f3,f4,f5,d1,d2,d3,d4,d5 = self.get_last_five_fx_rates() Valuation = Pool().get('valuation.valuation') last_total,last_variation = Valuation.get_totals() pnl_amount = "{:,.0f}".format(round(last_total,0)) pnl_variation = 0 if last_total and last_variation: pnl_variation = "{:,.2f}".format(round((last_variation/last_total)*100,0)) Open = Pool().get('open.position') opens = Open.search(['id','>',0]) exposure = "{:,.0f}".format(round(sum([e.net_exposure for e in opens]),0)) ToPay = Pool().get('account.invoice') topays = ToPay.search(['type','=','in']) amounts = ToPay.get_amount_to_pay(topays) total = sum(amounts.values(), Decimal(0)) topay = "{:,.0f}".format(round(total,0)) ToReceive = Pool().get('account.invoice') toreceives = ToReceive.search(['type','=','out']) amounts = ToPay.get_amount_to_pay(toreceives) total = sum(amounts.values(), Decimal(0)) toreceive = "{:,.0f}".format(round(total,0)) Purchase = Pool().get('purchase.purchase') draft = Purchase.search(['state','=','draft']) draft_p = len(draft) val = Purchase.search(['state','=','quotation']) val_p = len(val) conf = Purchase.search(['state','=','confirmed']) conf_p = len(conf) Sale = Pool().get('sale.sale') draft = Sale.search(['state','=','draft']) draft_s = len(draft) val = Sale.search(['state','=','quotation']) val_s = len(val) conf = Sale.search(['state','=','confirmed']) conf_s = len(conf) draft = Shipment.search(['state','=','draft']) shipment_d = len(draft) val = Shipment.search(['state','=','started']) shipment_s = len(val) conf = Shipment.search(['state','=','received']) shipment_r = len(conf) Lot = Pool().get('lot.lot') lots = Lot.search([('sale_line','!=',None),('line','!=',None),('lot_type','=','physic')]) lot_m = len(lots) val = Lot.search([('sale_line','=',None),('line','!=',None),('lot_type','=','physic')]) lot_a = len(val) Invoice = Pool().get('account.invoice') invs = Invoice.search(['type','=','in']) inv_p = len(invs) invs = Invoice.search([('type','=','in'),('state','=','paid')]) inv_p_p = len(invs) invs = Invoice.search([('type','=','in'),('state','!=','paid')]) inv_p_np = len(invs) invs = Invoice.search(['type','=','out']) inv_s = len(invs) invs = Invoice.search([('type','=','out'),('state','=','paid')]) inv_s_p = len(invs) invs = Invoice.search([('type','=','out'),('state','!=','paid')]) inv_s_np = len(invs) AccountMove = Pool().get('account.move') accs = AccountMove.search([('journal','=',3),('state','!=','posted')]) pay_val = len(accs) accs = AccountMove.search([('journal','=',3),('state','=','posted')]) pay_posted = len(accs) return ( config.dashboard + "/dashboard/index.html?pnl_amount=" + str(pnl_amount) + "&pnl_variation=" + str(pnl_variation) + "&exposure=" + str(exposure) + "&topay=" + str(topay) + "&toreceive=" + str(toreceive) + "&eurusd=" + str(f1) + "&eurusd=" + str(f2) + "&eurusd=" + str(f3) + "&eurusd=" + str(f4) + "&eurusd=" + str(f5) + "&eurusd_date=" + str(d1) + "&eurusd_date=" + str(d2) + "&eurusd_date=" + str(d3) + "&eurusd_date=" + str(d4) + "&eurusd_date=" + str(d5) + "&draft_p=" + str(draft_p) + "&val_p=" + str(val_p) + "&conf_p=" + str(conf_p) + "&draft_s=" + str(draft_s) + "&val_s=" + str(val_s) + "&conf_s=" + str(conf_s) + "&shipment_d=" + str(shipment_d) + "&shipment_s=" + str(shipment_s) + "&shipment_r=" + str(shipment_r) + "&lot_m=" + str(lot_m) + "&lot_a=" + str(lot_a) + "&inv_p=" + str(inv_p) + "&inv_p_p=" + str(inv_p_p) + "&inv_p_np=" + str(inv_p_np) + "&inv_s=" + str(inv_s) + "&inv_s_p=" + str(inv_s_p) + "&inv_s_np=" + str(inv_s_np) + "&pay_val=" + str(pay_val) + "&pay_posted=" + str(pay_posted) ) def get_news(self, name): News = Pool().get('news.news') Date = Pool().get('ir.date') news_list = News.search([('active', '=', True)], limit=5, order=[('publish_date', 'DESC')]) last_rate,prev_rate, = self.get_last_five_fx_rates() if last_rate and prev_rate: variation = ((last_rate - prev_rate) / prev_rate) * 100 if prev_rate else 0 direction = "📈" if variation > 0 else "📉" # 🆕 Création d’une “fake news” locale forex_news = News() forex_news.icon = direction forex_news.category = "Forex" # Détermine la direction et le style couleur if variation > 0: arrow = "⬆️" color = "#28a745" # vert elif variation < 0: arrow = "⬇️" color = "#dc3545" # rouge else: arrow = "➡️" color = "#6c757d" # gris neutre # Contenu HTML enrichi forex_news.title = ( f"EUR/USD: {last_rate:.4f}" f"" f" {variation:+.2f}%" f" " ) forex_news.publish_date = Date.today() forex_news.model = 'currency.currency' # par exemple 'forex.forex' forex_news.model_id = 2 forex_news.active = True # On insère la “news” au début de la liste news_list.insert(0, forex_news) html_parts = [ '
', '
Last news
' ] for n in news_list: icon = n.icon or "📰" category = n.category or "General" title = n.title or "" # content = (n.content or "").replace('\n', '
')
{content}
publish_date = n.publish_date.strftime('%d-%m-%Y') if n.publish_date else "" # Génération du lien Tryton if n.model and n.model_id: # 🆕 lien JS pour ouvrir un tab interne Tryton js_link = ( f"javascript:Sao.Tab.create({{" f"model: '{n.model}', " f"res_id: {n.model_id}, " f"mode: ['form'], " f"target: 'new'" f"}});" ) title_html = ( f'' f'{title}' ) else: title_html = f'{title}' html_parts.append(f'''
{icon} {category}
{title_html}
{publish_date}
''') html_parts.append('
') return "\n".join(html_parts) def get_demos(self, name): Demos = Pool().get('demos.demos') html_parts = [ '
🎬 Available Demo
' ] demos = Demos.search([('active', '=', True)],order=[('id', 'DESC')]) for n in demos: icon = n.icon or "📰" category = n.category or "General" title = n.title or "Sans titre" description = n.description or "" # suppose que n.video_path contient un chemin du type '/videos/demo1.mp4' video_url = f"/videos/{n.video_file}" if getattr(n, 'video_file', None) else None # lien vers le lecteur vidéo du navigateur video_link = ( f'Play the video 🎥' if video_url else 'No available video' ) html_parts.append(f'''
{icon} {category}
{title}
{description}
''') html_parts.append('') return "\n".join(html_parts) def get_map(self): departure = { "name":"SANTOS","lat": str(-23.9), "lon": str(-46.3) } arrival = { "name":"ISTANBUL","lat": str(41), "lon": str(29) } data = { "highlightedCountryNames": [{"name":"Turkey"},{"name":"Brazil"}], "routes": [[ { "lon": -46.3, "lat": -23.9 }, { "lon": -30.0, "lat": -20.0 }, { "lon": -30.0, "lat": 0.0 }, { "lon": -6.0, "lat": 35.9 }, { "lon": 15.0, "lat": 38.0 }, { "lon": 29.0, "lat": 41.0 } ]], "boats": [{ "name": "CARIBBEAN 1", "imo": "1234567", "lon": -30.0, "lat": 0.0, "status": "En route", "links": [ { "text": "Voir sur VesselFinder", "url": "https://www.vesselfinder.com" }, { "text": "Détails techniques", "url": "https://example.com/tech" } ], "actions": [ { "type": "track", "id": "123", "label": "Suivre ce bateau" }, { "type": "details", "id": "123", "label": "Voir détails" } ] }], "cottonStocks": [ { "name":"Mali","lat": 12.65, "lon": -8.0, "amount": 300 }, { "name":"Egypte","lat": 30.05, "lon": 31.25, "amount": 500 }, { "name":"Irak","lat": 33.0, "lon": 44.0, "amount": 150 } ], "departures": [departure], "arrivals": [arrival] } return "d3:" + json.dumps(data) def FindInContent(self,what,content): if what == 'shipment': Shipment = Pool().get('stock.shipment.in') shipments = Shipment.search([('id','>',0),('vessel','!=',None)],order=[('create_date', 'DESC')]) if shipments: for sh in shipments: if sh.vessel.vessel_name.lower() in content.lower(): return sh elif what == 'purchase': Purchase = Pool().get('purchase.purchase') purchases = Purchase.search([('id','>',0)],order=[('create_date', 'DESC')]) if purchases: for pu in purchases: if pu.party.name.lower() in content.lower(): return pu def gen_url(self,name=None): Configuration = Pool().get('gr.configuration') config = Configuration.search(['id','>',0])[0] payload = { "resource": {"dashboard": self.bi_id}, "params": {}, "exp": datetime.datetime.utcnow() + datetime.timedelta(minutes=30), } token = jwt.encode(payload, config.payload, algorithm="HS256") logger.info("TOKEN:%s",token) return f"metabase:http://vps107.geneva.hosting:3000/embed/dashboard/{token}#bordered=true&titled=true" # @fields.depends('input') # def on_change_with_metabase(self): # return self.gen_url() # @classmethod # def default_html_content(cls): # return 'http://62.72.36.116:3000/question/41-sales-by-clients-and-suppliers' def get_user(self,name): User = Pool().get('res.user') user = User(Transaction().user) return "Welcome " + user.name + " !" class Incoming(metaclass=PoolMeta): __name__ = 'document.incoming' dashboard = fields.Many2One('purchase.dashboard',"Dashboard") class BotWizard(Wizard): "Bot wizard" __name__ = "bot.wizard" start = StateTransition() def get_model(self,keyword): if keyword == 'purchase': return 'purchase.purchase' elif keyword == 'sale': return 'sale.sale' elif 'prepayment' in keyword: return 'account.invoice' def transition_start(self): Dashboard = Pool().get('purchase.dashboard') Party = Pool().get('party.party') Account = Pool().get('account.account') Currency = Pool().get('currency.currency') Purchase = Pool().get('purchase.purchase') Sale = Pool().get('sale.sale') Invoice = Pool().get('account.invoice') InvoiceLine = Pool().get('account.invoice.line') StockMove = Pool().get('stock.move') ExecutionPlan = Pool().get('workflow.plan') DelPeriod = Pool().get('product.month') Lot = Pool().get('lot.lot') LotQt = Pool().get('lot.qt') LotAdd = Pool().get('lot.add.line') Date = Pool().get('ir.date') d = Dashboard(self.records[0]) context = Transaction().context if d: d.action_return = "" Dashboard.save([d]) parts = shlex.split(context['user_request']) do_, mo, cp, fl, un, un2, un3 = (parts + [None]*7)[:7] if do_ in keywords: logger.info("BOT_WIZARD:%s",fl) parties = Party.search(['id','>',0]) party = None model = self.get_model(mo) if cp: for p in parties: if cp.lower() == p.name.lower(): party = p break if do_ == "duplicate": if mo == "help": return 'end' if mo == "purchase" or mo == "sale": if party: Model = Pool().get(model) if fl: line_price = None line_period = None if un: plan = ExecutionPlan.search(['name','=', un]) if plan: models = Model.search([('party','=',party.id),('plan','=',plan[0].id)],order=[('create_date', 'DESC')]) if un3: line_price = Decimal(un3) line_period = DelPeriod.search(['month_name','=',un2])[0] else: if un2.isdigit(): line_price = Decimal(un2) else: periods = DelPeriod.search(['month_name','=',un]) if periods: line_period = periods[0] if un2.isdigit(): line_price = Decimal(un2) else: if un.isdigit(): line_price = Decimal(un) else: models = Model.search([('party','=',party.id)],order=[('create_date', 'DESC')]) if models: model_id = models[0].id new_model, = Model.copy([model_id], default={}) Model.save([new_model]) Model.quote([new_model]) logger.info("BOT_WIZARD:%s",new_model.lines) if new_model.lines and fl: line = new_model.lines[0] line.quantity = Decimal(fl) line.quantity_theorical = Decimal(fl) line.premium = Decimal(0) if line_period: line.del_period = line_period if line_price: if line.linked_price > 0: line.linked_price = line_price line.unit_price = line.get_price_linked_currency() else: line.unit_price = line_price Pool().get(mo + '.line').save([line]) d.action_return = model + ',' + str(new_model.id) + ',' + str(new_model.number) Dashboard.save([d]) elif do_ == "match": Lml = Pool().get('lot.matching.lot') lmp = Lml() lms = Lml() p = Purchase.search(['number','=',mo]) if p: p = p[0] s = Sale.search(['number','=',cp]) if s: s = s[0] lp = p.lines[0].lots[1] lmp.lot_id = lp.id lqt = lp.getLotQt() lmp.lot_r_id = lqt[0].id if lqt else None lmp.lot_matched_qt = lp.lot_quantity ls = s.lines[0].lots[0] lms.lot_r_id = ls.getLotQt()[0].id lms.lot_matched_qt = lp.lot_quantity lms.lot_sale = s LotQt.match_lots([lmp],[lms]) Sale._process_shipment([s]) elif do_ == "add": if mo == "lot": purchase = Purchase.search([('number','=',cp)]) if purchase: purchase = purchase[0] vlot = purchase.lines[0].lots[0] lqt = LotQt.search([('lot_p','=',vlot.id)]) if lqt and vlot.lot_quantity > 0: lqt = lqt[0] l = LotAdd() l.lot_qt = vlot.lot_quantity l.lot_unit = purchase.lines[0].unit l.lot_unit_line = l.lot_unit l.lot_quantity = l.lot_qt l.lot_gross_quantity = l.lot_qt l.lot_premium = Decimal(0) l.lot_chunk_key = None lot_id = LotQt.add_physical_lots(lqt,[l]) d.action_return = 'lot.lot,' + str(lot_id) + ',' + str(lot_id) Dashboard.save([d]) elif do_ == "reception": if mo == "lot": lot = Lot(int(cp)) if lot.lot_move: StockMove.do([lot.lot_move[0].move]) d.action_return = 'stock.move,' + str(lot.lot_move[0].move.id) + ',' + str(lot.lot_move[0].move.id) Dashboard.save([d]) elif do_ == "delivery": if mo == "lot": lot = Lot(int(cp)) if lot.lot_move: StockMove.do([lot.lot_move[1].move]) d.action_return = 'stock.move,' + str(lot.lot_move[1].move.id) + ',' + str(lot.lot_move[1].move.id) Dashboard.save([d]) elif do_ == "invoice": if mo == "lot": lot = Lot(int(cp)) Purchase._process_invoice([lot.line.purchase],[lot],'prov') if lot.invoice_line_prov: invoice = Invoice(lot.invoice_line_prov.invoice.id) invoice.invoice_date = Date.today() if fl == "prepayment": if un: invoice.call_deposit(Account(748),un) else: invoice.call_deposit(Account(748),'Deposit') Invoice.validate_invoice([invoice]) d.action_return = 'account.invoice,' + str(lot.invoice_line_prov.invoice.id) + ',' + str(lot.invoice_line_prov.invoice.number) Dashboard.save([d]) elif do_ == "pay": if mo == "invoice": #PayInvoice = Pool().get('account.invoice.pay') PaymentMethod = Pool().get('account.invoice.payment.method') inv = Invoice.search(['number','=',cp]) if inv: pm = PaymentMethod.search(['id','>',0])[0] inv = inv[0] lines = inv.pay_invoice( inv.amount_to_pay, pm, Date.today(), 'Payment of invoice ' + inv.number, 0, party=inv.party.id) if lines: d.action_return = 'account.invoice,' + str(inv.id) + ',' + inv.number Dashboard.save([d]) elif do_ == "create": if mo == "prepaymentP" or mo == "prepaymentS": if party: if party.addresses: invoice = Invoice() invoice.type = 'in' if mo == "prepaymentP" else 'out' invoice.journal = 2 if mo == "prepaymentP" else 1#invoice.set_journal() logger.info("WIZARD_PREPAY:%s",invoice.journal) invoice.invoice_date = Date.today() invoice.party = party.id invoice.invoice_address = party.addresses[0] invoice.account = party.account_payable_used if mo == "prepaymentP" else party.account_receivable_used invoice.payment_term = 6 invoice.description = 'Prepayment supplier' if mo == "prepaymentP" else 'Prepayment client' if un: curr = Currency.search(['name','=', un]) if curr: invoice.currency = curr[0].id line = InvoiceLine() line.account = 748 line.quantity = 1 line.unit_price = Decimal(fl) invoice.lines = [line] Invoice.save([invoice]) Invoice.post([invoice]) d.action_return = model + ',' + str(invoice.id)+ ',' + str(invoice.number) Dashboard.save([d]) return 'end' class BotAction(ModelSQL, ModelView): "Bot Action" __name__ = "bot.action" dashboard = fields.Many2One('purchase.dashboard',"Dashboard") type = fields.Selection([ ('duplicate', 'duplicate'), ('invoice', 'invoice'), ('receive', 'receive'), ('add physic', 'add physic'), ('match', 'match'), ('link', 'link'), ('create', 'create')]) model = fields.Char("Model") am_qt = fields.Numeric("Amount/Quantity") unit = fields.Char("Unit/Currency") keyword = fields.Char("Keyword") cp = fields.Char("Party") model_id = fields.Integer("Model id") res_id = fields.Integer("Res id") state = fields.Selection([ ('todo', 'todo'), ('done', 'done'), ]) @classmethod def state_default(cls): return 'todo' @classmethod def state_dashboard(cls): return 1