Files
tradon/modules/purchase_trade/dashboard.py
2026-03-27 14:08:07 +01:00

879 lines
38 KiB
Python
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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'<a href="#" class="tryton-link" data-model="{model}" '
f'data-id="{res_id}" data-view="form">Open {keyword} {res_nb}</a>'
)
}
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 dune “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"<span style='color:{color}; font-weight:bold;'>"
f" {variation:+.2f}%"
f"</span> "
)
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 = [
'<div class="last-news">',
' <div class="last-news-title">Last news</div>'
]
for n in news_list:
icon = n.icon or "📰"
category = n.category or "General"
title = n.title or ""
# content = (n.content or "").replace('\n', '<br/>') <div class="last-news-content">{content}</div>
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'<a href="{js_link}" style="text-decoration:none; color:#2a5db0;">'
f'{title}</a>'
)
else:
title_html = f'<span>{title}</span>'
html_parts.append(f'''
<div class="last-news-item" style="margin-bottom: 8px;">
<div class="last-news-category">{icon} {category}</div>
<div class="last-news-header" style="display:flex; justify-content:space-between; align-items:center;">
<div class="last-news-title-item"><strong>{title_html}</strong></div>
<div class="last-news-date" style="color:#666; font-size:0.9em;">{publish_date}</div>
</div>
</div>
''')
html_parts.append('</div>')
return "\n".join(html_parts)
def get_demos(self, name):
Demos = Pool().get('demos.demos')
html_parts = [
'<div class="demos" style="background-color:#f5f5f5; padding:12px; border-radius:12px;>',
' <div class="demos-title" style="font-size:1.2em; font-weight:bold; margin-bottom:10px;">🎬 Available Demo</div>'
]
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'<a href="{video_url}" target="_blank" '
'style="color:#007bff; text-decoration:none;">Play the video 🎥</a>'
if video_url else
'<span style="color:gray;">No available video</span>'
)
html_parts.append(f'''
<div class="demo-item" style="margin-bottom: 12px; border-bottom:1px solid #eee; padding-bottom:8px;">
<div class="demo-category" style="font-size:0.9em; color:#666;">{icon} {category}</div>
<div class="demo-header" style="display:flex; justify-content:space-between; align-items:center;">
<div class="demo-title" style="font-weight:bold;">{title}</div>
<div class="demo-link">{video_link}</div>
</div>
<div class="demo-description" style="font-size:0.85em; color:#555;">{description}</div>
</div>
''')
html_parts.append('</div>')
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