feat: add history view for recent expense submissions to kiosk dashboard

This commit is contained in:
Suherdy Yacob 2026-04-21 11:48:23 +07:00
parent a981456e93
commit 07b70336c6
3 changed files with 81 additions and 0 deletions

View File

@ -79,6 +79,37 @@ class HrExpenseKioskController(http.Controller):
return [] return []
return request.env['hr.expense.realization'].sudo().get_pending_realizations(employee_id) return request.env['hr.expense.realization'].sudo().get_pending_realizations(employee_id)
@http.route('/hr_expense/kiosk_get_submitted/<string:token>', type='json', auth='public')
def get_submitted(self, token, employee_id):
""" Returns submitted expenses for the employee. """
if not self._check_token(token):
return []
expenses = request.env['hr.expense'].sudo().search([
('employee_id', '=', employee_id),
('state', 'not in', ['draft', 'refused'])
], order='date desc, id desc', limit=15)
result = []
state_selection = dict(request.env['hr.expense']._fields['state']._description_selection(request.env))
# Get payment state labels from account.move if possible
payment_selection = dict(request.env['account.move']._fields['payment_state']._description_selection(request.env))
for exp in expenses:
payment_state = exp.account_move_id.payment_state if exp.account_move_id else 'not_paid'
result.append({
'id': exp.id,
'name': exp.name,
'sequences': exp.sequence_name or '',
'date': exp.date.strftime('%Y-%m-%d') if exp.date else '',
'total_amount': exp.currency_id.symbol + " " + "{:,.2f}".format(exp.total_amount),
'state': state_selection.get(exp.state),
'state_raw': exp.state,
'payment_status': payment_selection.get(payment_state, _("Not Paid")),
'payment_state_raw': payment_state,
})
return result
@http.route('/hr_expense/kiosk_submit_realization/<string:token>', type='json', auth='public') @http.route('/hr_expense/kiosk_submit_realization/<string:token>', type='json', auth='public')
def submit_realization(self, token, employee_id, expense_id, lines=None): def submit_realization(self, token, employee_id, expense_id, lines=None):
""" Creates a realization report from the kiosk. """ """ Creates a realization report from the kiosk. """

View File

@ -20,6 +20,7 @@ class ExpenseKioskApp extends Component {
selectedCategory: null, selectedCategory: null,
enteredPin: "", enteredPin: "",
pendingRealizations: [], pendingRealizations: [],
submittedExpenses: [],
selectedAction: null, selectedAction: null,
selectedPaymentMode: null, selectedPaymentMode: null,
selectedExpense: null, selectedExpense: null,
@ -88,6 +89,7 @@ class ExpenseKioskApp extends Component {
if (result.status === 'ok') { if (result.status === 'ok') {
await this.loadPendingRealizations(); await this.loadPendingRealizations();
await this.loadSubmittedExpenses();
this.state.screen = 'action_selection'; this.state.screen = 'action_selection';
} else { } else {
this.notification.add(result.message, { type: 'danger' }); this.notification.add(result.message, { type: 'danger' });
@ -102,6 +104,13 @@ class ExpenseKioskApp extends Component {
this.state.pendingRealizations = data; this.state.pendingRealizations = data;
} }
async loadSubmittedExpenses() {
const data = await this.rpc(`/hr_expense/kiosk_get_submitted/${this.token}`, {
employee_id: this.state.selectedEmployee.id,
});
this.state.submittedExpenses = data;
}
// Action Selection // Action Selection
selectAction(action) { selectAction(action) {
this.state.selectedAction = action; this.state.selectedAction = action;
@ -237,6 +246,8 @@ class ExpenseKioskApp extends Component {
if (result.status === 'ok') { if (result.status === 'ok') {
this.state.screen = 'success'; this.state.screen = 'success';
await this.loadPendingRealizations();
await this.loadSubmittedExpenses();
setTimeout(() => { setTimeout(() => {
this.backToSelection(); this.backToSelection();
}, 3000); }, 3000);

View File

@ -79,6 +79,45 @@
<p class="text-muted small">Submit a new reimbursement request</p> <p class="text-muted small">Submit a new reimbursement request</p>
</div> </div>
</div> </div>
<!-- Recent Submissions List -->
<div class="mt-5 w-100 px-3" t-if="state.submittedExpenses.length > 0">
<h4 class="mb-3 text-start"><i class="fa fa-history me-2"></i>Your Recent Submissions</h4>
<div class="table-responsive rounded shadow-sm bg-white">
<table class="table table-hover align-middle mb-0">
<thead class="table-light">
<tr>
<th class="ps-3">Reference / Description</th>
<th>Date</th>
<th class="text-end">Total</th>
<th class="text-center">Status</th>
<th class="text-center pe-3">Payment</th>
</tr>
</thead>
<tbody>
<t t-foreach="state.submittedExpenses" t-as="exp" t-key="exp.id">
<tr>
<td class="ps-3">
<div class="fw-bold text-primary" t-esc="exp.sequences"/>
<div class="small text-muted" t-esc="exp.name"/>
</td>
<td t-esc="exp.date"/>
<td class="text-end fw-bold" t-esc="exp.total_amount"/>
<td class="text-center">
<span t-attf-class="badge rounded-pill #{['approved', 'paid', 'posted'].includes(exp.state_raw) ? 'bg-success' : (['submitted', 'reported'].includes(exp.state_raw) ? 'bg-info' : (exp.state_raw === 'refused' ? 'bg-danger' : 'bg-warning'))}" t-esc="exp.state"/>
</td>
<td class="text-center pe-3">
<span t-attf-class="badge rounded-pill #{exp.payment_state_raw === 'paid' ? 'bg-success' : (exp.payment_state_raw === 'in_payment' ? 'bg-info' : (exp.payment_state_raw === 'not_paid' ? 'bg-secondary' : 'bg-warning'))}" t-esc="exp.payment_status"/>
</td>
</tr>
</t>
</tbody>
</table>
</div>
<div class="text-muted small mt-2 text-start">
<i class="fa fa-info-circle me-1"></i> Showing your last 15 submissions.
</div>
</div>
</div> </div>
<!-- PAYMENT MODE SELECTION --> <!-- PAYMENT MODE SELECTION -->