diff --git a/src/app/organization/tellers/cashiers/create-cashier/create-cashier.component.ts b/src/app/organization/tellers/cashiers/create-cashier/create-cashier.component.ts
index a2c548451..b9e293abc 100644
--- a/src/app/organization/tellers/cashiers/create-cashier/create-cashier.component.ts
+++ b/src/app/organization/tellers/cashiers/create-cashier/create-cashier.component.ts
@@ -46,6 +46,10 @@ export class CreateCashierComponent implements OnInit {
cashierTemplate: any;
/** Create cashier form. */
createCashierForm: UntypedFormGroup;
+ /** Hours options for time selection (00-23). */
+ hours: string[] = Array.from({ length: 24 }, (_, i) => i.toString().padStart(2, '0'));
+ /** Minutes options for time selection (00-59). */
+ minutes: string[] = Array.from({ length: 60 }, (_, i) => i.toString().padStart(2, '0'));
/**
* Fetches cashier template from `resolve`
@@ -85,7 +89,11 @@ export class CreateCashierComponent implements OnInit {
'',
Validators.required
],
- isFullDay: [false]
+ isFullDay: [true],
+ hourStartTime: ['00'],
+ minStartTime: ['00'],
+ hourEndTime: ['00'],
+ minEndTime: ['00']
});
}
@@ -104,11 +112,40 @@ export class CreateCashierComponent implements OnInit {
if (createCashierFormData.endDate instanceof Date) {
createCashierFormData.endDate = this.dateUtils.formatDate(prevEndDate, dateFormat);
}
- const data = {
- ...createCashierFormData,
+ const data: any = {
+ staffId: createCashierFormData.staffId,
+ description: createCashierFormData.description,
+ startDate: createCashierFormData.startDate,
+ endDate: createCashierFormData.endDate,
+ isFullDay: createCashierFormData.isFullDay,
dateFormat,
locale
};
+ // Clear stale time-range errors before re-validating
+ if (this.createCashierForm.hasError('invalidTimeRange')) {
+ const { invalidTimeRange, ...rest } = this.createCashierForm.errors ?? {};
+ this.createCashierForm.setErrors(Object.keys(rest).length ? rest : null);
+ }
+ // Add time fields only when not full day
+ if (!createCashierFormData.isFullDay) {
+ const hourStart = createCashierFormData.hourStartTime;
+ const minStart = createCashierFormData.minStartTime;
+ const hourEnd = createCashierFormData.hourEndTime;
+ const minEnd = createCashierFormData.minEndTime;
+ // Validate that end time is after start time
+ const startMinutes = Number(hourStart) * 60 + Number(minStart);
+ const endMinutes = Number(hourEnd) * 60 + Number(minEnd);
+ if (Number.isNaN(startMinutes) || Number.isNaN(endMinutes) || endMinutes <= startMinutes) {
+ this.createCashierForm.setErrors({ invalidTimeRange: true });
+ return;
+ }
+ data.hourStartTime = hourStart;
+ data.minStartTime = minStart;
+ data.hourEndTime = hourEnd;
+ data.minEndTime = minEnd;
+ data.startTime = `${hourStart}:${minStart}`;
+ data.endTime = `${hourEnd}:${minEnd}`;
+ }
this.organizationService.createCashier(this.cashierTemplate.tellerId, data).subscribe((response: any) => {
this.router.navigate(['../'], { relativeTo: this.route });
});
diff --git a/src/app/organization/tellers/cashiers/edit-cashier/edit-cashier.component.html b/src/app/organization/tellers/cashiers/edit-cashier/edit-cashier.component.html
index 1da40e784..09a65266f 100644
--- a/src/app/organization/tellers/cashiers/edit-cashier/edit-cashier.component.html
+++ b/src/app/organization/tellers/cashiers/edit-cashier/edit-cashier.component.html
@@ -92,7 +92,63 @@
}
-
+ @if (!editCashierForm.value.isFullDay) {
+
+
+ {{ 'labels.inputs.Start Time' | translate }} ({{ 'labels.inputs.Hour' | translate }})
+
+ @for (hour of hours; track hour) {
+ {{ hour }}
+ }
+
+
+
+ {{ 'labels.inputs.Start Time' | translate }} ({{ 'labels.inputs.Minute' | translate }})
+
+ @for (minute of minutes; track minute) {
+ {{ minute }}
+ }
+
+
+
+
+
+ {{ 'labels.inputs.End Time' | translate }} ({{ 'labels.inputs.Hour' | translate }})
+
+ @for (hour of hours; track hour) {
+ {{ hour }}
+ }
+
+
+
+ {{ 'labels.inputs.End Time' | translate }} ({{ 'labels.inputs.Minute' | translate }})
+
+ @for (minute of minutes; track minute) {
+ {{ minute }}
+ }
+
+
+
+ @if (editCashierForm.hasError('invalidTimeRange')) {
+
+
+ {{ 'labels.inputs.End Time' | translate }} {{ 'labels.commons.must' | translate }}
+ {{ 'labels.commons.be' | translate }} {{ 'labels.commons.greater than' | translate }}
+ {{ 'labels.inputs.Start Time' | translate }}
+
+
+ }
+ }
+
+
{{ 'labels.inputs.Full Day' | translate }}?
diff --git a/src/app/organization/tellers/cashiers/edit-cashier/edit-cashier.component.ts b/src/app/organization/tellers/cashiers/edit-cashier/edit-cashier.component.ts
index a709ba0b5..cf31e44bc 100644
--- a/src/app/organization/tellers/cashiers/edit-cashier/edit-cashier.component.ts
+++ b/src/app/organization/tellers/cashiers/edit-cashier/edit-cashier.component.ts
@@ -48,6 +48,10 @@ export class EditCashierComponent implements OnInit {
minDate = new Date(2000, 0, 1);
/** Maximum Date allowed. */
maxDate = new Date();
+ /** Hours options for time selection (00-23). */
+ hours: string[] = Array.from({ length: 24 }, (_, i) => i.toString().padStart(2, '0'));
+ /** Minutes options for time selection (00-59). */
+ minutes: string[] = Array.from({ length: 60 }, (_, i) => i.toString().padStart(2, '0'));
/**
*
@@ -70,28 +74,49 @@ export class EditCashierComponent implements OnInit {
ngOnInit() {
this.maxDate = this.settingsService.maxFutureDate;
- this.setEditChargeForm();
+ this.setEditCashierForm();
}
/**
- * Sets Edit Charge Form.
+ * Sets Edit Cashier Form.
*/
- setEditChargeForm() {
+ setEditCashierForm() {
+ // Extract hours and minutes from existing time data with safe defaults
+ const startTime = this.cashierData.data.startTime || '09:00';
+ const endTime = this.cashierData.data.endTime || '17:00';
+ const [
+ hourStart = '09',
+ minStart = '00'
+ ] = startTime.includes(':') ? startTime.split(':') : [
+ '09',
+ '00'
+ ];
+ const [
+ hourEnd = '17',
+ minEnd = '00'
+ ] = endTime.includes(':') ? endTime.split(':') : [
+ '17',
+ '00'
+ ];
this.editCashierForm = this.formBuilder.group({
staffId: [{ value: this.cashierData.data.staffId, disabled: true }],
description: [this.cashierData.data.description],
startDate: [
- this.cashierData.data.startDate && new Date(this.cashierData.data.startDate),
+ this.cashierData.data.startDate ? new Date(this.cashierData.data.startDate) : null,
Validators.required
],
endDate: [
- this.cashierData.data.endDate && new Date(this.cashierData.data.endDate),
+ this.cashierData.data.endDate ? new Date(this.cashierData.data.endDate) : null,
Validators.required
],
isFullDay: [
this.cashierData.data.isFullDay,
Validators.required
- ]
+ ],
+ hourStartTime: [hourStart.padStart(2, '0')],
+ minStartTime: [minStart.padStart(2, '0')],
+ hourEndTime: [hourEnd.padStart(2, '0')],
+ minEndTime: [minEnd.padStart(2, '0')]
});
}
@@ -110,12 +135,40 @@ export class EditCashierComponent implements OnInit {
if (editCashierFormData.endDate instanceof Date) {
editCashierFormData.endDate = this.dateUtils.formatDate(prevEndDate, dateFormat);
}
- const data = {
- ...editCashierFormData,
+ const data: any = {
staffId: this.cashierData.data.staffId,
+ description: editCashierFormData.description,
+ startDate: editCashierFormData.startDate,
+ endDate: editCashierFormData.endDate,
+ isFullDay: editCashierFormData.isFullDay,
dateFormat,
locale
};
+ // Clear stale time-range errors before re-validating
+ if (this.editCashierForm.hasError('invalidTimeRange')) {
+ const { invalidTimeRange, ...rest } = this.editCashierForm.errors ?? {};
+ this.editCashierForm.setErrors(Object.keys(rest).length ? rest : null);
+ }
+ // Add time fields only when not full day
+ if (!editCashierFormData.isFullDay) {
+ const hourStart = editCashierFormData.hourStartTime;
+ const minStart = editCashierFormData.minStartTime;
+ const hourEnd = editCashierFormData.hourEndTime;
+ const minEnd = editCashierFormData.minEndTime;
+ // Validate that end time is after start time
+ const startMinutes = Number(hourStart) * 60 + Number(minStart);
+ const endMinutes = Number(hourEnd) * 60 + Number(minEnd);
+ if (Number.isNaN(startMinutes) || Number.isNaN(endMinutes) || endMinutes <= startMinutes) {
+ this.editCashierForm.setErrors({ invalidTimeRange: true });
+ return;
+ }
+ data.hourStartTime = hourStart;
+ data.minStartTime = minStart;
+ data.hourEndTime = hourEnd;
+ data.minEndTime = minEnd;
+ data.startTime = `${hourStart}:${minStart}`;
+ data.endTime = `${hourEnd}:${minEnd}`;
+ }
this.organizationService
.updateCashier(this.cashierData.data.tellerId, this.cashierData.data.id, data)
.subscribe((response: any) => {
diff --git a/src/assets/translations/cs-CS.json b/src/assets/translations/cs-CS.json
index 67a349d04..b0eba501d 100644
--- a/src/assets/translations/cs-CS.json
+++ b/src/assets/translations/cs-CS.json
@@ -1848,6 +1848,7 @@
"Hook Template": "Šablona háčku",
"Host": "Hostitel",
"Host Name": "Název hostitele",
+ "Hour": "Hodina",
"ID": "ID",
"INTEREST RATE CHART": "TABULKA ÚROKOVÝCH SAZEB",
"Id": "Id",
@@ -2082,6 +2083,7 @@
"Minimum Value": "Minimální hodnota",
"Minimum days between disbursal and first repayment date": "Minimální dny mezi výplatou a datem první splátky",
"Minimum gap between Installments": "Minimální mezera mezi splátkami",
+ "Minute": "Minuta",
"Mobile": "mobilní, pohybliví",
"Mobile Banking App": "Aplikace pro mobilní bankovnictví",
"Mobile No": "Mobil č",
diff --git a/src/assets/translations/de-DE.json b/src/assets/translations/de-DE.json
index cd1a386a2..e1184a19f 100644
--- a/src/assets/translations/de-DE.json
+++ b/src/assets/translations/de-DE.json
@@ -1849,6 +1849,7 @@
"Hook Template": "Hakenvorlage",
"Host": "Gastgeber",
"Host Name": "Hostname",
+ "Hour": "Stunde",
"ID": "AUSWEIS",
"INTEREST RATE CHART": "ZINSTABELLE",
"Id": "Ausweis",
@@ -2083,6 +2084,7 @@
"Minimum Value": "Mindestwert",
"Minimum days between disbursal and first repayment date": "Mindesttage zwischen Auszahlung und erstem Rückzahlungsdatum",
"Minimum gap between Installments": "Mindestabstand zwischen den Raten",
+ "Minute": "Minute",
"Mobile": "Handy, Mobiltelefon",
"Mobile Banking App": "Mobile-Banking-App",
"Mobile No": "Mobil-Nr",
diff --git a/src/assets/translations/en-US.json b/src/assets/translations/en-US.json
index 94bb6a50f..98a07a750 100644
--- a/src/assets/translations/en-US.json
+++ b/src/assets/translations/en-US.json
@@ -1857,6 +1857,7 @@
"Hook Template": "Hook Template",
"Host": "Host",
"Host Name": "Host Name",
+ "Hour": "Hour",
"ID": "ID",
"INTEREST RATE CHART": "INTEREST RATE CHART",
"Id": "Id",
@@ -2092,6 +2093,7 @@
"Minimum Value": "Minimum Value",
"Minimum days between disbursal and first repayment date": "Minimum days between disbursal and first repayment date",
"Minimum gap between Installments": "Minimum gap between Installments",
+ "Minute": "Minute",
"Mobile": "Mobile",
"Mobile Banking App": "Mobile Banking App",
"Mobile No": "Mobile No",
diff --git a/src/assets/translations/es-CL.json b/src/assets/translations/es-CL.json
index 975ed09ac..734f688fc 100644
--- a/src/assets/translations/es-CL.json
+++ b/src/assets/translations/es-CL.json
@@ -1849,6 +1849,7 @@
"Hook Template": "Plantilla de gancho",
"Host": "Anfitrión",
"Host Name": "Nombre del anfitrión",
+ "Hour": "Hora",
"ID": "IDENTIFICACIÓN",
"INTEREST RATE CHART": "TABLA DE TIPOS DE INTERÉS",
"Id": "Id",
@@ -2083,6 +2084,7 @@
"Minimum Value": "Valor mínimo",
"Minimum days between disbursal and first repayment date": "Días mínimos entre el curso y la primera fecha de amortización",
"Minimum gap between Installments": "Brecha mínima entre Cuotas",
+ "Minute": "Minuto",
"Mobile": "Móvil",
"Mobile Banking App": "Aplicación de banca móvil",
"Mobile No": "Teléfono móvil",
diff --git a/src/assets/translations/es-MX.json b/src/assets/translations/es-MX.json
index c340bf123..7d3991f04 100644
--- a/src/assets/translations/es-MX.json
+++ b/src/assets/translations/es-MX.json
@@ -1849,6 +1849,7 @@
"Hook Template": "Plantilla de gancho",
"Host": "Anfitrión",
"Host Name": "Nombre del anfitrión",
+ "Hour": "Hora",
"ID": "IDENTIFICACIÓN",
"INTEREST RATE CHART": "TABLA DE TIPOS DE INTERÉS",
"Id": "Id",
@@ -2083,6 +2084,7 @@
"Minimum Value": "Valor mínimo",
"Minimum days between disbursal and first repayment date": "Días mínimos entre el desembolso y la primera fecha de amortización",
"Minimum gap between Installments": "Brecha mínima entre Cuotas",
+ "Minute": "Minuto",
"Mobile": "Móvil",
"Mobile Banking App": "Aplicación de banca móvil",
"Mobile No": "Teléfono móvil",
diff --git a/src/assets/translations/fr-FR.json b/src/assets/translations/fr-FR.json
index 3e4773bfa..f425925b5 100644
--- a/src/assets/translations/fr-FR.json
+++ b/src/assets/translations/fr-FR.json
@@ -846,6 +846,7 @@
"and at": "et à",
"and must be greater than": "et doit être supérieur à",
"are": "sont",
+ "be": "être",
"be larger than 0 and at most 100": "être supérieur à 0 et au maximum 100",
"begin with a special character or number": "commencer par un caractère spécial ou un chiffre",
"cannot begin with a special character or number": "ne peut pas commencer par un caractère spécial ou un chiffre",
@@ -853,12 +854,14 @@
"do not match": "ne correspondent pas",
"does not match": "ne correspond pas",
"greater equal to than 0": "supérieur égal à 0",
+ "greater than": "supérieur à",
"invalid": "invalide",
"is": "est",
"Is": "Est",
"is required": "est requis",
"least one uppercase": "au moins une majuscule",
"lowercase and special character": "minuscules et caractères spéciaux",
+ "must": "doit",
"must be": "doit être",
"must be a positive integer": "doit être un entier positif",
"must be an integer between -9999 and 9999": "doit être un entier compris entre -9999 et 9999",
@@ -1849,6 +1852,7 @@
"Hook Template": "Modèle de crochet",
"Host": "Hôte",
"Host Name": "Nom d'hôte",
+ "Hour": "Heure",
"ID": "IDENTIFIANT",
"INTEREST RATE CHART": "TABLEAU DES TAUX D'INTÉRÊT",
"Id": "Identifiant",
@@ -2083,6 +2087,7 @@
"Minimum Value": "Valeur minimum",
"Minimum days between disbursal and first repayment date": "Jours minimum entre le décaissement et la première date de remboursement",
"Minimum gap between Installments": "Écart minimum entre les versements",
+ "Minute": "Minute",
"Mobile": "Mobile",
"Mobile Banking App": "Application bancaire mobile",
"Mobile No": "Non de portable",
diff --git a/src/assets/translations/it-IT.json b/src/assets/translations/it-IT.json
index b6085c9b7..df42d49db 100644
--- a/src/assets/translations/it-IT.json
+++ b/src/assets/translations/it-IT.json
@@ -1848,6 +1848,7 @@
"Hook Template": "Modello di gancio",
"Host": "Ospite",
"Host Name": "Nome host",
+ "Hour": "Ora",
"ID": "ID",
"INTEREST RATE CHART": "GRAFICO DEL TASSO DI INTERESSE",
"Id": "Id",
@@ -2082,6 +2083,7 @@
"Minimum Value": "Valore minimo",
"Minimum days between disbursal and first repayment date": "Giorni minimi tra la data di erogazione e la data del primo rimborso",
"Minimum gap between Installments": "Intervallo minimo tra le rate",
+ "Minute": "Minuto",
"Mobile": "Mobile",
"Mobile Banking App": "Applicazione bancaria mobile",
"Mobile No": "Cellulare n",
diff --git a/src/assets/translations/ko-KO.json b/src/assets/translations/ko-KO.json
index d1babf91e..73aee80a1 100644
--- a/src/assets/translations/ko-KO.json
+++ b/src/assets/translations/ko-KO.json
@@ -1850,6 +1850,7 @@
"Hook Template": "후크 템플릿",
"Host": "주인",
"Host Name": "호스트 이름",
+ "Hour": "시",
"ID": "ID",
"INTEREST RATE CHART": "금리 차트",
"Id": "ID",
@@ -2084,6 +2085,7 @@
"Minimum Value": "최소값",
"Minimum days between disbursal and first repayment date": "지불 날짜와 첫 번째 상환 날짜 사이의 최소 일수",
"Minimum gap between Installments": "할부 간 최소 간격",
+ "Minute": "분",
"Mobile": "이동하는",
"Mobile Banking App": "모바일 뱅킹 앱",
"Mobile No": "모바일 번호",
diff --git a/src/assets/translations/lt-LT.json b/src/assets/translations/lt-LT.json
index 7c19b7c75..a3cef3574 100644
--- a/src/assets/translations/lt-LT.json
+++ b/src/assets/translations/lt-LT.json
@@ -1847,6 +1847,7 @@
"Hook Template": "Kabliuko šablonas",
"Host": "Šeimininkas",
"Host Name": "Pagrindinio kompiuterio pavadinimas",
+ "Hour": "Valanda",
"ID": "ID",
"INTEREST RATE CHART": "PALŪKANŲ NORMOS SCHEMA",
"Id": "Id",
@@ -2081,6 +2082,7 @@
"Minimum Value": "Minimali vertė",
"Minimum days between disbursal and first repayment date": "Minimalios dienos nuo išmokėjimo iki pirmojo grąžinimo datos",
"Minimum gap between Installments": "Minimalus tarpas tarp įmokų",
+ "Minute": "Minutė",
"Mobile": "Mobilusis",
"Mobile Banking App": "Mobiliosios bankininkystės programa",
"Mobile No": "Mobiliojo Nr",
diff --git a/src/assets/translations/lv-LV.json b/src/assets/translations/lv-LV.json
index 0d5aaa13e..d0925086f 100644
--- a/src/assets/translations/lv-LV.json
+++ b/src/assets/translations/lv-LV.json
@@ -1849,6 +1849,7 @@
"Hook Template": "Āķa veidne",
"Host": "Uzņēmēja",
"Host Name": "Saimniekdatora nosaukums",
+ "Hour": "Stunda",
"ID": "ID",
"INTEREST RATE CHART": "PROCENTU LIKMJU grafiks",
"Id": "Id",
@@ -2083,6 +2084,7 @@
"Minimum Value": "Minimālā vērtība",
"Minimum days between disbursal and first repayment date": "Minimālās dienas starp izmaksu un pirmo atmaksas datumu",
"Minimum gap between Installments": "Minimālā starpība starp iemaksām",
+ "Minute": "Minūte",
"Mobile": "Mobilais",
"Mobile Banking App": "Mobilās bankas lietotne",
"Mobile No": "Mobilais Nr",
diff --git a/src/assets/translations/ne-NE.json b/src/assets/translations/ne-NE.json
index e08bfce16..f6eb9aeaa 100644
--- a/src/assets/translations/ne-NE.json
+++ b/src/assets/translations/ne-NE.json
@@ -1847,6 +1847,7 @@
"Hook Template": "हुक टेम्प्लेट",
"Host": "होस्ट",
"Host Name": "होस्ट नाम",
+ "Hour": "घण्टा",
"ID": "ID",
"INTEREST RATE CHART": "ब्याज दर चार्ट",
"Id": "आईडी",
@@ -2081,6 +2082,7 @@
"Minimum Value": "न्यूनतम मान",
"Minimum days between disbursal and first repayment date": "भुक्तानी र पहिलो भुक्तानी मिति बीचको न्यूनतम दिन",
"Minimum gap between Installments": "किस्ताहरू बीचको न्यूनतम अन्तर",
+ "Minute": "मिनेट",
"Mobile": "मोबाइल",
"Mobile Banking App": "मोबाइल बैंकिङ एप",
"Mobile No": "मोबाइल नम्बर",
diff --git a/src/assets/translations/pt-PT.json b/src/assets/translations/pt-PT.json
index eeeb5c7fc..f087127da 100644
--- a/src/assets/translations/pt-PT.json
+++ b/src/assets/translations/pt-PT.json
@@ -1848,6 +1848,7 @@
"Hook Template": "Modelo de gancho",
"Host": "Hospedar",
"Host Name": "Nome de anfitrião",
+ "Hour": "Hora",
"ID": "EU IA",
"INTEREST RATE CHART": "TABELA DE TAXAS DE JUROS",
"Id": "Eu ia",
@@ -2082,6 +2083,7 @@
"Minimum Value": "Valor Mínimo",
"Minimum days between disbursal and first repayment date": "Dias mínimos entre o desembolso e a data do primeiro reembolso",
"Minimum gap between Installments": "Intervalo mínimo entre parcelas",
+ "Minute": "Minuto",
"Mobile": "Móvel",
"Mobile Banking App": "Aplicativo de banco móvel",
"Mobile No": "Celular Não",
diff --git a/src/assets/translations/sw-SW.json b/src/assets/translations/sw-SW.json
index b64e22ad2..4aef56834 100644
--- a/src/assets/translations/sw-SW.json
+++ b/src/assets/translations/sw-SW.json
@@ -1846,6 +1846,7 @@
"Hook Template": "Kigezo cha ndoano",
"Host": "Mwenyeji",
"Host Name": "Jina la mwenyeji",
+ "Hour": "Saa",
"ID": "ID",
"INTEREST RATE CHART": "CHATI YA KIWANGO CHA RIBA",
"Id": "Kitambulisho",
@@ -2080,6 +2081,7 @@
"Minimum Value": "Thamani ya chini",
"Minimum days between disbursal and first repayment date": "Siku chache kati ya malipo na tarehe ya malipo ya kwanza",
"Minimum gap between Installments": "Pengo la chini kati ya Mikopo",
+ "Minute": "Dakika",
"Mobile": "Rununu",
"Mobile Banking App": "Programu ya Benki ya Simu ya Mkononi",
"Mobile No": "Simu No",