This commit is contained in:
Raman Marikanti 2026-02-12 14:53:38 +05:30
parent 796a8e1e17
commit 5581b94b64
3 changed files with 1239 additions and 730 deletions

View File

@ -43,6 +43,9 @@ class CustomerOrdersReport(models.AbstractModel):
from odoo import models, fields, api from odoo import models, fields, api
from datetime import datetime, timedelta from datetime import datetime, timedelta
import json import json
from datetime import date, datetime
import calendar
class CORDashboard(models.AbstractModel): class CORDashboard(models.AbstractModel):
@ -257,107 +260,65 @@ class CORDashboard(models.AbstractModel):
except Exception as e: except Exception as e:
return [] return []
@api.model
# Add this method to your cor.dashboard model def get_dpr_daily_production_data(self, month, year):
def get_dpr_daily_production_data(self):
"""Get DPR (Daily Production Report) data"""
try: try:
# Get current date month = str(int(month))
today = fields.Date.today() year = str(int(year))
first_day_of_month = today.replace(day=1) results = []
last_day_of_prev_month = first_day_of_month - timedelta(days=1)
first_day_of_prev_month = last_day_of_prev_month.replace(day=1)
# Initialize result # Search for customer orders in selected month/year
result = [] co = self.env['customer.order'].search([
('order_month', '=', month),
# Get all products ('order_year', '=', year)
products = self.env['product.product'].search([
('type', '=', 'product'),
('categ_id.complete_name', 'ilike', 'Finished Goods')
]) ])
for product in products: for line in co.order_line_ids:
# Get pending orders from last month month_int = int(line.order_id.order_month)
pending_orders = self.env['sale.order.line'].search([ year_int = int(line.order_id.order_year)
('product_id', '=', product.id),
('order_id.state', 'in', ['sale', 'done']), first_day = date(year_int, month_int, 1)
('order_id.date_order', '>=', first_day_of_prev_month), last_day = date(year_int, month_int,
('order_id.date_order', '<=', last_day_of_prev_month), calendar.monthrange(year_int, month_int)[1])
('qty_delivered', '<', 'product_uom_qty')
# Convert to datetime for date_start (Datetime field)
start_datetime = datetime.combine(first_day, datetime.min.time())
end_datetime = datetime.combine(last_day, datetime.max.time())
# Get all production orders for this customer order's products
production_orders = self.env['mrp.production'].search_read([
('product_id', '=', line.product_id.id),
('date_start', '>=', start_datetime),
('date_start', '<=', end_datetime),
], ['date_start', 'product_uom_qty'])
# Calculate pending orders from previous months
pending_orders = self.env['customer.order.line'].search([
('product_id', '=', line.product_id.id),
('order_id.customer_id', '=', line.order_id.customer_id.id),
('order_month', '!=', month),
('pending_dispatch', '>', 0)
]) ])
pending_qty = sum(pending_orders.mapped('product_uom_qty')) - sum(pending_orders.mapped('qty_delivered')) pending_order_qty = sum(pending_orders.mapped('pending_dispatch'))
pending_value = pending_qty * product.standard_price
# Get present month orders results.append({
present_orders = self.env['sale.order.line'].search([ 'customer': line.order_id.customer_id.name,
('product_id', '=', product.id), 'product': line.product_id.display_name,
('order_id.state', 'in', ['sale', 'done']), 'order_qty': line.order_qty,
('order_id.date_order', '>=', first_day_of_month), 'order_qty_kg': line.order_qty * line.product_id.weight,
('order_id.date_order', '<=', today) 'fg_qty': line.fg_available,
]) 'pending_order': pending_order_qty,
'produced_qty': line.produced_qty,
present_qty = sum(present_orders.mapped('product_uom_qty')) 'produced_qty_kg': line.produced_qty * line.product_id.weight,
present_value = present_qty * product.standard_price 'dispatch_qty': line.dispatched_qty,
'dispatched_qty_kg': line.dispatched_qty * line.product_id.weight,
# Get FG available quantity 'remaining_production': line.order_qty - (line.produced_qty + line.fg_available),
fg_qty = product.qty_available 'date_wise': production_orders
fg_value = fg_qty * product.standard_price
# Get dispatch qty for current month
moves = self.env['stock.move'].search([
('product_id', '=', product.id),
('state', '=', 'done'),
('date', '>=', first_day_of_month),
('date', '<=', today),
('location_dest_id.usage', '=', 'customer'),
('location_id.usage', '=', 'internal')
])
dispatch_qty = sum(moves.mapped('quantity_done'))
dispatch_value = dispatch_qty * product.standard_price
# Get daily production for last 31 days
daily_data = {}
for i in range(31, 0, -1):
day_date = today - timedelta(days=(31 - i))
day_moves = self.env['stock.move'].search([
('product_id', '=', product.id),
('state', '=', 'done'),
('date', '>=', day_date),
('date', '<', day_date + timedelta(days=1)),
('production_id', '!=', False) # Production moves
])
daily_data[f'day{i}_qty'] = sum(day_moves.mapped('quantity_done'))
# Calculate totals
total_order_qty = pending_qty + present_qty
total_order_value = pending_value + present_value
remaining_qty = max(0, total_order_qty - fg_qty)
remaining_value = max(0, total_order_value - fg_value)
result.append({
'product_id': product.id,
'product_name': product.display_name,
'pending_order_qty': pending_qty,
'pending_order_value': pending_value,
'present_month_order_qty': present_qty,
'present_month_order_value': present_value,
'total_order_qty': total_order_qty,
'total_order_value': total_order_value,
'fg_available_qty': fg_qty,
'fg_available_value': fg_value,
'dispatch_qty': dispatch_qty,
'dispatch_value': dispatch_value,
'remaining_to_produce_qty': remaining_qty,
'remaining_to_produce_value': remaining_value,
**daily_data
}) })
return result return results
except Exception as e: except Exception as e:
# _logger.error(f"Error in get_dpr_daily_production_data: {str(e)}") # logger.error(f"Error in get_dpr_daily_production_data: {str(e)}")
return [] return []

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<templates xml:space="preserve"> <templates xml:space="preserve">
<t t-name="CustomerOrderStatusGrid" owl="1"> <t t-name="CustomerOrderStatusGrid" owl="1">
<div class="customer-order-status-container overflow-auto"> <div class="customer-order-status-container overflow-auto">
<!-- Header --> <!-- Header -->
@ -8,6 +7,7 @@
<div class="header-card"> <div class="header-card">
<div class="header-content"> <div class="header-content">
<h5 class="header-title">Customer Order Status Dashboard</h5> <h5 class="header-title">Customer Order Status Dashboard</h5>
<!-- Simple Month/Year Filter - Auto filter on change --> <!-- Simple Month/Year Filter - Auto filter on change -->
<div class="simple-filter" t-if="!state.isLoading"> <div class="simple-filter" t-if="!state.isLoading">
<div class="row g-2 align-items-center"> <div class="row g-2 align-items-center">
@ -16,15 +16,18 @@
<span class="input-group-text"> <span class="input-group-text">
<i class="fa fa-calendar"></i> <i class="fa fa-calendar"></i>
</span> </span>
<input type="month" <input
class="form-control form-control-sm" type="month"
t-model="state.selectedMonthYear" class="form-control form-control-sm"
t-on-change="loadAllData"/> t-model="state.selectedMonthYear"
t-on-change="loadAllData"
/>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<div class="header-stats" t-if="!state.isLoading">
<div class="header-stats" t-if="!state.isLoading">
<div class="stats-container"> <div class="stats-container">
<div class="stat-item"> <div class="stat-item">
<span class="stat-label">Total Orders:</span> <span class="stat-label">Total Orders:</span>
@ -40,8 +43,7 @@
</div> </div>
<div class="stat-item"> <div class="stat-item">
<span class="stat-label">Production Rate:</span> <span class="stat-label">Production Rate:</span>
<span class="stat-value badge" <span class="stat-value badge" t-att-class="getCompletionRateClass(state.summary.completionRate)">
t-att-class="getCompletionRateClass(state.summary.completionRate)">
<t t-esc="state.summary.completionRate.toFixed(1)"/>% <t t-esc="state.summary.completionRate.toFixed(1)"/>%
</span> </span>
</div> </div>
@ -59,60 +61,68 @@
<!-- Tabs Navigation --> <!-- Tabs Navigation -->
<ul class="nav nav-tabs dashboard-tabs" role="tablist"> <ul class="nav nav-tabs dashboard-tabs" role="tablist">
<li class="nav-item" role="presentation"> <li class="nav-item" role="presentation">
<button class="nav-link active" <button
id="order-tab" class="nav-link active"
data-bs-toggle="tab" id="order-tab"
data-bs-target="#order-content" data-bs-toggle="tab"
type="button" data-bs-target="#order-content"
role="tab" type="button"
aria-controls="order-content" role="tab"
aria-selected="true" aria-controls="order-content"
t-on-click="onTabShow"> aria-selected="true"
t-on-click="onTabShow"
>
<i class="fa fa-users mr-2"></i> <i class="fa fa-users mr-2"></i>
Customer Order Status Customer Order Status
<span class="badge bg-primary ms-2" t-esc="state.gridData.length"/> <span class="badge bg-primary ms-2" t-esc="state.gridData.length"/>
</button> </button>
</li> </li>
<li class="nav-item" role="presentation"> <li class="nav-item" role="presentation">
<button class="nav-link" <button
id="dispatch-tab" class="nav-link"
data-bs-toggle="tab" id="dispatch-tab"
data-bs-target="#dispatch-content" data-bs-toggle="tab"
type="button" data-bs-target="#dispatch-content"
role="tab" type="button"
aria-controls="dispatch-content" role="tab"
aria-selected="false" aria-controls="dispatch-content"
t-on-click="onTabShow"> aria-selected="false"
t-on-click="onTabShow"
>
<i class="fa fa-truck mr-2"></i> <i class="fa fa-truck mr-2"></i>
Dispatch Summary Dispatch Summary
<span class="badge bg-success ms-2" t-esc="state.dispatchData.length"/> <span class="badge bg-success ms-2" t-esc="state.dispatchData.length"/>
</button> </button>
</li> </li>
<li class="nav-item" role="presentation"> <li class="nav-item" role="presentation">
<button class="nav-link" <button
id="rm-tab" class="nav-link"
data-bs-toggle="tab" id="rm-tab"
data-bs-target="#rm-content" data-bs-toggle="tab"
type="button" data-bs-target="#rm-content"
role="tab" type="button"
aria-controls="rm-content" role="tab"
aria-selected="false" aria-controls="rm-content"
t-on-click="onTabShow"> aria-selected="false"
t-on-click="onTabShow"
>
<i class="fa fa-cubes mr-2"></i> <i class="fa fa-cubes mr-2"></i>
Raw Material Availability Raw Material Availability
<span class="badge bg-warning ms-2" t-esc="state.rmData.length"/> <span class="badge bg-warning ms-2" t-esc="state.rmData.length"/>
</button> </button>
</li> </li>
<li class="nav-item" role="presentation"> <li class="nav-item" role="presentation">
<button class="nav-link" <button
id="dpr-tab" class="nav-link"
data-bs-toggle="tab" id="dpr-tab"
data-bs-target="#dpr-content" data-bs-toggle="tab"
type="button" data-bs-target="#dpr-content"
role="tab" type="button"
aria-controls="dpr-content" role="tab"
aria-selected="false" aria-controls="dpr-content"
t-on-click="onTabShow"> aria-selected="false"
t-on-click="onTabShow"
>
<i class="fa fa-chart-line mr-2"></i> <i class="fa fa-chart-line mr-2"></i>
DPR - Daily Production Report DPR - Daily Production Report
<span class="badge bg-info ms-2" t-esc="state.dprData.length"/> <span class="badge bg-info ms-2" t-esc="state.dprData.length"/>
@ -123,10 +133,12 @@
<!-- Tab Content --> <!-- Tab Content -->
<div class="tab-content dashboard-tab-content"> <div class="tab-content dashboard-tab-content">
<!-- Tab 1: Customer Order Status --> <!-- Tab 1: Customer Order Status -->
<div class="tab-pane fade show active" <div
id="order-content" class="tab-pane fade show active"
role="tabpanel" id="order-content"
aria-labelledby="order-tab"> role="tabpanel"
aria-labelledby="order-tab"
>
<div class="tab-card"> <div class="tab-card">
<div class="tab-header d-flex justify-content-between align-items-center"> <div class="tab-header d-flex justify-content-between align-items-center">
<div class="tab-stats"> <div class="tab-stats">
@ -159,7 +171,7 @@
<button class="btn btn-sm btn-outline-secondary" t-on-click="refreshOrderStatus"> <button class="btn btn-sm btn-outline-secondary" t-on-click="refreshOrderStatus">
<i class="fa fa-refresh mr-1"></i> Refresh <i class="fa fa-refresh mr-1"></i> Refresh
</button> </button>
<button class="btn btn-sm btn-outline-primary" t-on-click="() => exportGrid('order')"> <button class="btn btn-sm btn-outline-primary" t-on-click="exportOrderGrid">
<i class="fa fa-download mr-1"></i> Export <i class="fa fa-download mr-1"></i> Export
</button> </button>
</div> </div>
@ -181,10 +193,12 @@
</div> </div>
<!-- Tab 2: Dispatch Summary --> <!-- Tab 2: Dispatch Summary -->
<div class="tab-pane fade" <div
id="dispatch-content" class="tab-pane fade"
role="tabpanel" id="dispatch-content"
aria-labelledby="dispatch-tab"> role="tabpanel"
aria-labelledby="dispatch-tab"
>
<div class="tab-card"> <div class="tab-card">
<div class="tab-header d-flex justify-content-between align-items-center"> <div class="tab-header d-flex justify-content-between align-items-center">
<div class="tab-stats"> <div class="tab-stats">
@ -217,7 +231,7 @@
<button class="btn btn-sm btn-outline-secondary" t-on-click="refreshDispatchSummary"> <button class="btn btn-sm btn-outline-secondary" t-on-click="refreshDispatchSummary">
<i class="fa fa-refresh mr-1"></i> Refresh <i class="fa fa-refresh mr-1"></i> Refresh
</button> </button>
<button class="btn btn-sm btn-outline-primary" t-on-click="() => exportGrid('dispatch')"> <button class="btn btn-sm btn-outline-primary" t-on-click="exportDispatchGrid">
<i class="fa fa-download mr-1"></i> Export <i class="fa fa-download mr-1"></i> Export
</button> </button>
</div> </div>
@ -239,10 +253,12 @@
</div> </div>
<!-- Tab 3: Raw Material Availability --> <!-- Tab 3: Raw Material Availability -->
<div class="tab-pane fade" <div
id="rm-content" class="tab-pane fade"
role="tabpanel" id="rm-content"
aria-labelledby="rm-tab"> role="tabpanel"
aria-labelledby="rm-tab"
>
<div class="tab-card"> <div class="tab-card">
<div class="tab-header d-flex justify-content-between align-items-center"> <div class="tab-header d-flex justify-content-between align-items-center">
<div class="tab-stats"> <div class="tab-stats">
@ -275,7 +291,7 @@
<button class="btn btn-sm btn-outline-secondary" t-on-click="refreshRMAvailability"> <button class="btn btn-sm btn-outline-secondary" t-on-click="refreshRMAvailability">
<i class="fa fa-refresh mr-1"></i> Refresh <i class="fa fa-refresh mr-1"></i> Refresh
</button> </button>
<button class="btn btn-sm btn-outline-primary" t-on-click="() => exportGrid('rm')"> <button class="btn btn-sm btn-outline-primary" t-on-click="exportRMGrid">
<i class="fa fa-download mr-1"></i> Export <i class="fa fa-download mr-1"></i> Export
</button> </button>
</div> </div>
@ -297,10 +313,12 @@
</div> </div>
<!-- Tab 4: DPR - Daily Production Report --> <!-- Tab 4: DPR - Daily Production Report -->
<div class="tab-pane fade" <div
id="dpr-content" class="tab-pane fade"
role="tabpanel" id="dpr-content"
aria-labelledby="dpr-tab"> role="tabpanel"
aria-labelledby="dpr-tab"
>
<div class="tab-card"> <div class="tab-card">
<div class="tab-header d-flex justify-content-between align-items-center"> <div class="tab-header d-flex justify-content-between align-items-center">
<div class="tab-stats"> <div class="tab-stats">
@ -333,7 +351,7 @@
<button class="btn btn-sm btn-outline-secondary" t-on-click="refreshDPRData"> <button class="btn btn-sm btn-outline-secondary" t-on-click="refreshDPRData">
<i class="fa fa-refresh mr-1"></i> Refresh <i class="fa fa-refresh mr-1"></i> Refresh
</button> </button>
<button class="btn btn-sm btn-outline-primary" t-on-click="() => exportGrid('dpr')"> <button class="btn btn-sm btn-outline-primary" t-on-click="exportDPRGrid">
<i class="fa fa-download mr-1"></i> Export <i class="fa fa-download mr-1"></i> Export
</button> </button>
</div> </div>
@ -343,17 +361,6 @@
<i class="fa fa-info-circle fa-2x text-muted mb-3"></i> <i class="fa fa-info-circle fa-2x text-muted mb-3"></i>
<p class="text-muted">No DPR data available</p> <p class="text-muted">No DPR data available</p>
</div> </div>
<div class="dpr-formula-section mb-3 p-3 bg-light rounded">
<h6 class="mb-2"><i class="fa fa-calculator mr-2"></i>Formulas:</h6>
<div class="formula-items">
<div class="formula-item">
<strong>Total Order Qty</strong> = Pending Order + Present Month Order
</div>
<div class="formula-item">
<strong>Remaining to Produce</strong> = Total Order Qty Closing FG
</div>
</div>
</div>
<div t-if="state.dprData.length > 0" class="grid-container-full"> <div t-if="state.dprData.length > 0" class="grid-container-full">
<div t-ref="dprGrid" class="grid-element-full"></div> <div t-ref="dprGrid" class="grid-element-full"></div>
</div> </div>
@ -438,7 +445,7 @@
padding: 5px 10px; padding: 5px 10px;
min-width: 60px; min-width: 60px;
text-align: center; text-align: center;
color : white; color: white;
} }
.refresh-btn { .refresh-btn {
@ -762,76 +769,48 @@
} }
/* Simple Filter Styles - Right side */ /* Simple Filter Styles - Right side */
.simple-filter { .simple-filter {
margin-top: 15px; margin-top: 15px;
padding: 10px 0; padding: 10px 0;
} }
/* Position filter on right side */ /* Position filter on right side */
.col-auto.ms-auto { .col-auto.ms-auto {
margin-left: auto !important; margin-left: auto !important;
} }
.input-group-sm { .input-group-sm {
width: 160px; width: 160px;
} }
.input-group-sm .form-control { .input-group-sm .form-control {
background: white; background: white;
border: 1px solid rgba(255, 255, 255, 0.3); border: 1px solid rgba(255, 255, 255, 0.3);
color: #495057; color: #495057;
} }
.input-group-sm .input-group-text { .input-group-sm .input-group-text {
background: rgba(255, 255, 255, 0.2); background: rgba(255, 255, 255, 0.2);
border: 1px solid rgba(255, 255, 255, 0.3); border: 1px solid rgba(255, 255, 255, 0.3);
color: white; color: white;
} }
/* Hover effects */ /* Hover effects */
.input-group-sm:hover .input-group-text { .input-group-sm:hover .input-group-text {
background: rgba(255, 255, 255, 0.3); background: rgba(255, 255, 255, 0.3);
} }
/* Responsive */ /* Responsive */
@media (max-width: 768px) { @media (max-width: 768px) {
.col-auto.ms-auto { .col-auto.ms-auto {
margin-left: 0 !important; margin-left: 0 !important;
margin-top: 10px; margin-top: 10px;
} }
.simple-filter { .simple-filter {
text-align: center; text-align: center;
} }
} }
/* ParamQuery Grid Customization */
<!-- .pq-grid {-->
<!-- border: none !important;-->
<!-- }-->
<!-- .pq-grid .pq-grid-header {-->
<!-- background: #f8f9fa !important;-->
<!-- border-bottom: 2px solid #dee2e6 !important;-->
<!-- }-->
<!-- .pq-grid .pq-grid-title {-->
<!-- color: #495057 !important;-->
<!-- font-weight: 600 !important;-->
<!-- }-->
<!-- .pq-grid .pq-grid-cell {-->
<!-- border-right: 1px solid #e9ecef !important;-->
<!-- border-bottom: 1px solid #e9ecef !important;-->
<!-- }-->
<!-- .pq-grid .pq-grid-row:nth-child(even) {-->
<!-- background-color: #f8f9fa !important;-->
<!-- }-->
<!-- .pq-grid .pq-grid-row:hover {-->
<!-- background-color: #e9ecef !important;-->
<!-- }-->
</style> </style>
</t> </t>
</templates> </templates>