@acontplus/ng-customer 1.0.9 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @acontplus/ng-customer
2
2
 
3
- Angular customer management library for AcontPlus applications, providing components and services for customer CRUD operations following clean architecture principles.
3
+ Angular customer management library following clean architecture principles. Provides comprehensive customer CRUD operations with Angular Material UI components, domain-driven design patterns, and external SRI integration.
4
4
 
5
5
  ## Installation
6
6
 
@@ -10,42 +10,382 @@ npm install @acontplus/ng-customer
10
10
 
11
11
  ## Features
12
12
 
13
- - **Customer Components**: Customer card and add/edit components built with Angular Material
14
- - **Clean Architecture**: Organized in domain, application, infrastructure, and presentation layers
15
- - **Domain Models**: Customer entities and value objects
16
- - **Use Cases**: Business logic for customer operations (create, read, update, delete)
17
- - **Repositories**: Data access abstractions with concrete implementations
18
- - **DTOs and Mappers**: Data transfer objects and mapping utilities
19
- - **TypeScript Support**: Full type safety with comprehensive TypeScript definitions
20
- - **Angular Material Integration**: Consistent UI components
13
+ - **Clean Architecture**: Domain, Application, Infrastructure, and UI layers
14
+ - **Customer Components**: Card display and add/edit form components
15
+ - **SRI Integration**: External customer data validation and retrieval
16
+ - **Use Cases**: Business logic for customer operations (CRUD)
17
+ - **BaseRepository Pattern**: Data access abstractions with HTTP implementations
18
+ - **DTOs and Mappers**: Data transformation and mapping utilities
19
+ - **Form Validation**: Comprehensive validation including Ecuadorian ID/RUC
20
+ - **Angular Material**: Consistent Material Design components
21
21
 
22
- ## Components
22
+ ## Quick Start
23
23
 
24
- ### Customer Card
24
+ ### 1. Import Components
25
25
 
26
- Display component for showing customer information in a card format.
26
+ ```typescript
27
+ import { CustomerCard, CustomerAddEditComponent } from '@acontplus/ng-customer';
28
+
29
+ @Component({
30
+ selector: 'app-customers',
31
+ imports: [CustomerCard],
32
+ template: `
33
+ <acp-customer-card
34
+ [customer]="customer"
35
+ (editCustomer)="onEdit($event)"
36
+ (deleteCustomer)="onDelete($event)"
37
+ >
38
+ </acp-customer-card>
39
+ `,
40
+ })
41
+ export class CustomersComponent {
42
+ customer: CustomerListItemDto = {
43
+ idCliente: 1,
44
+ businessName: 'Acme Corp',
45
+ email: 'contact@acme.com',
46
+ phone: '123-456-7890',
47
+ };
48
+
49
+ onEdit(customer: CustomerListItemDto) {
50
+ // Handle edit action
51
+ }
52
+
53
+ onDelete(customer: CustomerListItemDto) {
54
+ // Handle delete action
55
+ }
56
+ }
57
+ ```
58
+
59
+ ### 2. Customer Add/Edit Dialog
60
+
61
+ ```typescript
62
+ import { MatDialog } from '@angular/material/dialog';
63
+ import { CustomerAddEditComponent } from '@acontplus/ng-customer';
64
+
65
+ @Component({...})
66
+ export class CustomerListComponent {
67
+ constructor(private dialog: MatDialog) {}
68
+
69
+ openCustomerDialog(customer?: any) {
70
+ const dialogRef = this.dialog.open(CustomerAddEditComponent, {
71
+ width: '800px',
72
+ data: {
73
+ id: customer?.id || 0,
74
+ descripcion: customer ? 'Editar Cliente' : 'Nuevo Cliente',
75
+ data: customer || {}
76
+ }
77
+ });
78
+
79
+ dialogRef.afterClosed().subscribe(result => {
80
+ if (result) {
81
+ // Handle successful save
82
+ console.log('Customer saved:', result);
83
+ }
84
+ });
85
+ }
86
+ }
87
+ ```
88
+
89
+ ## Architecture Layers
90
+
91
+ ### Domain Layer
92
+
93
+ **Entities:**
94
+
95
+ ```typescript
96
+ // Customer entity with business logic
97
+ export class Customer {
98
+ constructor(
99
+ public readonly id: number,
100
+ public readonly businessName: string,
101
+ public readonly identificationNumber: string,
102
+ public readonly email?: string,
103
+ ) {}
104
+
105
+ isValidForCredit(): boolean {
106
+ // Business logic for credit validation
107
+ return this.identificationNumber.length >= 10;
108
+ }
109
+ }
110
+
111
+ // External customer entity for SRI integration
112
+ export class CustomerExternal {
113
+ constructor(
114
+ public readonly idCard: string,
115
+ public readonly businessName: string,
116
+ public readonly address: string,
117
+ ) {}
118
+ }
119
+ ```
120
+
121
+ **BaseRepository Interfaces:**
122
+
123
+ ```typescript
124
+ export interface CustomerRepository {
125
+ getById(id: number): Promise<Customer>;
126
+ create(customer: CreateCustomerDto): Promise<Customer>;
127
+ update(id: number, customer: UpdateCustomerDto): Promise<Customer>;
128
+ delete(id: number): Promise<void>;
129
+ checkExistence(identificationNumber: string): Promise<boolean>;
130
+ }
131
+
132
+ export interface CustomerExternalRepository {
133
+ getById(identificationNumber: string): Promise<CustomerExternal>;
134
+ }
135
+ ```
136
+
137
+ ### Application Layer
138
+
139
+ **Use Cases:**
140
+
141
+ ```typescript
142
+ // Customer business operations
143
+ export class CustomerUseCase {
144
+ constructor(private repository: CustomerRepository) {}
145
+
146
+ async create(dto: CreateCustomerDto): Promise<ApiResponse<Customer>> {
147
+ // Business validation
148
+ if (!this.isValidIdentification(dto.numeroIdentificacion)) {
149
+ throw new Error('Invalid identification number');
150
+ }
151
+
152
+ return await this.repository.create(dto);
153
+ }
154
+
155
+ async getFormData(): Promise<ApiResponse<FormData>> {
156
+ // Load form dropdown data
157
+ return await this.repository.getFormData();
158
+ }
159
+
160
+ private isValidIdentification(id: string): boolean {
161
+ // Ecuadorian ID/RUC validation logic
162
+ return id.length >= 10;
163
+ }
164
+ }
165
+
166
+ // External SRI integration
167
+ export class CustomerExternalUseCase {
168
+ constructor(private repository: CustomerExternalRepository) {}
169
+
170
+ async getById(identificationNumber: string): Promise<ApiResponse<CustomerExternal>> {
171
+ return await this.repository.getById(identificationNumber);
172
+ }
173
+ }
174
+ ```
175
+
176
+ ### Infrastructure Layer
177
+
178
+ **DTOs:**
179
+
180
+ ```typescript
181
+ export interface CustomerListItemDto {
182
+ idCliente: number;
183
+ businessName: string;
184
+ email?: string;
185
+ phone?: string;
186
+ identificationNumber?: string;
187
+ }
27
188
 
28
- ### Customer Add/Edit
189
+ export interface CreateCustomerDto {
190
+ nombreFiscal: string;
191
+ nombreComercial?: string;
192
+ numeroIdentificacion: string;
193
+ idTipoIdentificacion: number;
194
+ direccion: string;
195
+ correo?: string;
196
+ telefono?: string;
197
+ }
198
+ ```
199
+
200
+ **HTTP Repositories:**
201
+
202
+ ```typescript
203
+ export class CustomerHttpRepository implements CustomerRepository {
204
+ constructor(private http: HttpClient) {}
205
+
206
+ async getById(id: number): Promise<Customer> {
207
+ const response = await firstValueFrom(
208
+ this.http.get<ApiResponse<CustomerDto>>(`/api/customers/${id}`),
209
+ );
210
+ return CustomerMapper.toDomain(response.data);
211
+ }
212
+
213
+ async create(dto: CreateCustomerDto): Promise<Customer> {
214
+ const response = await firstValueFrom(
215
+ this.http.post<ApiResponse<CustomerDto>>('/api/customers', dto),
216
+ );
217
+ return CustomerMapper.toDomain(response.data);
218
+ }
219
+ }
220
+ ```
221
+
222
+ **Mappers:**
223
+
224
+ ```typescript
225
+ export class CustomerMapper {
226
+ static toDomain(dto: CustomerDto): Customer {
227
+ return new Customer(dto.idCliente, dto.nombreFiscal, dto.numeroIdentificacion, dto.correo);
228
+ }
229
+
230
+ static toDto(customer: Customer): CustomerDto {
231
+ return {
232
+ idCliente: customer.id,
233
+ nombreFiscal: customer.businessName,
234
+ numeroIdentificacion: customer.identificationNumber,
235
+ correo: customer.email,
236
+ };
237
+ }
238
+ }
239
+ ```
240
+
241
+ ### UI Layer
242
+
243
+ **Customer Card Component:**
244
+
245
+ ```typescript
246
+ @Component({
247
+ selector: 'acp-customer-card',
248
+ template: `
249
+ <mat-card>
250
+ <mat-card-header>
251
+ <div mat-card-avatar class="customer-avatar">
252
+ {{ getLogoSliceBusinessName() }}
253
+ </div>
254
+ <mat-card-title>{{ customer().businessName }}</mat-card-title>
255
+ <mat-card-subtitle>{{ customer().email }}</mat-card-subtitle>
256
+ </mat-card-header>
257
+
258
+ <mat-card-content>
259
+ <p><strong>ID:</strong> {{ customer().identificationNumber }}</p>
260
+ <p><strong>Phone:</strong> {{ customer().phone }}</p>
261
+ </mat-card-content>
262
+
263
+ <mat-card-actions>
264
+ <button mat-button (click)="onEditClick()">Edit</button>
265
+ <button mat-button color="warn" (click)="onDeleteClick()">Delete</button>
266
+ </mat-card-actions>
267
+ </mat-card>
268
+ `,
269
+ })
270
+ export class CustomerCard {
271
+ customer = input.required<CustomerListItemDto>();
272
+ editCustomer = output<CustomerListItemDto>();
273
+ deleteCustomer = output<CustomerListItemDto>();
274
+
275
+ getLogoSliceBusinessName = computed(() => {
276
+ return this.customer()?.businessName?.slice(0, 1) || '?';
277
+ });
278
+ }
279
+ ```
29
280
 
30
- Form component for creating and editing customer details.
281
+ ## Advanced Features
31
282
 
32
- ## Architecture
283
+ ### SRI Integration
33
284
 
34
- The library follows Clean Architecture principles:
285
+ Automatic customer data retrieval from Ecuador's SRI (Tax Service):
35
286
 
36
- - **Domain**: Core business logic, entities, and repository interfaces
37
- - **Application**: Use cases and application services
38
- - **Infrastructure**: Repository implementations, DTOs, mappers, and external integrations
39
- - **UI**: Angular components and presentation logic
287
+ ```typescript
288
+ // Automatic SRI lookup on ID number input
289
+ onKeyDownGovernmentId() {
290
+ const identificationNumber = this.customerForm.get('numeroIdentificacion')?.value;
291
+ const identificationType = this.customerForm.get('idTipoIdentificacion')?.value;
292
+
293
+ if (identificationNumber && identificationType) {
294
+ this.customerExternalUseCase.getById(identificationNumber)
295
+ .subscribe(response => {
296
+ if (response.success) {
297
+ // Auto-fill form with SRI data
298
+ this.customerForm.patchValue({
299
+ nombreFiscal: response.data.businessName,
300
+ direccion: response.data.address,
301
+ validationSri: true
302
+ });
303
+ }
304
+ });
305
+ }
306
+ }
307
+ ```
308
+
309
+ ### Form Validation
310
+
311
+ Comprehensive validation including Ecuadorian ID/RUC:
312
+
313
+ ```typescript
314
+ // Custom validator for RUC format (must end with 001)
315
+ endsWith001Validator(control: AbstractControl): ValidationErrors | null {
316
+ const value = control.value;
317
+ if (value && value.length >= 3 && !value.endsWith('001')) {
318
+ return { endsWith001: true };
319
+ }
320
+ return null;
321
+ }
322
+
323
+ // Dynamic validation based on identification type
324
+ updateFormControlNumeroIdentificacion(codigoSri: string): void {
325
+ const idNumberControl = this.customerForm.get('numeroIdentificacion');
326
+
327
+ if (codigoSri === SRI_DOCUMENT_TYPE.CEDULA) {
328
+ idNumberControl?.setValidators([
329
+ Validators.required,
330
+ Validators.pattern(/^\d+$/),
331
+ Validators.minLength(10),
332
+ Validators.maxLength(10)
333
+ ]);
334
+ } else if (codigoSri === SRI_DOCUMENT_TYPE.RUC) {
335
+ idNumberControl?.setValidators([
336
+ Validators.required,
337
+ Validators.pattern(/^\d+$/),
338
+ Validators.minLength(13),
339
+ Validators.maxLength(13),
340
+ this.endsWith001Validator
341
+ ]);
342
+ }
343
+
344
+ idNumberControl?.updateValueAndValidity();
345
+ }
346
+ ```
40
347
 
41
- ## Usage
348
+ ### Multi-Contact Support
42
349
 
43
- Import the components and services you need:
350
+ Handle multiple emails, phones, and license plates:
44
351
 
45
352
  ```typescript
46
- import { CustomerCardComponent, CustomerAddEditComponent } from '@acontplus/ng-customer';
353
+ // Arrays for multiple contact methods
354
+ emails: string[] = [];
355
+ telephones: string[] = [];
356
+ placas = signal<string[]>([]);
357
+
358
+ // Save with concatenated values
359
+ onSave() {
360
+ this.customerForm.patchValue({
361
+ telefono: this.telephones.join('|'),
362
+ correo: this.emails.join('|'),
363
+ placa: this.placas().join('|')
364
+ });
365
+
366
+ // Submit form...
367
+ }
47
368
  ```
48
369
 
49
- ## Running unit tests
370
+ ## API Reference
371
+
372
+ ### Components
373
+
374
+ - **CustomerCard**: Display customer information in card format
375
+ - **CustomerAddEditComponent**: Form for creating/editing customers
376
+
377
+ ### Use Cases
378
+
379
+ - **CustomerUseCase**: Main customer business operations
380
+ - **CustomerExternalUseCase**: SRI integration operations
381
+
382
+ ### Repositories
383
+
384
+ - **CustomerHttpRepository**: HTTP-based customer data access
385
+ - **CustomerExternalHttpRepository**: External SRI data access
386
+
387
+ ### DTOs
50
388
 
51
- Run `nx test ng-customer` to execute the unit tests.
389
+ - **CustomerListItemDto**: Customer list display data
390
+ - **CreateCustomerDto**: Customer creation data
391
+ - **CustomerExternalDto**: External SRI customer data