Add address fields to sales invoices and implement related validations
- Introduced new columns `address`, `latitude`, and `longitude` in the `sales_invoices` table to store location details. - Updated the `SalesInvoicesService` to handle the new fields, including validation for address format and geographical coordinates. - Enhanced the `SalesInvoiceDto` and related data transfer objects to include the new fields for API requests and responses. - Added unit and e2e tests to ensure proper handling of the new fields and validate their integration within the sales invoice workflow. - Created a new migration script to apply the database schema changes for the sales invoices.
This commit is contained in:
+23
-5
@@ -14,6 +14,23 @@ import {
|
||||
} from '../src/database/schema';
|
||||
import { PRIVILEGE_ACTIONS } from '../src/modules/privileges/privilege-action';
|
||||
import { registerAndActivate } from './helpers/activate-user';
|
||||
import { DateTime } from '../src/common/value-objects/date-time/date-time';
|
||||
|
||||
const MS_PER_DAY = 86_400_000;
|
||||
|
||||
function nextMondayRange(): { from: string; to: string } {
|
||||
const start = DateTime.fromUnixMs(Math.trunc(Date.now())).startOfDay();
|
||||
const mondays: string[] = [];
|
||||
for (let offset = 0; offset < 21 && mondays.length < 2; offset += 1) {
|
||||
const day = DateTime.fromUnixMs(
|
||||
start.value + offset * MS_PER_DAY,
|
||||
).startOfDay();
|
||||
if (day.weekdayName() === 'monday') {
|
||||
mondays.push(day.format().slice(0, 10));
|
||||
}
|
||||
}
|
||||
return { from: mondays[0], to: mondays[1] };
|
||||
}
|
||||
|
||||
describe('Plans (e2e)', () => {
|
||||
let app: INestApplication<App>;
|
||||
@@ -203,14 +220,15 @@ describe('Plans (e2e)', () => {
|
||||
});
|
||||
|
||||
it('generates Monday plans, skips existing, and supports D-Day destination edits', async () => {
|
||||
const { from, to } = nextMondayRange();
|
||||
const generated = await request(app.getHttpServer())
|
||||
.post('/plans/generate')
|
||||
.set('Authorization', `Bearer ${adminAccessToken}`)
|
||||
.send({
|
||||
employeeId,
|
||||
purpose: 'sales',
|
||||
from: '2026-01-05',
|
||||
to: '2026-01-12',
|
||||
from,
|
||||
to,
|
||||
})
|
||||
.expect(201);
|
||||
expect((generated.body as { created: number }).created).toBe(2);
|
||||
@@ -222,15 +240,15 @@ describe('Plans (e2e)', () => {
|
||||
.send({
|
||||
employeeId,
|
||||
purpose: 'sales',
|
||||
from: '2026-01-05',
|
||||
to: '2026-01-12',
|
||||
from,
|
||||
to,
|
||||
})
|
||||
.expect(201);
|
||||
expect((again.body as { created: number }).created).toBe(0);
|
||||
|
||||
const list = await request(app.getHttpServer())
|
||||
.get('/plans')
|
||||
.query({ employeeId, purpose: 'sales', date: '2026-01-05' })
|
||||
.query({ employeeId, purpose: 'sales', date: from })
|
||||
.set('Authorization', `Bearer ${adminAccessToken}`)
|
||||
.expect(200);
|
||||
const planId = (list.body.data as Array<{ id: string }>)[0].id;
|
||||
|
||||
@@ -165,12 +165,18 @@ describe('Sales invoices (e2e)', () => {
|
||||
branchId,
|
||||
divisionId,
|
||||
customerId,
|
||||
address: 'Jl Sudirman 1',
|
||||
latitude: -6.2,
|
||||
longitude: 106.8,
|
||||
products: [{ productId, quantity: '2' }],
|
||||
})
|
||||
.expect(201);
|
||||
expect(created.body).toMatchObject({
|
||||
status: 'draft',
|
||||
balance: '25000.0000',
|
||||
address: 'Jl Sudirman 1',
|
||||
latitude: -6.2,
|
||||
longitude: 106.8,
|
||||
});
|
||||
expect((created.body as { code: string }).code).toMatch(/^SI-/);
|
||||
const id = (created.body as { id: string }).id;
|
||||
|
||||
@@ -138,6 +138,7 @@ describe('Sales payments (e2e)', () => {
|
||||
branchId: (branch.body as { id: string }).id,
|
||||
divisionId: (division.body as { id: string }).id,
|
||||
customerId: (customer.body as { id: string }).id,
|
||||
address: 'Jl Sudirman 1',
|
||||
products: [
|
||||
{
|
||||
productId: (product.body as { id: string }).id,
|
||||
|
||||
Reference in New Issue
Block a user