mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-09-07 08:25:43 +00:00
add company stamp upload functionality for contract signing
- Introduced StampUpload component for uploading company stamp images. - Integrated stamp upload in contract signing modal, supporting PNG and JPG formats. - Implemented validation for file type and size (max 5 MB). - Added visual feedback for drag-and-drop functionality. - Updated contract-related pages to handle duplicate contract alerts and pricing notices. - Enhanced contract expiry management with a nightly sweep service. - Added unit tests for new features and updated existing tests for contract handling.
This commit is contained in:
@@ -22,6 +22,7 @@ describe('BookingWindowService — window state machine', () => {
|
||||
expireLeftoverDayPool: jest.Mock;
|
||||
expireLeftoverExportDay: jest.Mock;
|
||||
fillFromWaitingList: jest.Mock;
|
||||
countUnacceptedForRouteDay: jest.Mock;
|
||||
};
|
||||
let trainSchedulesRepository: { findById: jest.Mock; findAll: jest.Mock };
|
||||
let trainSchedulingService: { finalizeSchedule: jest.Mock; getWindowConfig: jest.Mock };
|
||||
@@ -79,6 +80,7 @@ describe('BookingWindowService — window state machine', () => {
|
||||
expireLeftoverExportDay: jest.fn().mockResolvedValue(undefined),
|
||||
// No waiting booking fits by default, so conclude proceeds to reopen/DONE.
|
||||
fillFromWaitingList: jest.fn().mockResolvedValue(0),
|
||||
countUnacceptedForRouteDay: jest.fn().mockResolvedValue(0),
|
||||
};
|
||||
trainSchedulesRepository = {
|
||||
findById: jest.fn().mockResolvedValue(null),
|
||||
@@ -267,4 +269,87 @@ describe('BookingWindowService — window state machine', () => {
|
||||
expect(s.windowPhase).toBe('OPEN');
|
||||
expect(batch.setWindow).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
// ---- header alarm ---------------------------------------------------------
|
||||
|
||||
describe('getDocReviewAlert', () => {
|
||||
const reviewing = (over: Partial<TrainSchedule>): TrainSchedule =>
|
||||
baseSchedule({
|
||||
windowPhase: 'DOC_REVIEW',
|
||||
docReviewEndsAt: new Date('2026-07-01T01:30:00.000Z'),
|
||||
...over,
|
||||
});
|
||||
|
||||
it('returns null when nothing is under document review', async () => {
|
||||
trainSchedulesRepository.findAll.mockResolvedValue([
|
||||
baseSchedule({ windowPhase: 'OPEN' }),
|
||||
]);
|
||||
expect(await service.getDocReviewAlert()).toBeNull();
|
||||
});
|
||||
|
||||
it('returns null when every request on the route-day is decided', async () => {
|
||||
trainSchedulesRepository.findAll.mockResolvedValue([reviewing({})]);
|
||||
batch.countUnacceptedForRouteDay.mockResolvedValue(0);
|
||||
expect(await service.getDocReviewAlert()).toBeNull();
|
||||
});
|
||||
|
||||
it('reports the deadline, its own pending count and the phase length', async () => {
|
||||
trainSchedulesRepository.findAll.mockResolvedValue([reviewing({})]);
|
||||
batch.countUnacceptedForRouteDay.mockResolvedValue(3);
|
||||
|
||||
const alert = await service.getDocReviewAlert();
|
||||
|
||||
expect(alert).toMatchObject({
|
||||
scheduleId,
|
||||
originYardId: 'yard-o',
|
||||
destinationYardId: 'yard-d',
|
||||
tradeDirection: 'IMPORT',
|
||||
pendingCount: 3,
|
||||
docReviewMinutes: 30,
|
||||
docReviewEndsAt: '2026-07-01T01:30:00.000Z',
|
||||
});
|
||||
});
|
||||
|
||||
it('skips the nearest deadline when it has nothing pending', async () => {
|
||||
trainSchedulesRepository.findAll.mockResolvedValue([
|
||||
reviewing({
|
||||
id: 'sched-later',
|
||||
destinationStationId: 'yard-far',
|
||||
docReviewEndsAt: new Date('2026-07-01T02:00:00.000Z'),
|
||||
}),
|
||||
reviewing({ id: 'sched-soon' }),
|
||||
]);
|
||||
// Nearest (sched-soon, yard-d) is clear; the later route-day still isn't.
|
||||
batch.countUnacceptedForRouteDay.mockImplementation(
|
||||
async (g: { destinationYardId: string }) =>
|
||||
g.destinationYardId === 'yard-far' ? 2 : 0,
|
||||
);
|
||||
|
||||
const alert = await service.getDocReviewAlert();
|
||||
|
||||
expect(alert?.scheduleId).toBe('sched-later');
|
||||
expect(alert?.pendingCount).toBe(2);
|
||||
});
|
||||
|
||||
it('counts a route-day once when sibling trains share the review phase', async () => {
|
||||
trainSchedulesRepository.findAll.mockResolvedValue([
|
||||
reviewing({ id: 'sched-a' }),
|
||||
reviewing({ id: 'sched-b' }),
|
||||
]);
|
||||
batch.countUnacceptedForRouteDay.mockResolvedValue(4);
|
||||
|
||||
const alert = await service.getDocReviewAlert();
|
||||
|
||||
expect(alert?.pendingCount).toBe(4);
|
||||
expect(batch.countUnacceptedForRouteDay).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('ignores a phase staff already completed early', async () => {
|
||||
trainSchedulesRepository.findAll.mockResolvedValue([
|
||||
reviewing({ docReviewCompletedAt: new Date('2026-07-01T01:10:00.000Z') }),
|
||||
]);
|
||||
batch.countUnacceptedForRouteDay.mockResolvedValue(5);
|
||||
expect(await service.getDocReviewAlert()).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user