You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

304 lines
11 KiB

/** @odoo-module */
import { registry } from "@web/core/registry";
const { Component, onMounted, onWillUnmount, useState } = owl;
import { useService } from "@web/core/utils/hooks";
class KitchenScreenDashboard extends Component {
setup() {
super.setup();
// Services
this.action = useService("action");
this.rpc = this.env.services.rpc;
this.orm = useService("orm");
this.busService = useService("bus_service");
// Method binding
this.getCurrentShopId = this.getCurrentShopId.bind(this);
this.loadOrders = this.loadOrders.bind(this);
this.startCountdown = this.startCountdown.bind(this);
this.updateCountdownState = this.updateCountdownState.bind(this);
this.onPosOrderCreation = this.onPosOrderCreation.bind(this);
this.accept_order = this.accept_order.bind(this);
this.done_order = this.done_order.bind(this);
this.cancel_order = this.cancel_order.bind(this);
this.accept_order_line = this.accept_order_line.bind(this);
this.forceRefresh = this.forceRefresh.bind(this);
// Stage change methods
this.ready_stage = (e) => this.state.stages = 'ready';
this.waiting_stage = (e) => this.state.stages = 'waiting';
this.draft_stage = (e) => this.state.stages = 'draft';
// Initialization
this.currentShopId = this.getCurrentShopId();
this.channel = `pos_order_created_${this.currentShopId}`;
this.countdownIntervals = {};
// State management
this.state = useState({
order_details: [],
shop_id: this.currentShopId,
stages: 'draft',
draft_count: 0,
waiting_count: 0,
ready_count: 0,
lines: [],
prepare_times: [],
countdowns: {},
isLoading: false
});
// Component lifecycle
onMounted(() => {
this.busService.addChannel(this.channel);
this.busService.subscribe('notification', this.onPosOrderCreation);
this.loadOrders();
this.autoRefreshInterval = setInterval(() => {
this.loadOrders();
}, 30000);
});
onWillUnmount(() => {
this.busService.deleteChannel(this.channel);
this.busService.unsubscribe('notification', this.onPosOrderCreation);
if (this.autoRefreshInterval) {
clearInterval(this.autoRefreshInterval);
}
Object.values(this.countdownIntervals).forEach(interval => {
clearInterval(interval);
});
this.countdownIntervals = {};
});
}
getCurrentShopId() {
let session_shop_id;
if (this.props.action?.context?.default_shop_id) {
sessionStorage.setItem('shop_id', this.props.action.context.default_shop_id);
session_shop_id = this.props.action.context.default_shop_id;
} else {
session_shop_id = sessionStorage.getItem('shop_id');
}
return parseInt(session_shop_id, 10) || 0;
}
async loadOrders() {
if (this.state.isLoading) return;
try {
this.state.isLoading = true;
const result = await this.orm.call("pos.order", "get_details", [this.currentShopId]);
this.state.order_details = result.orders || [];
this.state.lines = result.order_lines || [];
const activeOrders = this.state.order_details.filter(order => {
const configMatch = Array.isArray(order.config_id) ?
order.config_id[0] === this.currentShopId :
order.config_id === this.currentShopId;
return configMatch && order.order_status !== 'cancel' && order.state !== 'cancel';
});
const productIds = [...new Set(this.state.lines.map(line => line.product_id[0]))];
if (productIds.length) {
const overTimes = await this.orm.call(
"product.product",
"search_read",
[[["id", "in", productIds]], ["id", "prepair_time_minutes"]]
);
this.state.prepare_times = overTimes.map(item => ({
...item,
prepare_time: !item.prepair_time_minutes ? "00:00:00" :
typeof item.prepair_time_minutes === 'number' ?
parseFloat(item.prepair_time_minutes.toFixed(2)) :
item.prepair_time_minutes
}));
}
this.state.draft_count = activeOrders.filter(o => o.order_status === 'draft').length;
this.state.waiting_count = activeOrders.filter(o => o.order_status === 'waiting').length;
this.state.ready_count = activeOrders.filter(o => o.order_status === 'ready').length;
activeOrders.forEach(order => {
if (order.order_status === 'waiting' && order.avg_prepare_time) {
if (!this.countdownIntervals[order.id]) {
this.startCountdown(order.id, order.avg_prepare_time);
}
} else if (order.order_status === 'ready') {
this.updateCountdownState(order.id, 0, true);
if (this.countdownIntervals[order.id]) {
clearInterval(this.countdownIntervals[order.id]);
delete this.countdownIntervals[order.id];
}
}
});
} catch (error) {
console.error("Error loading orders:", error);
} finally {
this.state.isLoading = false;
}
}
async startCountdown(orderId, timeString,config_id) {
if (this.countdownIntervals[orderId]) {
clearInterval(this.countdownIntervals[orderId]);
}
const [minutes, seconds] = timeString.toFixed(2).split('.').map(Number);
let totalSeconds = minutes * 60 + seconds;
this.updateCountdownState(orderId, totalSeconds, false);
this.countdownIntervals[orderId] = setInterval(async () => {
totalSeconds--;
this.updateCountdownState(orderId, totalSeconds, false);
if (totalSeconds <= 0) {
try {
let orderData = await this.orm.call(
'kitchen.screen',
'search_read',
[
[["pos_config_id", "=", config_id[0]]],
["is_preparation_complete"]
]
);
clearInterval(this.countdownIntervals[orderId]);
delete this.countdownIntervals[orderId];
this.updateCountdownState(orderId, 0, true);
if (orderData[0].is_preparation_complete === true){
this.done_order({ target: { value: orderId.toString() } });
}
} catch (error) {
console.error("Error fetching order data:", error);
// Handle error appropriately
}
}
}, 1000);
}
updateCountdownState(orderId, totalSeconds, isCompleted = false) {
const minutes = Math.floor(totalSeconds / 60);
const seconds = totalSeconds % 60;
this.state.countdowns = {
...this.state.countdowns,
[orderId]: {
minutes,
seconds,
isCompleted
}
};
}
onPosOrderCreation(message) {
if (!message || message.config_id !== this.currentShopId) {
return;
}
const relevantMessages = [
'pos_order_created',
'pos_order_updated',
'pos_order_paid',
'pos_order_accepted',
'pos_order_cancelled',
'pos_order_completed',
'pos_order_line_updated'
];
if ((message.res_model === "pos.order" || message.res_model === "pos.order.line") &&
relevantMessages.includes(message.message)) {
this.loadOrders();
}
}
async accept_order(e) {
const orderId = Number(e.target.value);
try {
await this.orm.call("pos.order", "order_progress_draft", [orderId]);
const order = this.state.order_details.find(o => o.id === orderId);
if (order) {
order.order_status = 'waiting';
if (order.avg_prepare_time) {
this.startCountdown(orderId, order.avg_prepare_time, order.config_id);
}
}
setTimeout(() => this.loadOrders(), 500);
} catch (error) {
console.error("Error accepting order:", error);
}
}
async done_order(e) {
const orderId = Number(e.target.value);
try {
await this.orm.call("pos.order", "order_progress_change", [orderId]);
const order = this.state.order_details.find(o => o.id === orderId);
if (order) {
order.order_status = 'ready';
this.updateCountdownState(orderId, 0, true);
if (this.countdownIntervals[orderId]) {
clearInterval(this.countdownIntervals[orderId]);
delete this.countdownIntervals[orderId];
}
}
setTimeout(() => this.loadOrders(), 500);
} catch (error) {
console.error("Error completing order:", error);
}
}
async cancel_order(e) {
const orderId = Number(e.target.value);
try {
await this.orm.call("pos.order", "order_progress_cancel", [orderId]);
const order = this.state.order_details.find(o => o.id === orderId);
if (order) {
order.order_status = 'cancel';
}
setTimeout(() => this.loadOrders(), 500);
} catch (error) {
console.error("Error cancelling order:", error);
}
}
async accept_order_line(e) {
const lineId = Number(e.target.value);
try {
await this.orm.call("pos.order.line", "order_progress_change", [lineId]);
const line = this.state.lines.find(l => l.id === lineId);
if (line) {
line.order_status = line.order_status === 'ready' ? 'waiting' : 'ready';
}
setTimeout(() => this.loadOrders(), 500);
} catch (error) {
console.error("Error updating order line:", error);
}
}
get filteredOrders() {
return this.state.order_details.filter(order => {
const configMatch = Array.isArray(order.config_id) ?
order.config_id[0] === this.currentShopId :
order.config_id === this.currentShopId;
const stageMatch = order.order_status === this.state.stages;
return configMatch && stageMatch && order.order_status !== 'cancel';
});
}
forceRefresh() {
this.loadOrders();
}
}
KitchenScreenDashboard.template = 'KitchenCustomDashBoard';
registry.category("actions").add("kitchen_custom_dashboard_tags", KitchenScreenDashboard);