mirror of
https://gitlab.com/mbugroup/lti-web-client.git
synced 2026-05-20 13:32:00 +00:00
1103 lines
25 KiB
TypeScript
1103 lines
25 KiB
TypeScript
import axios from 'axios';
|
|
import { sleep } from '@/lib/helper';
|
|
import { BaseApiService } from '@/services/api/base';
|
|
import { BaseApiResponse, CreatedUser } from '@/types/api/api-general';
|
|
import { CreateExpensePayload, Expense } from '@/types/api/expense';
|
|
import { httpClient } from '@/services/http/client';
|
|
|
|
import { BaseArea } from '@/types/api/master-data/area';
|
|
import { BaseLocation, Location } from '@/types/api/master-data/location';
|
|
import { Kandang } from '@/types/api/master-data/kandang';
|
|
import { BaseSupplier, Supplier } from '@/types/api/master-data/supplier';
|
|
import { Nonstock } from '@/types/api/master-data/nonstock';
|
|
import { resourceUsage } from 'process';
|
|
|
|
// Shared base objects
|
|
const adminUser: CreatedUser = {
|
|
id: 1,
|
|
id_user: 1,
|
|
email: 'admin@example.com',
|
|
name: 'Admin User',
|
|
};
|
|
|
|
const managerAreaUser: CreatedUser = {
|
|
id: 200,
|
|
id_user: 200,
|
|
email: 'manager.area@example.com',
|
|
name: 'Manager Area',
|
|
};
|
|
|
|
const headFinanceUser: CreatedUser = {
|
|
id: 300,
|
|
id_user: 300,
|
|
email: 'head.finance@example.com',
|
|
name: 'Head Finance',
|
|
};
|
|
|
|
const financeStaffUser: CreatedUser = {
|
|
id: 400,
|
|
id_user: 400,
|
|
email: 'finance.staff@example.com',
|
|
name: 'Finance Staff',
|
|
};
|
|
|
|
const auditUser: CreatedUser = {
|
|
id: 500,
|
|
id_user: 500,
|
|
email: 'internal.audit@example.com',
|
|
name: 'Internal Audit',
|
|
};
|
|
|
|
const areaUtara: BaseArea = {
|
|
id: 1,
|
|
name: 'Area Utara',
|
|
};
|
|
|
|
const areaSelatan: BaseArea = {
|
|
id: 2,
|
|
name: 'Area Selatan',
|
|
};
|
|
|
|
const baseLocationA: BaseLocation = {
|
|
id: 1,
|
|
name: 'Singaparna',
|
|
address: 'Jl. Kebun Raya 12',
|
|
area: areaUtara,
|
|
};
|
|
|
|
const baseLocationB: BaseLocation = {
|
|
id: 2,
|
|
name: 'Cikaum',
|
|
address: 'Jl. Melati 20',
|
|
area: areaSelatan,
|
|
};
|
|
|
|
const locationA: Location = {
|
|
...baseLocationA,
|
|
created_user: adminUser,
|
|
created_at: '2025-01-01T00:00:00Z',
|
|
updated_at: '2025-01-01T00:00:00Z',
|
|
};
|
|
|
|
const locationB: Location = {
|
|
...baseLocationB,
|
|
created_user: adminUser,
|
|
created_at: '2025-01-05T00:00:00Z',
|
|
updated_at: '2025-01-05T00:00:00Z',
|
|
};
|
|
|
|
const kandangA1: Kandang = {
|
|
id: 1,
|
|
name: 'Singaparna 1',
|
|
status: 'ACTIVE',
|
|
capacity: 5000,
|
|
location: baseLocationA,
|
|
pic: {
|
|
id: 101,
|
|
id_user: 101,
|
|
email: 'abkA1@example.com',
|
|
name: 'ABK A1',
|
|
},
|
|
created_user: adminUser,
|
|
created_at: '2024-12-10T00:00:00Z',
|
|
updated_at: '2024-12-10T00:00:00Z',
|
|
};
|
|
|
|
const kandangA2: Kandang = {
|
|
id: 2,
|
|
name: 'Singaparna 2',
|
|
status: 'ACTIVE',
|
|
capacity: 4500,
|
|
location: baseLocationA,
|
|
pic: {
|
|
id: 102,
|
|
id_user: 102,
|
|
email: 'abkA2@example.com',
|
|
name: 'ABK A2',
|
|
},
|
|
created_user: adminUser,
|
|
created_at: '2024-12-12T00:00:00Z',
|
|
updated_at: '2024-12-12T00:00:00Z',
|
|
};
|
|
|
|
const kandangB1: Kandang = {
|
|
id: 21,
|
|
name: 'Kandang B1',
|
|
status: 'ACTIVE',
|
|
capacity: 3800,
|
|
location: baseLocationB,
|
|
pic: {
|
|
id: 201,
|
|
id_user: 201,
|
|
email: 'abkB1@example.com',
|
|
name: 'ABK B1',
|
|
},
|
|
created_user: adminUser,
|
|
created_at: '2024-12-15T00:00:00Z',
|
|
updated_at: '2024-12-15T00:00:00Z',
|
|
};
|
|
|
|
const baseSupplierPakan: BaseSupplier = {
|
|
id: 1,
|
|
name: 'PT CHAROEN POKPHAND INDONESIA Tbk',
|
|
alias: 'PPJ',
|
|
pic: 'Budi',
|
|
type: 'Pakan',
|
|
category: 'PAKAN',
|
|
hatchery: '-',
|
|
phone: '08121234567',
|
|
email: 'pakan@example.com',
|
|
address: 'Jl. Raya Pakan 88',
|
|
npwp: '1234567890',
|
|
account_number: '111-222-333',
|
|
due_date: 30,
|
|
balance: 5000000,
|
|
};
|
|
|
|
const baseSupplierObat: BaseSupplier = {
|
|
id: 502,
|
|
name: 'CV Obat Sehat',
|
|
alias: 'COS',
|
|
pic: 'Susi',
|
|
type: 'Obat',
|
|
category: 'OBAT',
|
|
hatchery: '-',
|
|
phone: '085612345678',
|
|
email: 'cos@example.com',
|
|
address: 'Jl. Obat Raya 10',
|
|
npwp: '987654321',
|
|
account_number: '222-333-444',
|
|
due_date: 14,
|
|
};
|
|
|
|
const supplierPakan: Supplier = {
|
|
...baseSupplierPakan,
|
|
created_user: adminUser,
|
|
created_at: '2025-01-01T00:00:00Z',
|
|
updated_at: '2025-01-01T00:00:00Z',
|
|
};
|
|
|
|
const supplierObat: Supplier = {
|
|
...baseSupplierObat,
|
|
created_user: adminUser,
|
|
created_at: '2025-01-02T00:00:00Z',
|
|
updated_at: '2025-01-02T00:00:00Z',
|
|
};
|
|
|
|
const nonstockPakanStarter: Nonstock = {
|
|
id: 3001,
|
|
name: 'Pakan Ayam Starter',
|
|
uom_id: 1,
|
|
uom: { id: 1, name: 'KG' },
|
|
suppliers: [baseSupplierPakan],
|
|
flags: ['PAKAN', 'STARTER'],
|
|
created_user: adminUser,
|
|
created_at: '2025-01-10T00:00:00Z',
|
|
updated_at: '2025-01-10T00:00:00Z',
|
|
};
|
|
|
|
const nonstockPakanFinisher: Nonstock = {
|
|
id: 3002,
|
|
name: 'Pakan Ayam Finisher',
|
|
uom_id: 1,
|
|
uom: { id: 1, name: 'KG' },
|
|
suppliers: [baseSupplierPakan],
|
|
flags: ['PAKAN', 'FINISHER'],
|
|
created_user: adminUser,
|
|
created_at: '2025-01-11T00:00:00Z',
|
|
updated_at: '2025-01-11T00:00:00Z',
|
|
};
|
|
|
|
const nonstockObat: Nonstock = {
|
|
id: 3101,
|
|
name: 'Obat Antibiotik A',
|
|
uom_id: 2,
|
|
uom: { id: 2, name: 'BOTOL' },
|
|
suppliers: [baseSupplierObat],
|
|
flags: ['OBAT'],
|
|
created_user: adminUser,
|
|
created_at: '2025-01-12T00:00:00Z',
|
|
updated_at: '2025-01-12T00:00:00Z',
|
|
};
|
|
|
|
const nonstockVitamin: Nonstock = {
|
|
id: 3102,
|
|
name: 'Vitamin Ayam B-Complex',
|
|
uom_id: 2,
|
|
uom: { id: 2, name: 'BOTOL' },
|
|
suppliers: [baseSupplierObat],
|
|
flags: ['VITAMIN'],
|
|
created_user: adminUser,
|
|
created_at: '2025-01-13T00:00:00Z',
|
|
updated_at: '2025-01-13T00:00:00Z',
|
|
};
|
|
|
|
export const DUMMY_EXPENSE: Expense[] = [
|
|
// STEP 1 - Pengajuan (OK)
|
|
{
|
|
id: 1,
|
|
reference_number: 'REF-EXP-0001',
|
|
po_number: 'PO-STEP1-OK',
|
|
created_user: adminUser,
|
|
created_at: '2025-02-10T08:00:00Z',
|
|
updated_at: '2025-02-10T08:00:00Z',
|
|
|
|
location: locationA,
|
|
transaction_date: '2025-02-10',
|
|
|
|
kandangs: [kandangA1, kandangA2],
|
|
vendor: supplierPakan,
|
|
request_documents: [
|
|
{
|
|
name: 'pengajuan_step1_ok.pdf',
|
|
url: 'https://example.com/pengajuan_step1_ok.pdf',
|
|
},
|
|
],
|
|
kandang_expenses: [
|
|
{
|
|
kandang: kandangA1,
|
|
expenses: [
|
|
{
|
|
nonstock: nonstockPakanStarter,
|
|
total_quantity: 100,
|
|
total_expense: 1500000,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
kandang: kandangA2,
|
|
expenses: [
|
|
{
|
|
nonstock: nonstockPakanFinisher,
|
|
total_quantity: 80,
|
|
total_expense: 1200000,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
nominal: 2700000,
|
|
paid: 0,
|
|
remaining_cost: 2700000,
|
|
|
|
approval: {
|
|
step_number: 1,
|
|
step_name: 'Pengajuan',
|
|
action: 'SUBMITTED',
|
|
notes: 'Pengajuan pakan untuk kandang A1 dan A2.',
|
|
action_by: adminUser,
|
|
action_at: '2025-02-10T08:05:00Z',
|
|
},
|
|
},
|
|
|
|
// STEP 1 - Pengajuan (REJECTED)
|
|
{
|
|
id: 2,
|
|
reference_number: 'REF-EXP-0002',
|
|
po_number: 'PO-STEP1-REJECT',
|
|
created_user: adminUser,
|
|
created_at: '2025-02-11T09:00:00Z',
|
|
updated_at: '2025-02-11T09:15:00Z',
|
|
|
|
location: locationA,
|
|
transaction_date: '2025-02-11',
|
|
|
|
kandangs: [kandangA1],
|
|
vendor: supplierPakan,
|
|
request_documents: [],
|
|
kandang_expenses: [
|
|
{
|
|
kandang: kandangA1,
|
|
expenses: [
|
|
{
|
|
nonstock: nonstockPakanFinisher,
|
|
total_quantity: 300,
|
|
total_expense: 4500000,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
nominal: 4500000,
|
|
paid: 0,
|
|
remaining_cost: 4500000,
|
|
|
|
approval: {
|
|
step_number: 1,
|
|
step_name: 'Pengajuan',
|
|
action: 'REJECTED',
|
|
notes: 'Jumlah terlalu besar untuk pengajuan awal.',
|
|
action_by: managerAreaUser,
|
|
action_at: '2025-02-11T09:15:00Z',
|
|
},
|
|
},
|
|
|
|
// STEP 2 - Approval Manager Area (APPROVED)
|
|
{
|
|
id: 3,
|
|
reference_number: 'REF-EXP-0003',
|
|
po_number: 'PO-STEP2-OK',
|
|
created_user: adminUser,
|
|
created_at: '2025-02-12T07:30:00Z',
|
|
updated_at: '2025-02-12T08:30:00Z',
|
|
|
|
location: locationA,
|
|
transaction_date: '2025-02-12',
|
|
|
|
kandangs: [kandangA1, kandangA2],
|
|
vendor: supplierPakan,
|
|
request_documents: [
|
|
{
|
|
name: 'pengajuan_step2_ok.pdf',
|
|
url: 'https://example.com/pengajuan_step2_ok.pdf',
|
|
},
|
|
],
|
|
kandang_expenses: [
|
|
{
|
|
kandang: kandangA1,
|
|
expenses: [
|
|
{
|
|
nonstock: nonstockPakanStarter,
|
|
total_quantity: 120,
|
|
total_expense: 1800000,
|
|
notes:
|
|
'Lorem ipsum dolor sit amet consectetur adipisicing elit. Non eveniet quos aspernatur magnam mollitia consequatur dolore natus amet libero enim?',
|
|
},
|
|
{
|
|
nonstock: nonstockPakanFinisher,
|
|
total_quantity: 50,
|
|
total_expense: 750000,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
kandang: kandangA2,
|
|
expenses: [
|
|
{
|
|
nonstock: nonstockPakanStarter,
|
|
total_quantity: 60,
|
|
total_expense: 900000,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
nominal: 3450000,
|
|
paid: 0,
|
|
remaining_cost: 3450000,
|
|
|
|
approval: {
|
|
step_number: 2,
|
|
step_name: 'Approval Manager Area',
|
|
action: 'APPROVED',
|
|
notes: 'Disetujui, kebutuhan pakan sesuai rencana.',
|
|
action_by: managerAreaUser,
|
|
action_at: '2025-02-12T08:30:00Z',
|
|
},
|
|
},
|
|
|
|
// STEP 2 - Approval Manager Area (REJECTED)
|
|
{
|
|
id: 4,
|
|
reference_number: 'REF-EXP-0004',
|
|
created_user: adminUser,
|
|
created_at: '2025-02-13T10:00:00Z',
|
|
updated_at: '2025-02-13T10:20:00Z',
|
|
|
|
location: locationB,
|
|
transaction_date: '2025-02-13',
|
|
|
|
kandangs: [kandangB1],
|
|
vendor: supplierPakan,
|
|
request_documents: [],
|
|
kandang_expenses: [
|
|
{
|
|
kandang: kandangB1,
|
|
expenses: [
|
|
{
|
|
nonstock: nonstockPakanFinisher,
|
|
total_quantity: 400,
|
|
total_expense: 6000000,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
nominal: 6000000,
|
|
paid: 0,
|
|
remaining_cost: 6000000,
|
|
|
|
approval: {
|
|
step_number: 2,
|
|
step_name: 'Approval Manager Area',
|
|
action: 'REJECTED',
|
|
notes: 'Tidak sesuai rencana kebutuhan area Selatan.',
|
|
action_by: managerAreaUser,
|
|
action_at: '2025-02-13T10:20:00Z',
|
|
},
|
|
},
|
|
|
|
// STEP 3 - Approval Finance (APPROVED)
|
|
{
|
|
id: 5,
|
|
reference_number: 'REF-EXP-0005',
|
|
po_number: 'PO-STEP3-OK',
|
|
created_user: adminUser,
|
|
created_at: '2025-02-14T09:00:00Z',
|
|
updated_at: '2025-02-14T09:30:00Z',
|
|
|
|
location: locationB,
|
|
transaction_date: '2025-02-14',
|
|
|
|
kandangs: [kandangB1],
|
|
vendor: supplierObat,
|
|
request_documents: [
|
|
{
|
|
name: 'pengajuan_step3_ok.pdf',
|
|
url: 'https://example.com/pengajuan_step3_ok.pdf',
|
|
},
|
|
],
|
|
kandang_expenses: [
|
|
{
|
|
kandang: kandangB1,
|
|
expenses: [
|
|
{
|
|
nonstock: nonstockObat,
|
|
total_quantity: 10,
|
|
total_expense: 700000,
|
|
},
|
|
{
|
|
nonstock: nonstockVitamin,
|
|
total_quantity: 5,
|
|
total_expense: 250000,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
nominal: 950000,
|
|
paid: 0,
|
|
remaining_cost: 950000,
|
|
|
|
approval: {
|
|
step_number: 3,
|
|
step_name: 'Approval Finance',
|
|
action: 'APPROVED',
|
|
notes: 'Budget tersedia untuk obat & vitamin.',
|
|
action_by: headFinanceUser,
|
|
action_at: '2025-02-14T09:30:00Z',
|
|
},
|
|
},
|
|
|
|
// STEP 3 - Approval Finance (REJECTED)
|
|
{
|
|
id: 6,
|
|
reference_number: 'REF-EXP-0006',
|
|
created_user: adminUser,
|
|
created_at: '2025-02-15T11:00:00Z',
|
|
updated_at: '2025-02-15T11:30:00Z',
|
|
|
|
location: locationB,
|
|
transaction_date: '2025-02-15',
|
|
|
|
kandangs: [kandangB1],
|
|
vendor: supplierObat,
|
|
request_documents: [],
|
|
kandang_expenses: [
|
|
{
|
|
kandang: kandangB1,
|
|
expenses: [
|
|
{
|
|
nonstock: nonstockObat,
|
|
total_quantity: 60,
|
|
total_expense: 4200000,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
nominal: 4200000,
|
|
paid: 0,
|
|
remaining_cost: 4200000,
|
|
|
|
approval: {
|
|
step_number: 3,
|
|
step_name: 'Approval Finance',
|
|
action: 'REJECTED',
|
|
notes: 'Melebihi plafon budget obat bulan ini.',
|
|
action_by: headFinanceUser,
|
|
action_at: '2025-02-15T11:30:00Z',
|
|
},
|
|
},
|
|
|
|
// STEP 4 - Realisasi (IN_PROGRESS)
|
|
{
|
|
id: 7,
|
|
reference_number: 'REF-EXP-0007',
|
|
po_number: 'PO-STEP4-OK',
|
|
created_user: adminUser,
|
|
created_at: '2025-02-16T08:00:00Z',
|
|
updated_at: '2025-02-16T12:00:00Z',
|
|
|
|
location: locationA,
|
|
transaction_date: '2025-02-16',
|
|
realization_date: '2025-02-17',
|
|
|
|
kandangs: [kandangA1, kandangA2],
|
|
vendor: supplierPakan,
|
|
request_documents: [
|
|
{
|
|
name: 'do_step4_ok.pdf',
|
|
url: 'https://example.com/do_step4_ok.pdf',
|
|
},
|
|
],
|
|
kandang_expenses: [
|
|
{
|
|
kandang: kandangA1,
|
|
expenses: [
|
|
{
|
|
nonstock: nonstockPakanStarter,
|
|
total_quantity: 70,
|
|
total_expense: 1050000,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
kandang: kandangA2,
|
|
expenses: [
|
|
{
|
|
nonstock: nonstockPakanFinisher,
|
|
total_quantity: 40,
|
|
total_expense: 600000,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
nominal: 1650000,
|
|
paid: 500000,
|
|
remaining_cost: 1150000,
|
|
|
|
approval: {
|
|
step_number: 4,
|
|
step_name: 'Realisasi',
|
|
action: 'IN_PROGRESS',
|
|
notes: 'Barang diterima, pembayaran sebagian.',
|
|
action_by: financeStaffUser,
|
|
action_at: '2025-02-16T12:00:00Z',
|
|
},
|
|
},
|
|
|
|
// STEP 4 - Realisasi (REJECTED)
|
|
{
|
|
id: 8,
|
|
reference_number: 'REF-EXP-0008',
|
|
created_user: adminUser,
|
|
created_at: '2025-02-17T09:00:00Z',
|
|
updated_at: '2025-02-17T09:45:00Z',
|
|
|
|
location: locationA,
|
|
transaction_date: '2025-02-17',
|
|
|
|
kandangs: [kandangA1],
|
|
vendor: supplierPakan,
|
|
request_documents: [
|
|
{
|
|
name: 'invoice_step4_reject.pdf',
|
|
url: 'https://example.com/invoice_step4_reject.pdf',
|
|
},
|
|
],
|
|
kandang_expenses: [
|
|
{
|
|
kandang: kandangA1,
|
|
expenses: [
|
|
{
|
|
nonstock: nonstockPakanStarter,
|
|
total_quantity: 50,
|
|
total_expense: 750000,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
nominal: 750000,
|
|
paid: 0,
|
|
remaining_cost: 750000,
|
|
|
|
approval: {
|
|
step_number: 4,
|
|
step_name: 'Realisasi',
|
|
action: 'REJECTED',
|
|
notes: 'Dokumen realisasi tidak sesuai PO.',
|
|
action_by: financeStaffUser,
|
|
action_at: '2025-02-17T09:45:00Z',
|
|
},
|
|
},
|
|
|
|
// STEP 5 - Selesai (DONE)
|
|
{
|
|
id: 9,
|
|
reference_number: 'REF-EXP-0009',
|
|
po_number: 'PO-STEP5-OK',
|
|
created_user: adminUser,
|
|
created_at: '2025-02-18T08:00:00Z',
|
|
updated_at: '2025-02-20T15:00:00Z',
|
|
|
|
location: locationB,
|
|
transaction_date: '2025-02-18',
|
|
realization_date: '2025-02-19',
|
|
|
|
kandangs: [kandangB1],
|
|
vendor: supplierObat,
|
|
request_documents: [
|
|
{
|
|
name: 'invoice_step5_ok.pdf',
|
|
url: 'https://example.com/invoice_step5_ok.pdf',
|
|
},
|
|
{
|
|
name: 'bukti_transfer_step5_ok.pdf',
|
|
url: 'https://example.com/bukti_transfer_step5_ok.pdf',
|
|
},
|
|
],
|
|
kandang_expenses: [
|
|
{
|
|
kandang: kandangB1,
|
|
expenses: [
|
|
{
|
|
nonstock: nonstockObat,
|
|
total_quantity: 20,
|
|
total_expense: 1400000,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
nominal: 1400000,
|
|
paid: 1400000,
|
|
remaining_cost: 0,
|
|
|
|
approval: {
|
|
step_number: 5,
|
|
step_name: 'Selesai',
|
|
action: 'DONE',
|
|
notes: 'Proses selesai, sudah lunas.',
|
|
action_by: financeStaffUser,
|
|
action_at: '2025-02-20T15:00:00Z',
|
|
},
|
|
},
|
|
|
|
// STEP 5 - Selesai (REJECTED by audit)
|
|
{
|
|
id: 10,
|
|
reference_number: 'REF-EXP-0010',
|
|
created_user: adminUser,
|
|
created_at: '2025-02-19T09:00:00Z',
|
|
updated_at: '2025-02-21T10:30:00Z',
|
|
|
|
location: locationA,
|
|
transaction_date: '2025-02-19',
|
|
|
|
kandangs: [kandangA1, kandangA2],
|
|
vendor: supplierPakan,
|
|
request_documents: [
|
|
{
|
|
name: 'invoice_step5_recheck.pdf',
|
|
url: 'https://example.com/invoice_step5_recheck.pdf',
|
|
},
|
|
],
|
|
kandang_expenses: [
|
|
{
|
|
kandang: kandangA1,
|
|
expenses: [
|
|
{
|
|
nonstock: nonstockPakanStarter,
|
|
total_quantity: 60,
|
|
total_expense: 900000,
|
|
},
|
|
],
|
|
},
|
|
{
|
|
kandang: kandangA2,
|
|
expenses: [
|
|
{
|
|
nonstock: nonstockPakanFinisher,
|
|
total_quantity: 40,
|
|
total_expense: 600000,
|
|
},
|
|
],
|
|
},
|
|
],
|
|
nominal: 1500000,
|
|
paid: 1500000,
|
|
remaining_cost: 0,
|
|
|
|
approval: {
|
|
step_number: 5,
|
|
step_name: 'Selesai',
|
|
action: 'REJECTED',
|
|
notes: 'Dibatalkan saat audit akhir, terdapat perbedaan kuantitas.',
|
|
action_by: auditUser,
|
|
action_at: '2025-02-21T10:30:00Z',
|
|
},
|
|
},
|
|
];
|
|
|
|
export class ExpenseApiService extends BaseApiService<
|
|
Expense,
|
|
FormData,
|
|
FormData
|
|
> {
|
|
constructor(basePath: string) {
|
|
super(basePath);
|
|
}
|
|
|
|
// TODO: remove dummy data and integrate to real API
|
|
override async getAllFetcher(
|
|
endpoint: string
|
|
): Promise<BaseApiResponse<Expense[]>> {
|
|
// return await httpClientFetcher<BaseApiResponse<T[]>>(endpoint);
|
|
|
|
await sleep(750);
|
|
|
|
return {
|
|
code: 200,
|
|
status: 'success',
|
|
message: 'Successfully get all expense data!',
|
|
meta: {
|
|
page: 1,
|
|
limit: 10,
|
|
total_pages: 1,
|
|
total_results: 8,
|
|
},
|
|
data: DUMMY_EXPENSE,
|
|
};
|
|
}
|
|
|
|
// TODO: remove this and integrate to real API
|
|
async getSingle(id: number): Promise<BaseApiResponse<Expense> | undefined> {
|
|
await sleep(1000);
|
|
|
|
return {
|
|
code: 200,
|
|
status: 'success',
|
|
message: 'Successfully get expense data!',
|
|
meta: {
|
|
page: 1,
|
|
limit: 10,
|
|
total_pages: 1,
|
|
total_results: 8,
|
|
},
|
|
data: DUMMY_EXPENSE[id - 1],
|
|
};
|
|
}
|
|
|
|
// TODO: remove this and integrate to real API
|
|
async create(
|
|
payload: FormData
|
|
): Promise<BaseApiResponse<Expense> | undefined> {
|
|
await sleep(750);
|
|
|
|
const sentPayload = new Map();
|
|
for (const pair of payload.entries()) {
|
|
sentPayload.set(pair[0], pair[1]);
|
|
}
|
|
|
|
console.log({ sentPayload });
|
|
|
|
return {
|
|
code: 200,
|
|
status: 'success',
|
|
message: 'Berhasil membuat pengajuan biaya operasional!',
|
|
data: DUMMY_EXPENSE[0],
|
|
};
|
|
}
|
|
|
|
// TODO: remove this and integrate to real API
|
|
async update(
|
|
id: number,
|
|
payload: FormData
|
|
): Promise<BaseApiResponse<Expense> | undefined> {
|
|
await sleep(750);
|
|
|
|
const sentPayload = new Map();
|
|
for (const pair of payload.entries()) {
|
|
sentPayload.set(pair[0], pair[1]);
|
|
}
|
|
|
|
console.log({ sentPayload });
|
|
|
|
return {
|
|
code: 200,
|
|
status: 'success',
|
|
message: 'Berhasil mengubah pengajuan biaya operasional!',
|
|
data: DUMMY_EXPENSE[0],
|
|
};
|
|
}
|
|
|
|
// TODO: remove this and integrate to real API
|
|
async delete(id: number): Promise<BaseApiResponse | undefined> {
|
|
await sleep(1000);
|
|
|
|
return {
|
|
code: 200,
|
|
status: 'success',
|
|
message: 'Successfully delete expense data!',
|
|
data: {
|
|
id,
|
|
},
|
|
};
|
|
}
|
|
|
|
// TODO: remove this and integrate to real API
|
|
async uploadRequestDocuments(
|
|
id: number,
|
|
files: File[]
|
|
): Promise<BaseApiResponse | undefined> {
|
|
try {
|
|
// const requestDocumentsFormData = new FormData();
|
|
|
|
// // request_documents (array of File)
|
|
// files.forEach((file, index) => {
|
|
// requestDocumentsFormData.append(`request_documents[${index}]`, file);
|
|
// });
|
|
|
|
// const uploadRequestDocumentsRes = await httpClient<BaseApiResponse>(
|
|
// this.basePath,
|
|
// {
|
|
// method: 'POST',
|
|
// body: requestDocumentsFormData,
|
|
// }
|
|
// );
|
|
|
|
// return uploadRequestDocumentsRes;
|
|
|
|
await sleep(1000);
|
|
|
|
return {
|
|
code: 200,
|
|
status: 'success',
|
|
message: 'Berhasil menambahkan dokumen pengajuan!',
|
|
data: null,
|
|
};
|
|
} catch (error) {
|
|
if (axios.isAxiosError<BaseApiResponse>(error)) {
|
|
return error.response?.data;
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
// TODO: integrate to real API
|
|
async approve(
|
|
id: number,
|
|
notes?: string
|
|
): Promise<BaseApiResponse<Expense> | undefined> {
|
|
try {
|
|
// const approveRes = await httpClient<BaseApiResponse<Expense>>(
|
|
// `${this.basePath}/approvals`,
|
|
// {
|
|
// method: 'POST',
|
|
// body: {
|
|
// action: 'APPROVED',
|
|
// approvable_ids: [id],
|
|
// notes: notes,
|
|
// },
|
|
// }
|
|
// );
|
|
//
|
|
// return approveRes;
|
|
|
|
await sleep(1000);
|
|
|
|
return {
|
|
code: 200,
|
|
status: 'success',
|
|
message: 'Successfully approve expense data!',
|
|
meta: {
|
|
page: 1,
|
|
limit: 10,
|
|
total_pages: 1,
|
|
total_results: 1,
|
|
},
|
|
data: DUMMY_EXPENSE[id - 1],
|
|
};
|
|
} catch (error) {
|
|
if (axios.isAxiosError<BaseApiResponse<Expense>>(error)) {
|
|
return error.response?.data;
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
// TODO: integrate to real API
|
|
async bulkApprove(
|
|
ids: number[],
|
|
notes?: string
|
|
): Promise<BaseApiResponse<Expense> | undefined> {
|
|
try {
|
|
// const approveRes = await httpClient<BaseApiResponse<Expense>>(
|
|
// `${this.basePath}/approvals`,
|
|
// {
|
|
// method: 'POST',
|
|
// body: {
|
|
// action: 'APPROVED',
|
|
// approvable_ids: ids,
|
|
// notes: notes,
|
|
// },
|
|
// }
|
|
// );
|
|
//
|
|
// return approveRes;
|
|
|
|
await sleep(1000);
|
|
|
|
return {
|
|
code: 200,
|
|
status: 'success',
|
|
message: 'Successfully bulk approve expense data!',
|
|
meta: {
|
|
page: 1,
|
|
limit: 10,
|
|
total_pages: 1,
|
|
total_results: 1,
|
|
},
|
|
data: DUMMY_EXPENSE[ids[0] - 1],
|
|
};
|
|
} catch (error) {
|
|
if (axios.isAxiosError<BaseApiResponse<Expense>>(error)) {
|
|
return error.response?.data;
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
// TODO: integrate to real API
|
|
async reject(
|
|
id: number,
|
|
notes?: string
|
|
): Promise<BaseApiResponse<Expense> | undefined> {
|
|
try {
|
|
// const rejectRes = await httpClient<BaseApiResponse<Expense>>(
|
|
// `${this.basePath}/approvals`,
|
|
// {
|
|
// method: 'POST',
|
|
// body: {
|
|
// action: 'REJECTED',
|
|
// approvable_ids: [id],
|
|
// notes: notes,
|
|
// },
|
|
// }
|
|
// );
|
|
//
|
|
// return rejectRes;
|
|
|
|
await sleep(1000);
|
|
|
|
return {
|
|
code: 200,
|
|
status: 'success',
|
|
message: 'Successfully reject expense data!',
|
|
meta: {
|
|
page: 1,
|
|
limit: 10,
|
|
total_pages: 1,
|
|
total_results: 1,
|
|
},
|
|
data: DUMMY_EXPENSE[id - 1],
|
|
};
|
|
} catch (error) {
|
|
if (axios.isAxiosError<BaseApiResponse<Expense>>(error)) {
|
|
return error.response?.data;
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
// TODO: integrate to real API
|
|
async bulkReject(
|
|
ids: number[],
|
|
notes?: string
|
|
): Promise<BaseApiResponse<Expense> | undefined> {
|
|
try {
|
|
// const rejectRes = await httpClient<BaseApiResponse<Expense>>(
|
|
// `${this.basePath}/approvals`,
|
|
// {
|
|
// method: 'POST',
|
|
// body: {
|
|
// action: 'REJECTED',
|
|
// approvable_ids: ids,
|
|
// notes: notes,
|
|
// },
|
|
// }
|
|
// );
|
|
//
|
|
// return rejectRes;
|
|
|
|
await sleep(1000);
|
|
|
|
return {
|
|
code: 200,
|
|
status: 'success',
|
|
message: 'Successfully bulk reject expense data!',
|
|
meta: {
|
|
page: 1,
|
|
limit: 10,
|
|
total_pages: 1,
|
|
total_results: 1,
|
|
},
|
|
data: DUMMY_EXPENSE[ids[0] - 1],
|
|
};
|
|
} catch (error) {
|
|
if (axios.isAxiosError<BaseApiResponse<Expense>>(error)) {
|
|
return error.response?.data;
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
convertPayloadToFormData = (payload: CreateExpensePayload) => {
|
|
const formData = new FormData();
|
|
|
|
formData.append('locationId', String(payload.locationId));
|
|
formData.append('transaction_date', payload.transaction_date);
|
|
formData.append('vendorId', String(payload.vendorId));
|
|
|
|
// kandangIds (array)
|
|
payload.kandangIds.forEach((id, index) => {
|
|
formData.append(`kandangIds[${index}]`, String(id));
|
|
});
|
|
|
|
// request_documents (array of File)
|
|
payload.request_documents.forEach((file, index) => {
|
|
formData.append(`request_documents[${index}]`, file);
|
|
});
|
|
|
|
// kandang_expenses (nested array)
|
|
payload.kandang_expenses.forEach((kandangExpense, kandangIndex) => {
|
|
formData.append(
|
|
`kandang_expenses[${kandangIndex}][kandangId]`,
|
|
String(kandangExpense.kandangId)
|
|
);
|
|
|
|
kandangExpense.expenses.forEach((expenseItem, expenseIndex) => {
|
|
formData.append(
|
|
`kandang_expenses[${kandangIndex}][expenses][${expenseIndex}][nonstockId]`,
|
|
String(expenseItem.nonstockId)
|
|
);
|
|
formData.append(
|
|
`kandang_expenses[${kandangIndex}][expenses][${expenseIndex}][total_quantity]`,
|
|
String(expenseItem.total_quantity)
|
|
);
|
|
formData.append(
|
|
`kandang_expenses[${kandangIndex}][expenses][${expenseIndex}][total_expense]`,
|
|
String(expenseItem.total_expense)
|
|
);
|
|
formData.append(
|
|
`kandang_expenses[${kandangIndex}][expenses][${expenseIndex}][notes]`,
|
|
expenseItem.notes ?? ''
|
|
);
|
|
});
|
|
});
|
|
|
|
return formData;
|
|
};
|
|
}
|
|
|
|
export const ExpenseApi = new ExpenseApiService('/expense');
|