Passed
Pull Request — master (#165)
by Mathieu
02:05
created

server/src/Application/Accounting/Command/Invoice/GenerateInvoiceCommandHandler.spec.ts   A

Complexity

Total Complexity 2
Complexity/F 0

Size

Lines of Code 177
Function Count 0

Duplication

Duplicated Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 153
dl 0
loc 177
rs 10
c 0
b 0
f 0
wmc 2
mnd 2
bc 2
fnc 0
bpm 0
cpm 0
noi 0
1
import { mock, instance, when, verify, anything, deepEqual } from 'ts-mockito';
2
import { ProjectRepository } from 'src/Infrastructure/Project/Repository/ProjectRepository';
3
import { GenerateInvoiceCommandHandler } from './GenerateInvoiceCommandHandler';
4
import { GenerateInvoiceCommand } from './GenerateInvoiceCommand';
5
import { User } from 'src/Domain/HumanResource/User/User.entity';
6
import { ProjectNotFoundException } from 'src/Domain/Project/Exception/ProjectNotFoundException';
7
import { InvoiceIdGenerator } from 'src/Domain/Accounting/Generators/InvoiceIdGenerator';
8
import { Invoice, InvoiceStatus } from 'src/Domain/Accounting/Invoice.entity';
9
import { EventRepository } from 'src/Infrastructure/FairCalendar/Repository/EventRepository';
10
import { InvoiceRepository } from 'src/Infrastructure/Accounting/Repository/InvoiceRepository';
11
import { InvoiceItemRepository } from 'src/Infrastructure/Accounting/Repository/InvoiceItemRepository';
12
import { DateUtilsAdapter } from 'src/Infrastructure/Adapter/DateUtilsAdapter';
13
import { Project } from 'src/Domain/Project/Project.entity';
14
import { NoBillableEventsFoundException } from 'src/Domain/Accounting/Exception/NoBillableEventsFoundException';
15
import { InvoiceItem } from 'src/Domain/Accounting/InvoiceItem.entity';
16
17
describe('GenerateInvoiceCommandHandler', () => {
18
  let projectRepository: ProjectRepository;
19
  let eventRepository: EventRepository;
20
  let invoiceRepository: InvoiceRepository;
21
  let invoiceItemRepository: InvoiceItemRepository;
22
  let invoiceIdGenerator: InvoiceIdGenerator;
23
  let dateUtilsAdapter: DateUtilsAdapter;
24
  let handler: GenerateInvoiceCommandHandler;
25
26
  const user = mock(User);
27
  const project = mock(Project);
28
  const date = new Date('2020-11-23T17:43:14.299Z');
29
  const expiryDate = new Date('2020-11-28T17:43:14.299Z');
30
  const command = new GenerateInvoiceCommand(
31
    'a491ccc9-df7c-4fc6-8e90-db816208f689',
32
    InvoiceStatus.DRAFT,
33
    5,
34
    instance(user)
35
  );
36
37
  beforeEach(() => {
38
    projectRepository = mock(ProjectRepository);
39
    invoiceIdGenerator = mock(InvoiceIdGenerator);
40
    eventRepository = mock(EventRepository);
41
    invoiceRepository = mock(InvoiceRepository);
42
    invoiceItemRepository = mock(InvoiceItemRepository);
43
    dateUtilsAdapter = mock(DateUtilsAdapter);
44
45
    handler = new GenerateInvoiceCommandHandler(
46
      instance(projectRepository),
47
      instance(eventRepository),
48
      instance(invoiceRepository),
49
      instance(invoiceItemRepository),
50
      instance(dateUtilsAdapter),
51
      instance(invoiceIdGenerator),
52
    );
53
  });
54
55
  it('testProjectNotFound', async () => {
56
    when(
57
      projectRepository.findOneById('a491ccc9-df7c-4fc6-8e90-db816208f689')
58
    ).thenResolve(null);
59
60
    try {
61
      await handler.execute(command);
62
    } catch (e) {
63
      expect(e).toBeInstanceOf(ProjectNotFoundException);
64
      expect(e.message).toBe('crm.projects.errors.not_found');
65
      verify(
66
        projectRepository.findOneById('a491ccc9-df7c-4fc6-8e90-db816208f689')
67
      ).once();
68
      verify(invoiceIdGenerator.generate()).never();
69
      verify(eventRepository.findBillableEventsByMonthAndProject(anything(), anything())).never();
70
      verify(dateUtilsAdapter.addDaysToDate(anything(), anything())).never();
71
      verify(invoiceRepository.save(anything())).never();
72
      verify(invoiceItemRepository.save(anything())).never();
73
      verify(dateUtilsAdapter.getCurrentDate()).never();
74
    }
75
  });
76
77
  it('testNoBillableEventsFound', async () => {
78
    when(
79
      projectRepository.findOneById('a491ccc9-df7c-4fc6-8e90-db816208f689')
80
    ).thenResolve(instance(project));
81
    when(dateUtilsAdapter.getCurrentDate()).thenReturn(date);
82
    when(invoiceIdGenerator.generate()).thenResolve('FS-2020-0001');
83
    when(
84
      eventRepository.findBillableEventsByMonthAndProject(date, instance(project))
85
    ).thenResolve([]);
86
87
    try {
88
      await handler.execute(command);
89
    } catch (e) {
90
      expect(e).toBeInstanceOf(NoBillableEventsFoundException);
91
      expect(e.message).toBe('accounting.invoices.errors.no_billable_events_found');
92
      verify(
93
        projectRepository.findOneById('a491ccc9-df7c-4fc6-8e90-db816208f689')
94
      ).once();
95
      verify(invoiceIdGenerator.generate()).once();
96
      verify(
97
        eventRepository.findBillableEventsByMonthAndProject(date, instance(project))
98
      ).once();
99
      verify(dateUtilsAdapter.addDaysToDate(anything(), anything())).never();
100
      verify(dateUtilsAdapter.getCurrentDate()).once();
101
      verify(invoiceRepository.save(anything())).never();
102
      verify(invoiceItemRepository.save(anything())).never();
103
    }
104
  });
105
106
  it('testGenerateInvoice', async () => {
107
    const events = [
108
      {
109
        time_spent: '180',
110
        billable: false,
111
        task_name: 'Développement',
112
        first_name: 'Mathieu',
113
        last_name: 'MARCHOIS',
114
        amount: 60000
115
      },
116
      {
117
        time_spent: '420',
118
        billable: true,
119
        task_name: 'Architecture',
120
        first_name: 'Mathieu',
121
        last_name: 'MARCHOIS',
122
        amount: null
123
      },
124
      {
125
        time_spent: '4200',
126
        billable: true,
127
        task_name: 'Développement',
128
        first_name: 'Mathieu',
129
        last_name: 'MARCHOIS',
130
        amount: 60000
131
      }
132
    ];
133
134
    const invoice = new Invoice(
135
      'FS-2020-0001',
136
      InvoiceStatus.DRAFT,
137
      '2020-11-28T17:43:14.299Z',
138
      instance(user),
139
      instance(project)
140
    );
141
142
    const savedInvoice = mock(Invoice);
143
    when(savedInvoice.getId()).thenReturn('fc8a4cd9-31eb-4fca-814d-b30c05de485d');
144
145
    const invoiceItems = [
146
      new InvoiceItem(invoice, 'Développement - Mathieu MARCHOIS', 180, 60000, 100),
147
      new InvoiceItem(invoice, 'Architecture - Mathieu MARCHOIS', 420, 0, 0),
148
      new InvoiceItem(invoice, 'Développement - Mathieu MARCHOIS', 4200, 60000, 0),
149
    ];
150
151
    when(
152
      projectRepository.findOneById('a491ccc9-df7c-4fc6-8e90-db816208f689')
153
    ).thenResolve(instance(project));
154
    when(dateUtilsAdapter.getCurrentDate()).thenReturn(date);
155
    when(invoiceIdGenerator.generate()).thenResolve('FS-2020-0001');
156
    when(
157
      eventRepository.findBillableEventsByMonthAndProject(date, instance(project))
158
    ).thenResolve(events);
159
    when(dateUtilsAdapter.addDaysToDate(date, 5)).thenReturn(expiryDate);
160
    when(invoiceRepository.save(deepEqual(invoice))).thenResolve(instance(savedInvoice));
161
162
    expect(await handler.execute(command)).toBe('fc8a4cd9-31eb-4fca-814d-b30c05de485d');
163
164
    verify(
165
      projectRepository.findOneById('a491ccc9-df7c-4fc6-8e90-db816208f689')
166
    ).once();
167
    verify(invoiceIdGenerator.generate()).once();
168
    verify(
169
      eventRepository.findBillableEventsByMonthAndProject(date, instance(project))
170
    ).once();
171
    verify(dateUtilsAdapter.addDaysToDate(date, 5)).once();
172
    verify(dateUtilsAdapter.getCurrentDate()).once();
173
    verify(invoiceRepository.save(deepEqual(invoice))).once();
174
    verify(invoiceItemRepository.save(deepEqual(invoiceItems))).once();
175
  });
176
});
177