Tower: upload laundry_management 19.0.19.0.4 (via marketplace)

This commit is contained in:
2026-05-01 15:01:08 +00:00
parent 98bc5f8027
commit 4261ec5ed0

View File

@@ -0,0 +1,180 @@
from odoo import models, fields, api
class LaundryDashboard(models.TransientModel):
"""Live KPI dashboard — queries sale.order with is_laundry_order = True."""
_name = 'laundry.dashboard'
_description = 'Laundry Dashboard'
today_orders = fields.Integer(string="Today's Orders")
today_revenue = fields.Monetary(string="Today's Revenue", currency_field='currency_id')
today_collected = fields.Monetary(string='Collected Today', currency_field='currency_id')
today_outstanding = fields.Monetary(string='Outstanding Today', currency_field='currency_id')
pending_count = fields.Integer(string='Pending Orders')
ready_count = fields.Integer(string='Ready for Pickup')
in_progress_count = fields.Integer(string='In Processing')
draft_count = fields.Integer(string='Quotes / Draft')
session_is_open = fields.Boolean(string='Session Open')
session_name = fields.Char(string='Session')
session_opening_cash = fields.Monetary(string='Opening Float', currency_field='currency_id')
session_sales = fields.Monetary(string='Session Sales', currency_field='currency_id')
session_cash = fields.Monetary(string='Session Cash', currency_field='currency_id')
session_bank = fields.Monetary(string='Session Bank', currency_field='currency_id')
session_id = fields.Many2one('laundry.session', string='Session Link')
month_orders = fields.Integer(string='Orders This Month')
month_revenue = fields.Monetary(string='Revenue This Month', currency_field='currency_id')
month_paid = fields.Monetary(string='Collected This Month', currency_field='currency_id')
currency_id = fields.Many2one(
'res.currency',
default=lambda self: self.env.company.currency_id,
)
@api.model
def _build(self):
today = fields.Date.today()
month_start = today.replace(day=1)
company = self.env.company
Order = self.env['sale.order']
Payment = self.env['account.payment']
_base_domain = [
('is_laundry_order', '=', True),
('company_id', '=', company.id),
]
# ── Today ──────────────────────────────────────────────────────
today_orders = Order.search(_base_domain + [
('date_order', '>=', fields.Datetime.to_datetime(today)),
('state', 'not in', ['cancel', 'draft']),
])
today_invoices = today_orders.mapped('invoice_ids').filtered(
lambda i: i.state == 'posted' and i.move_type == 'out_invoice'
)
today_revenue = sum(today_orders.mapped('amount_total'))
today_outstanding = sum(
max(i.amount_residual, 0.0) for i in today_invoices
)
today_collected = today_revenue - today_outstanding
# ── Pipeline (all active laundry orders) ──────────────────────
pipeline = Order.search(_base_domain + [
('state', '=', 'sale'),
])
pending_count = len(pipeline)
ready_count = len(pipeline.filtered(lambda o: o.laundry_state == 'ready'))
in_progress_count = len(pipeline.filtered(lambda o: o.laundry_state == 'processing'))
draft_count = len(Order.search(_base_domain + [('state', '=', 'draft')]))
# ── Session ────────────────────────────────────────────────────
session = self.env['laundry.session'].search([
('state', '=', 'opened'),
('company_id', '=', company.id),
], limit=1)
# ── Month ──────────────────────────────────────────────────────
month_orders = Order.search(_base_domain + [
('date_order', '>=', fields.Datetime.to_datetime(month_start)),
('state', 'not in', ['cancel', 'draft']),
])
month_invoices = month_orders.mapped('invoice_ids').filtered(
lambda i: i.state == 'posted' and i.move_type == 'out_invoice'
)
month_revenue = sum(month_orders.mapped('amount_total'))
month_outstanding = sum(max(i.amount_residual, 0.0) for i in month_invoices)
month_paid = month_revenue - month_outstanding
return self.create({
'today_orders' : len(today_orders),
'today_revenue' : today_revenue,
'today_collected' : max(today_collected, 0.0),
'today_outstanding' : today_outstanding,
'pending_count' : pending_count,
'ready_count' : ready_count,
'in_progress_count' : in_progress_count,
'draft_count' : draft_count,
'session_is_open' : bool(session),
'session_name' : session.name if session else '',
'session_opening_cash' : session.opening_cash if session else 0.0,
'session_sales' : session.total_sales if session else 0.0,
'session_cash' : session.total_cash if session else 0.0,
'session_bank' : session.total_bank if session else 0.0,
'session_id' : session.id if session else False,
'month_orders' : len(month_orders),
'month_revenue' : month_revenue,
'month_paid' : max(month_paid, 0.0),
})
@api.model
def action_open_dashboard(self):
rec = self._build()
return {
'type': 'ir.actions.act_window',
'name': 'Dashboard',
'res_model': 'laundry.dashboard',
'res_id': rec.id,
'view_mode': 'form',
'target': 'main',
'flags': {'mode': 'readonly'},
}
def action_refresh(self):
return self.action_open_dashboard()
def action_new_order(self):
return {
'type': 'ir.actions.act_window',
'name': 'New Laundry Order',
'res_model': 'sale.order',
'view_mode': 'form',
'target': 'current',
'context': {
'default_is_laundry_order': True,
},
}
def action_open_session(self):
if self.session_id:
return {
'type': 'ir.actions.act_window',
'name': 'Session',
'res_model': 'laundry.session',
'res_id': self.session_id.id,
'view_mode': 'form',
}
return {
'type': 'ir.actions.act_window',
'name': 'Sessions',
'res_model': 'laundry.session',
'view_mode': 'list,form',
}
def action_new_session(self):
return {
'type': 'ir.actions.act_window',
'name': 'New Session',
'res_model': 'laundry.session',
'view_mode': 'form',
}
def action_view_ready_orders(self):
return {
'type': 'ir.actions.act_window',
'name': 'Ready for Pickup',
'res_model': 'sale.order',
'view_mode': 'list,form',
'domain': [('is_laundry_order', '=', True), ('laundry_state', '=', 'ready')],
}
def action_view_pending_orders(self):
return {
'type': 'ir.actions.act_window',
'name': 'Pending Orders',
'res_model': 'sale.order',
'view_mode': 'list,form',
'domain': [('is_laundry_order', '=', True), ('state', '=', 'sale'),
('laundry_state', 'not in', ['delivered'])],
}