- New 'pending' status allows tasks to be created without a schedule, acting as a queue for unscheduled work that gets assigned later - Pending group appears in the Delivery Map sidebar with amber color - Other modules can create tasks in pending state for scheduling - scheduled_date no longer required (null for pending tasks) - New Pending Tasks menu item under Field Service - Pending filter added to search view Co-authored-by: Cursor <cursoragent@cursor.com>
137 lines
4.3 KiB
JavaScript
137 lines
4.3 KiB
JavaScript
/** @odoo-module **/
|
|
|
|
import { _t } from '@web/core/l10n/translation';
|
|
import { rpc } from '@web/core/network/rpc';
|
|
import { Component, useState } from '@odoo/owl';
|
|
|
|
export class TerminalPaymentWidget extends Component {
|
|
static template = 'fusion_poynt.TerminalPaymentWidget';
|
|
static props = {
|
|
providerId: { type: Number },
|
|
amount: { type: Number },
|
|
currency: { type: String },
|
|
reference: { type: String },
|
|
orderId: { type: String, optional: true },
|
|
onComplete: { type: Function, optional: true },
|
|
onError: { type: Function, optional: true },
|
|
};
|
|
|
|
setup() {
|
|
this.state = useState({
|
|
terminals: [],
|
|
selectedTerminalId: null,
|
|
loading: false,
|
|
polling: false,
|
|
status: '',
|
|
message: '',
|
|
});
|
|
this._loadTerminals();
|
|
}
|
|
|
|
async _loadTerminals() {
|
|
this.state.loading = true;
|
|
try {
|
|
const result = await rpc('/payment/poynt/terminals', {
|
|
provider_id: this.props.providerId,
|
|
});
|
|
this.state.terminals = result || [];
|
|
if (this.state.terminals.length > 0) {
|
|
this.state.selectedTerminalId = this.state.terminals[0].id;
|
|
}
|
|
} catch {
|
|
this.state.message = _t('Failed to load terminal devices.');
|
|
} finally {
|
|
this.state.loading = false;
|
|
}
|
|
}
|
|
|
|
onTerminalChange(ev) {
|
|
this.state.selectedTerminalId = parseInt(ev.target.value);
|
|
}
|
|
|
|
async onSendToTerminal() {
|
|
if (!this.state.selectedTerminalId) {
|
|
this.state.message = _t('Please select a terminal.');
|
|
return;
|
|
}
|
|
|
|
this.state.loading = true;
|
|
this.state.message = '';
|
|
|
|
try {
|
|
const result = await rpc('/payment/poynt/send_to_terminal', {
|
|
reference: this.props.reference,
|
|
terminal_id: this.state.selectedTerminalId,
|
|
poynt_order_id: this.props.orderId || '',
|
|
});
|
|
|
|
if (result.error) {
|
|
this.state.message = result.error;
|
|
this.state.loading = false;
|
|
if (this.props.onError) {
|
|
this.props.onError(result.error);
|
|
}
|
|
return;
|
|
}
|
|
|
|
this.state.polling = true;
|
|
this.state.status = _t('Waiting for payment on terminal...');
|
|
this._pollStatus(0);
|
|
} catch (error) {
|
|
this.state.message = error.message || _t('Failed to send payment to terminal.');
|
|
this.state.loading = false;
|
|
if (this.props.onError) {
|
|
this.props.onError(this.state.message);
|
|
}
|
|
}
|
|
}
|
|
|
|
async _pollStatus(attempt) {
|
|
const maxAttempts = 60;
|
|
const pollInterval = 3000;
|
|
|
|
if (attempt >= maxAttempts) {
|
|
this.state.polling = false;
|
|
this.state.loading = false;
|
|
this.state.message = _t('Payment timed out. Please check the terminal.');
|
|
if (this.props.onError) {
|
|
this.props.onError(this.state.message);
|
|
}
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const result = await rpc('/payment/poynt/terminal_status', {
|
|
reference: this.props.reference,
|
|
terminal_id: this.state.selectedTerminalId,
|
|
});
|
|
|
|
if (result.status === 'CAPTURED' || result.status === 'AUTHORIZED') {
|
|
this.state.polling = false;
|
|
this.state.loading = false;
|
|
this.state.status = _t('Payment completed!');
|
|
if (this.props.onComplete) {
|
|
this.props.onComplete(result);
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (result.status === 'DECLINED' || result.status === 'FAILED') {
|
|
this.state.polling = false;
|
|
this.state.loading = false;
|
|
this.state.message = _t('Payment was declined.');
|
|
if (this.props.onError) {
|
|
this.props.onError(this.state.message);
|
|
}
|
|
return;
|
|
}
|
|
|
|
this.state.status = _t('Status: ') + (result.status || _t('Pending'));
|
|
} catch {
|
|
this.state.status = _t('Checking...');
|
|
}
|
|
|
|
setTimeout(() => this._pollStatus(attempt + 1), pollInterval);
|
|
}
|
|
}
|