Compare commits

...

50 Commits

Author SHA1 Message Date
Michael Auer
6bc265a358 Merge branch 'release/2.3' 2023-07-11 12:20:14 +02:00
Lorenz Hilpert
264d914044 #4124 Kulturpass- Abholfrist auf Bearbeitenseite 2023-06-15 16:28:44 +02:00
Lorenz Hilpert
12f1e7c3af #4124 Kulturpass - Zurücklegen bis und Abholfrist nicht ausgrauen 2023-06-15 14:33:41 +02:00
Lorenz Hilpert
9eb4fbab65 Merge branch 'release/2.3' of https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend into release/2.3 2023-06-15 13:52:19 +02:00
Lorenz Hilpert
8d1a63e47f #4120 Marker Bezahlt über KulturPass in der Ergebnisliste 2023-06-15 13:51:57 +02:00
Nino Righi
e449e612c1 Merged PR 1566: #4112 Hotfix Manueller Preis Archivartikel
#4112 Hotfix Manueller Preis Archivartikel
2023-06-15 11:43:09 +00:00
Lorenz Hilpert
6ba65f031b #4119 Kaufoptionen - Nicht verfügbare Kaufoptionen werden ausgegraut 2023-06-15 11:56:05 +02:00
Lorenz Hilpert
f31ac4c2e3 #4117 Bug Fix Collect On Delivery 2023-06-15 11:01:39 +02:00
Lorenz Hilpert
f980f5aaf9 #4113 Wareneingang Navigation entfernt 2023-06-14 17:53:29 +02:00
Lorenz Hilpert
87a1e8a2c4 Kulturpass Handler Fix COLLECT_WITH_SMALLAMOUNTINVOICE 2023-06-14 16:30:19 +02:00
Lorenz Hilpert
5bb9ebd6ec #3961 Bestelldeadline anzeigen 2023-06-14 10:27:20 +02:00
Lorenz Hilpert
45ab1e9cc5 Kulturpass Drucken Fix 2023-06-13 17:51:33 +02:00
Lorenz Hilpert
8d2685a8c3 Fix Kulturpass Anzeige 2023-06-13 17:34:27 +02:00
Lorenz Hilpert
d38c41a99d Kulturpass - Fix - erneut drucken 2023-06-13 16:08:42 +02:00
Lorenz Hilpert
ff3dacde39 #4101 Kulturpass 2023-06-13 15:25:08 +02:00
Nino Righi
90b752e185 Merged PR 1558: #4092 Fix updateNotificationsGroup get orderId from first Item not from initi...
#4092 Fix updateNotificationsGroup get orderId from first Item not from initial form
2023-06-09 12:52:28 +00:00
Nino Righi
3ed9227508 Merged PR 1553: #4074 Bugfixes
#4074 Bugfixes
2023-06-06 16:12:56 +00:00
Nino Righi
9cfe8176d4 Merged PR 1548: #4083 Goods In Out Order Edit Bestellkanal orderSource from Order not from fi...
#4083 Goods In Out Order Edit Bestellkanal orderSource from Order not from first item
2023-06-06 14:53:47 +00:00
Nino Righi
3999b8f623 Merged PR 1544: #4074 Kaufoptionen Manuelle Preiseingabe
#4074 Kaufoptionen Manuelle Preiseingabe
2023-06-06 13:28:57 +00:00
Andreas Schickinger
c8f54b8be5 Merged PR 1540: #4073 Hotfix PP Filter wird zurückgesetzt, wenn nicht vollständig geladen
#4073 Hotfix PP Filter wird zurückgesetzt, wenn nicht vollständig geladen

Related work items: #4073
2023-06-02 14:34:52 +00:00
Nino Righi
413d2f5178 Merged PR 1539: #4048 Changed clientChannel to orderSource
#4048 Changed clientChannel to orderSource
2023-06-02 11:48:23 +00:00
Andreas Schickinger
35c2a6a046 Merged PR 1537: #4050 Hotfix Kaufoptionen Popup - Weiterleiten zur Kundensuche
#4050 Hotfix Kaufoptionen Popup - Weiterleiten zur Kundensuche

Related work items: #4050
2023-06-02 11:23:21 +00:00
Andreas Schickinger
1df201525c Merged PR 1536: #4068 Hotfix Warenkorb Rücklage Menge ändern
#4068 Warenkorb Rücklage Menge ändern hotfix
2023-06-01 15:42:19 +00:00
Lorenz Hilpert
ef72dcf554 #3984 - Filtereinstellungen wenn Artikel für Dig-Versand in Warenkorb sind falsch
(cherry picked from commit 6717f0ee3d)
2023-05-25 10:54:08 +02:00
Michael Auer
c228147c00 Merged PR 1529: Floating Docker Branch-Tags
Es wird zusätzlich zu den bisherigen Docker-Tags, ein Docker-Tag vergeben, das den aktuellen Branch repräsentiert. Da dieses (identische) Tag für jeden neuen Build aus einem Branch wieder vergeben wird, wandert das Tag auf das neueste Docker-Image aus diesem Branch (vgl. Tag _latest_)

Beispiele:
develop => refs_head_develop
release/2.3 => refs_head_release_2.3

(cherry picked from commit 8880ed0df6)
2023-05-24 18:37:47 +02:00
Lorenz Hilpert
4b56b973c8 #3979 SSC Nummer Anzeigen 2023-05-24 17:53:54 +02:00
Lorenz Hilpert
28a00b9f22 Merge branch 'release/2.3' of https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend into release/2.3 2023-05-24 17:38:57 +02:00
Nino Righi
b1fe692de5 Merged PR 1527: #4009 Filter reset and Branch Selector Dropdown Bugfixes
#4009 Filter reset and Branch Selector Dropdown Bugfixes
2023-05-23 11:55:14 +00:00
Lorenz Hilpert
08b2f6cbc9 removed console log 2023-05-23 13:32:28 +02:00
Lorenz Hilpert
10e1db388f #3979 Meldenummer in UI anzeigen 2023-05-23 13:32:05 +02:00
Lorenz Hilpert
cd7f71b968 #4044 Handlungsanweisung im Status fehlt 2023-05-22 16:13:57 +02:00
Lorenz Hilpert
9361a631dd Merge branch 'release/2.3' of https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend into release/2.3 2023-05-22 10:46:34 +02:00
Lorenz Hilpert
59c5abfd93 Deadline Pipe Fix 2023-05-22 10:46:10 +02:00
Nino Righi
633c23a6f0 Merged PR 1526: #4009 Clear Filter Release
#4009 Clear Filter Release
2023-05-18 22:27:32 +00:00
Lorenz Hilpert
779e7323e2 #3965 HSC ISA - Kundenkarten anlegen raus 2023-05-17 15:51:35 +02:00
Lorenz Hilpert
6e7fbd940b #3961 Order Deadline Popup 2023-05-17 14:56:25 +02:00
Lorenz Hilpert
6878b608fc #4035 branch slector fix 2023-05-17 13:23:43 +02:00
Lorenz Hilpert
a4fab8b64e #4035 Filiale wird bei Reservierung setzen 2023-05-16 15:32:38 +02:00
Lorenz Hilpert
21c0cc8794 #4039 - Irrläufer verschwinden nicht von der Liste 2023-05-16 13:30:51 +02:00
Lorenz Hilpert
7ea11ea0c4 #4039 Neu laden der Liste wenn man im Popup auf erledigt klickt 2023-05-12 16:19:21 +02:00
Lorenz Hilpert
778343f636 #4036 Bugfix scaling von texten 2023-05-09 13:03:02 +02:00
Lorenz Hilpert
d2fb3b6c85 #4036 - Kaufoptionen 2023-05-09 10:33:21 +02:00
Lorenz Hilpert
4c7b5eec38 #4016 Kaufoptionen - Preis bei Versandbestellungen 2023-05-03 16:22:51 +02:00
Lorenz Hilpert
35068e23cd #4021 PP - Suche nach Packstuecken oeffnet details bei einem treffer 2023-05-02 11:33:56 +02:00
Lorenz Hilpert
bc30b86cce #4020 PP - Label wird auf IPad nicht merh zwei zeilig angezeigt 2023-05-02 11:24:36 +02:00
Lorenz Hilpert
461f166e33 #4017 PP - scanId wird beim aender des status mit uebermittelt 2023-05-02 11:14:22 +02:00
Lorenz Hilpert
ad95faffa7 Merge branch 'release/2.3' of https://dev.azure.com/hugendubel/ISA/_git/ISA-Frontend into release/2.3 2023-04-24 23:09:21 +02:00
Michael Auer
a0eb3c0459 Revert "Update Remi Api Url"
This reverts commit ea9920c4d5.
2023-04-24 16:53:01 +02:00
Lorenz Hilpert
fea435a6d2 Bugfix - Hor-Scroll removed 2023-04-24 16:41:52 +02:00
Lorenz Hilpert
ea9920c4d5 Update Remi Api Url 2023-04-24 16:19:50 +02:00
95 changed files with 1265 additions and 300 deletions

View File

@@ -1635,5 +1635,8 @@
}
}
}
},
"cli": {
"analytics": false
}
}

View File

@@ -165,7 +165,7 @@ export class DomainAvailabilityService {
),
map(([response, supplier, defaultBranch]) => {
const price = item?.price;
return this._mapToTakeAwayAvailability({ response, supplier, branchId: branch.id ?? defaultBranch.id, quantity, price });
return this._mapToTakeAwayAvailability({ response, supplier, branchId: branch?.id ?? defaultBranch?.id, quantity, price });
}),
shareReplay(1)
);
@@ -540,6 +540,7 @@ export class DomainAvailabilityService {
return preferred.map((p) => {
return [
{
orderDeadline: p?.orderDeadline,
availabilityType: p?.status,
ssc: p?.ssc,
sscText: p?.sscText,
@@ -561,7 +562,6 @@ export class DomainAvailabilityService {
private _mapToShippingAvailability(availabilities: SwaggerAvailabilityDTO[]) {
const preferred = availabilities.filter((f) => f.preferred === 1);
return preferred.map((p) => {
return {
availabilityType: p?.status,
@@ -575,6 +575,7 @@ export class DomainAvailabilityService {
supplierInfo: p?.requestStatusCode,
lastRequest: p?.requested,
itemId: p.itemId,
priceMaintained: p.priceMaintained,
};
});
}

View File

@@ -21,6 +21,7 @@ export class CollectOnDeliveryNoteActionHandler extends ActionHandler<OrderItems
const response = await this.orderService
.OrderCollectOnDeliveryNote({
data,
eagerLoading: 1,
})
.toPromise();
@@ -29,7 +30,7 @@ export class CollectOnDeliveryNoteActionHandler extends ActionHandler<OrderItems
return {
...context,
receipts: response.result,
receipts: response.result.map((r) => r.data),
};
}
}

View File

@@ -0,0 +1,35 @@
import { Injectable } from '@angular/core';
import { ActionHandler } from '@core/command';
import { OrderService } from '@swagger/oms';
import { OrderItemsContext } from './order-items.context';
@Injectable()
export class CollectWithSmallAmountinvoiceActionHandler extends ActionHandler<OrderItemsContext> {
constructor(private orderService: OrderService) {
super('COLLECT_WITH_SMALLAMOUNTINVOICE');
}
async handler(context: OrderItemsContext): Promise<OrderItemsContext> {
const data: Record<number, number> = {};
context.items.forEach((orderItemSubsetId) => {
data[orderItemSubsetId.orderItemSubsetId] =
context.itemQuantity?.get(orderItemSubsetId.orderItemSubsetId) ?? orderItemSubsetId.quantity;
});
const response = await this.orderService
.OrderCollectWithSmallAmountInvoice({
data,
eagerLoading: 1,
})
.toPromise();
// Für korrekte Navigation nach Aufruf, da ProcessingStatus Serverseitig auf abgeholt gesetzt wird
context.items?.forEach((i) => (i.processingStatus = 256));
return {
...context,
receipts: response.result.map((r) => r.data),
};
}
}

View File

@@ -1,4 +1,3 @@
// start:ng42.barrel
export * from './accepted.action-handler';
export * from './arrived.action-handler';
export * from './assembled.action-handler';
@@ -7,6 +6,10 @@ export * from './back-to-stock.action-handler';
export * from './canceled-by-buyer.action-handler';
export * from './canceled-by-retailer.action-handler';
export * from './canceled-by-supplier.action-handler';
export * from './change-order-item-status-base.action-handler';
export * from './collect-on-deliverynote.action-handler';
export * from './collect-with-smallamountinvoice.action-handler';
export * from './create-returnitem.action-handler';
export * from './create-shipping-note.action-handler';
export * from './delivered.action-handler';
export * from './determine-supplier.action-handler';
@@ -25,16 +28,14 @@ export * from './parked.action-handler';
export * from './placed.action-handler';
export * from './preperation-for-shipping.action-handler';
export * from './print-compartment-label.action-handler';
export * from './print-pricediffqrcodelabel.action-handler';
export * from './print-shipping-note.action-handler';
export * from './re-ordered.action-handler';
export * from './print-smallamountinvoice.action-handler';
export * from './re-order.action-handler';
export * from './re-ordered.action-handler';
export * from './redirected-internally.action-handler';
export * from './requested.action-handler';
export * from './reserved.action-handler';
export * from './returned-by-buyer.action-handler';
export * from './shipping-note.action-handler';
export * from './supplier-temporarily-out-of-stock.action-handler copy';
export * from './collect-on-deliverynote.action-handler';
export * from './create-returnitem.action-handler';
export * from './print-pricediffqrcodelabel.action-handler';
// end:ng42.barrel

View File

@@ -27,7 +27,8 @@ export class PrintShippingNoteActionHandler extends ActionHandler<OrderItemsCont
printerType: 'Label',
print: async (printer) => {
try {
for (const group of groupBy(data?.receipts, (receipt) => receipt?.buyer?.buyerNumber)) {
const receipts = data?.receipts?.filter((r) => r?.receiptType & 1);
for (const group of groupBy(receipts, (receipt) => receipt?.buyer?.buyerNumber)) {
await this.domainPrinterService.printShippingNote({ printer, receipts: group?.items?.map((r) => r?.id) }).toPromise();
}
return {

View File

@@ -0,0 +1,56 @@
import { Injectable } from '@angular/core';
import { ActionHandler } from '@core/command';
import { OrderItemsContext } from './order-items.context';
import { OMSPrintService } from '@swagger/print';
import { UiModalService } from '@ui/modal';
import { PrintModalComponent, PrintModalData } from '@modal/printer';
import { NativeContainerService } from 'native-container';
import { groupBy } from '@ui/common';
@Injectable()
export class PrintSmallamountinvoiceActionHandler extends ActionHandler<OrderItemsContext> {
constructor(
private uiModal: UiModalService,
private omsPrintService: OMSPrintService,
private nativeContainerService: NativeContainerService
) {
super('PRINT_SMALLAMOUNTINVOICE');
}
async handler(data: OrderItemsContext): Promise<OrderItemsContext> {
await this.uiModal
.open({
content: PrintModalComponent,
config: { showScrollbarY: false },
data: {
printImmediately: !this.nativeContainerService.isNative,
printerType: 'Label',
print: async (printer) => {
try {
const receipts = data?.receipts?.filter((r) => r?.receiptType & 128);
for (const group of groupBy(receipts, (receipt) => receipt?.buyer?.buyerNumber)) {
await this.omsPrintService
.OMSPrintKleinbetragsrechnung({
data: group?.items?.map((r) => r?.id),
printer,
})
.toPromise();
}
return {
error: false,
};
} catch (error) {
console.error(error);
return {
error: true,
message: error?.message || error,
};
}
},
} as PrintModalData,
})
.afterClosed$.toPromise();
return data;
}
}

View File

@@ -1,6 +1,5 @@
import { Injectable } from '@angular/core';
import {
AutocompleteTokenDTO,
BranchService,
ChangeStockStatusCodeValues,
HistoryDTO,
@@ -11,9 +10,7 @@ import {
OrderItemSubsetDTO,
OrderListItemDTO,
OrderService,
QueryTokenDTO,
ReceiptService,
ResponseArgsOfIEnumerableOfHistoryDTO,
StatusValues,
StockStatusCodeService,
ValueTupleOfLongAndReceiptTypeAndEntityDTOContainerOfReceiptDTO,
@@ -22,7 +19,7 @@ import {
} from '@swagger/oms';
import { memorize } from '@utils/common';
import { Observable } from 'rxjs';
import { map, mergeMap, shareReplay } from 'rxjs/operators';
import { map, shareReplay } from 'rxjs/operators';
@Injectable()
export class DomainOmsService {
@@ -191,6 +188,10 @@ export class DomainOmsService {
);
}
getOrderSource(orderId: number): Observable<string> {
return this.getOrder(orderId).pipe(map((order) => order?.features?.orderSource));
}
updateNotifications(orderId: number, changes: { selected: NotificationChannel; email: string; mobile: string }) {
const communicationDetails = {
email: changes.email,

View File

@@ -0,0 +1,11 @@
import { PackageArrivalStatusDTO } from '@swagger/wws';
export abstract class PackageInspectionEvent {
constructor(public readonly type: string) {}
}
export class PackageStatusChangedEvent extends PackageInspectionEvent {
constructor(public readonly packageId: string, public readonly status: PackageArrivalStatusDTO) {
super('PackageStatusChangedEvent');
}
}

View File

@@ -1,10 +1,8 @@
import { Injectable } from '@angular/core';
import {
ListResponseArgsOfPackageDTO,
ListResponseArgsOfPackageDTO2,
PackageArrivalStatusDTO,
PackageDetailResponseDTO,
PackageDTO,
PackageDTO2,
QuerySettingsDTO,
QueryTokenDTO,
@@ -13,13 +11,18 @@ import {
ResponseArgsOfQuerySettingsDTO,
WareneingangService,
} from '@swagger/wws';
import { Observable } from 'rxjs';
import { map } from 'rxjs/operators';
import { Observable, Subject } from 'rxjs';
import { map, tap } from 'rxjs/operators';
import { PackageInspectionEvent, PackageStatusChangedEvent } from './events';
@Injectable({
providedIn: 'root',
})
export class DomainPackageInspectionService {
private _events = new Subject<PackageInspectionEvent>();
events = this._events.asObservable();
constructor(private _wareneingang: WareneingangService) {}
getQuerySettingsResponse(): Observable<ResponseArgsOfQuerySettingsDTO> {
@@ -47,23 +50,26 @@ export class DomainPackageInspectionService {
}
changePackageStatusResponse(pkg: PackageDTO2, modifier: string): Observable<ResponseArgsOfPackageArrivalStatusDTO> {
return this._wareneingang.WareneingangChangePackageStatus({
packageId: pkg.id,
payload: {
id: pkg.id,
annotation: pkg.annotation,
area: pkg.area,
arrivalChecked: pkg.arrivalChecked,
arrivalStatus: pkg.arrivalStatus,
deliveryNoteNumber: pkg.deliveryNoteNumber,
deliveryTarget: pkg.deliveryTarget,
estimatedDeliveryDate: pkg.estimatedDeliveryDate,
packageNumber: pkg.packageNumber,
supplier: pkg.supplier,
trackingNumber: pkg.trackingNumber,
},
modifier,
});
return this._wareneingang
.WareneingangChangePackageStatus({
packageId: pkg.id,
payload: {
id: pkg.id,
annotation: pkg.annotation,
area: pkg.area,
arrivalChecked: pkg.arrivalChecked,
arrivalStatus: pkg.arrivalStatus,
deliveryNoteNumber: pkg.deliveryNoteNumber,
deliveryTarget: pkg.deliveryTarget,
estimatedDeliveryDate: pkg.estimatedDeliveryDate,
packageNumber: pkg.packageNumber,
supplier: pkg.supplier,
trackingNumber: pkg.trackingNumber,
scanId: pkg.scanId,
},
modifier,
})
.pipe(tap((res) => this._events.next(new PackageStatusChangedEvent(pkg.id, res.result))));
}
changePackageStatus(pkg: PackageDTO2, modifier: string): Observable<PackageArrivalStatusDTO> {

View File

@@ -1,6 +1,6 @@
/*
* Public API Surface of package-inspection
*/
export * from './lib/events';
export * from './lib/package-inspection.service';
export * from './lib/package-inspection.module';

View File

@@ -73,10 +73,10 @@
<ui-icon icon="documents_refresh" size="24px"></ui-icon>
Remission
</a>
<a [routerLink]="['/filiale/package-inspection']" routerLinkActive="active" (click)="fetchAndOpenPackages()">
<!-- <a [routerLink]="['/filiale/package-inspection']" routerLinkActive="active" (click)="fetchAndOpenPackages()">
<ui-svg-icon icon="clipboard-check-outline" [size]="24"></ui-svg-icon>
Wareneingang
</a>
</a> -->
</ng-container>
</shell-footer>
</div>

View File

@@ -94,7 +94,24 @@
<div class="fetching xsmall" *ngIf="store.fetchingPickUpAvailability$ | async; else showAvailabilityPickUpIcon"></div>
<ng-template #showAvailabilityPickUpIcon>
<ui-icon *ngIf="store.isPickUpAvailabilityAvailable$ | async" icon="box_out" size="18px"></ui-icon>
<ui-icon
[uiOverlayTrigger]="orderDeadlineTooltip"
*ngIf="store.isPickUpAvailabilityAvailable$ | async"
icon="box_out"
size="18px"
></ui-icon>
<ui-tooltip
[warning]="true"
yPosition="above"
xPosition="after"
[yOffset]="-11"
[xOffset]="8"
#orderDeadlineTooltip
[closeable]="true"
>
<b>{{ (store.pickUpAvailability$ | async)?.orderDeadline | orderDeadline }}</b>
</ui-tooltip>
</ng-template>
<div class="fetching xsmall" *ngIf="store.fetchingDeliveryAvailability$ | async; else showAvailabilityDeliveryIcon"></div>

View File

@@ -20,6 +20,7 @@ import { DateAdapter } from '@ui/common';
import { DatePipe } from '@angular/common';
import { PurchaseOptionsModalService } from '@shared/modals/purchase-options-modal';
import { DomainAvailabilityService } from '@domain/availability';
import { DomainCheckoutService } from '@domain/checkout';
@Component({
selector: 'page-article-details',
@@ -65,6 +66,10 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy {
this.store.isDeliveryB2BAvailabilityAvailable$,
]).pipe(map(([digDelivery, b2bDelivery]) => b2bDelivery && !digDelivery));
customerFeatures$ = this.applicationService.activatedProcessId$.pipe(
switchMap((processId) => this._domainCheckoutService.getCustomerFeatures({ processId }))
);
showSubscriptionBadge$ = this.store.item$.pipe(map((item) => item?.features?.find((i) => i.key === 'PFO')));
showPromotionBadge$ = this.store.item$.pipe(map((item) => item?.features?.find((i) => i.key === 'Promotion')));
@@ -126,7 +131,8 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy {
public elementRef: ElementRef,
private _purchaseOptionsModalService: PurchaseOptionsModalService,
private _availability: DomainAvailabilityService,
private _router: Router
private _router: Router,
private _domainCheckoutService: DomainCheckoutService
) {}
ngOnInit() {
@@ -270,14 +276,22 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy {
type: 'add',
processId: this.applicationService.activatedProcessId,
items: [item],
pickupBranch: selectedBranch,
inStoreBranch: selectedBranch,
})
.afterClosed$.subscribe((result) => {
.afterClosed$.subscribe(async (result) => {
if (result?.data === 'continue') {
this.navigateToShoppingCart();
console.log('continue');
const customer = await this._domainCheckoutService
.getBuyer({ processId: this.applicationService.activatedProcessId })
.pipe(first())
.toPromise();
if (customer) {
this.navigateToShoppingCart();
} else {
this.navigateToCustomerSearch();
}
} else if (result?.data === 'continue-shopping') {
this.navigateToResultList();
console.log('continue-shopping');
}
});
}
@@ -286,6 +300,24 @@ export class ArticleDetailsComponent implements OnInit, OnDestroy {
this._router.navigate([`/kunde/${this.applicationService.activatedProcessId}/cart/review`]);
}
async navigateToCustomerSearch() {
try {
const response = await this.customerFeatures$
.pipe(
first(),
switchMap((customerFeatures) => {
return this._domainCheckoutService.canSetCustomer({ processId: this.applicationService.activatedProcessId, customerFeatures });
})
)
.toPromise();
this._router.navigate(['/kunde', this.applicationService.activatedProcessId, 'customer', 'search'], {
queryParams: { filter_customertype: response.filter.customertype },
});
} catch (error) {
this._router.navigate(['/kunde', this.applicationService.activatedProcessId, 'customer', 'search']);
}
}
async navigateToResultList() {
let crumbs = await this.breadcrumb
.getBreadcrumbsByKeyAndTags$(this.applicationService.activatedProcessId, ['catalog'])

View File

@@ -10,6 +10,7 @@ import { ArticleRecommendationsComponent } from './recommendations/article-recom
import { PipesModule } from '../shared/pipes/pipes.module';
import { UiTooltipModule } from '@ui/tooltip';
import { UiCommonModule } from '@ui/common';
import { OrderDeadlinePipeModule } from '@shared/pipes/order-deadline';
@NgModule({
imports: [
@@ -22,6 +23,7 @@ import { UiCommonModule } from '@ui/common';
UiCommonModule,
UiTooltipModule,
PipesModule,
OrderDeadlinePipeModule,
],
exports: [ArticleDetailsComponent, ArticleRecommendationsComponent],
declarations: [ArticleDetailsComponent, ArticleRecommendationsComponent],

View File

@@ -129,7 +129,11 @@ export class ArticleDetailsStore extends ComponentStore<ArticleDetailsState> {
//#region Abholung
readonly fetchingPickUpAvailability$ = this.select((s) => s.fetchingPickUpAvailability);
readonly pickUpAvailability$: Observable<AvailabilityDTO> = combineLatest([this.itemData$, this.branch$, this.isDownload$]).pipe(
readonly pickUpAvailability$: Observable<AvailabilityDTO & { orderDeadline?: string }> = combineLatest([
this.itemData$,
this.branch$,
this.isDownload$,
]).pipe(
tap(() => this.patchState({ fetchingPickUpAvailability: true, fetchingPickUpAvailabilityError: undefined })),
switchMap(([item, branch, isDownload]) =>
!!item && !!branch && !isDownload

View File

@@ -1,10 +1,13 @@
<ng-container *ngIf="filter$ | async; let filter">
<div class="catalog-search-filter-content">
<button class="btn-close" type="button" (click)="close.emit()">
<ui-icon icon="close" size="20px"></ui-icon>
</button>
<div class="w-full flex flex-row justify-end items-center">
<button (click)="clearFilter(filter)" class="text-[#0556B4] mr-[0.8125rem]">Alle Filter entfernen</button>
<button class="text-cool-grey p-4 outline-none border-none bg-transparent" type="button" (click)="close.emit()">
<ui-icon icon="close" size="20px"></ui-icon>
</button>
</div>
<div class="catalog-search-filter-content-main">
<div class="catalog-search-filter-content-main -mt-8">
<h1 class="text-3xl font-bold text-center py-4">Filter</h1>
<ui-filter
[filter]="filter"

View File

@@ -6,10 +6,6 @@
@apply relative mx-auto p-4;
}
.btn-close {
@apply absolute text-cool-grey top-3 p-4 right-4 outline-none border-none bg-transparent;
}
.catalog-search-filter-content-main {
h1.title {
@apply text-center;

View File

@@ -58,4 +58,8 @@ export class ArticleSearchFilterComponent implements OnInit {
const queryParams = { main_qs: value?.getQueryParams()?.main_qs || '' };
this.articleSearch.setDefaultFilter(queryParams);
}
clearFilter(value: UiFilter) {
value.unselectAll();
}
}

View File

@@ -643,6 +643,7 @@ export class CheckoutReviewComponent extends ComponentStore<CheckoutReviewCompon
})
)
.toPromise();
this.router.navigate(['/kunde', this.applicationService.activatedProcessId, 'customer', 'search'], {
queryParams: { filter_customertype: response.filter.customertype },
});

View File

@@ -1,9 +1,12 @@
<div class="goods-out-search-filter-content">
<button class="btn-close" type="button" (click)="close.emit()">
<ui-icon icon="close" size="20px"></ui-icon>
</button>
<div class="w-full flex flex-row justify-end items-center">
<button (click)="clearFilter(filter)" class="text-[#0556B4] mr-[0.8125rem]">Alle Filter entfernen</button>
<button class="text-cool-grey p-4 outline-none border-none bg-transparent" type="button" (click)="close.emit()">
<ui-icon icon="close" size="20px"></ui-icon>
</button>
</div>
<div class="goods-out-search-filter-content-main">
<div class="goods-out-search-filter-content-main -mt-8">
<h1 class="title">Filter</h1>
<ui-filter
[filter]="filter"

View File

@@ -6,10 +6,6 @@
@apply relative mx-auto;
}
.btn-close {
@apply absolute text-inactive-customer top-3 p-4 right-4 outline-none border-none bg-transparent;
}
.goods-out-search-filter-content-main {
@apply px-4;
h1.title {

View File

@@ -121,6 +121,10 @@ export class CustomerOrderSearchFilterComponent implements OnInit, OnDestroy {
this.updateBreadcrumb();
}
clearFilter(value: UiFilter) {
value.unselectAll();
}
getDetailsPath(item: OrderItemListItemDTO) {
return item?.compartmentCode
? `/kunde/${this.processId}/order/details/compartment/${encodeURIComponent(item?.compartmentCode)}/${item?.processingStatus}`

View File

@@ -25,18 +25,31 @@ export class OrderBranchIdInputComponent extends AbstractUiFilterInputDirective
ngOnInit() {
this.control.setValue({ id: Number(this.value) });
const uiInputChangesSub = this.uiInput?.changes?.subscribe((changes) => {
const controlValue = this.control?.value?.id;
const changesValue = Number(changes?.target?.value);
if (controlValue !== changesValue) {
this.control.setValue(changesValue && !isNaN(changesValue) ? { id: changesValue } : undefined);
}
});
const onInputChangeSub = this.onUiInputChange$.subscribe((input) => {
if (this.control.value !== input.value) {
this.control.setValue(input.value ? { id: Number(input.value) } : undefined);
const controlValue = this.control?.value?.id;
const inputValue = Number(input?.value);
if (controlValue !== inputValue) {
this.control.setValue(inputValue && !isNaN(inputValue) ? { id: inputValue } : undefined);
}
});
const onControlValueChangeSub = this.control.valueChanges.subscribe((value) => {
if (this.value !== value) {
this.setValue(value ? String(value?.id) : undefined);
if (!value) {
this.setValue(undefined);
} else if (this.value !== String(value?.id)) {
this.setValue(String(value?.id));
}
});
this._subscriptions.add(uiInputChangesSub);
this._subscriptions.add(onInputChangeSub);
this._subscriptions.add(onControlValueChangeSub);
}

View File

@@ -39,6 +39,8 @@ import {
ReOrderedActionHandler,
CollectOnDeliveryNoteActionHandler,
PrintPriceDiffQrCodeLabelActionHandler,
CollectWithSmallAmountinvoiceActionHandler,
PrintSmallamountinvoiceActionHandler,
} from '@domain/oms';
import { CoreCommandModule } from '@core/command';
import { CustomerOrderRoutingModule } from './customer-order-routing.module';
@@ -89,6 +91,8 @@ import { BreadcrumbModule } from '@shared/components/breadcrumb';
SupplierTemporarilyOutOfStockActionHandler,
CollectOnDeliveryNoteActionHandler,
PrintPriceDiffQrCodeLabelActionHandler,
CollectWithSmallAmountinvoiceActionHandler,
PrintSmallamountinvoiceActionHandler,
]),
],
exports: [CustomerOrderComponent],

View File

@@ -1,29 +1,30 @@
<div class="customer-search-filter-content">
<button class="btn-close" type="button" (click)="close.emit()">
<ui-icon icon="close" size="20px"></ui-icon>
</button>
<div *ngIf="filter$ | async; let filter" class="customer-search-filter-content">
<div class="w-full flex flex-row justify-end items-center">
<button (click)="clearFilter(filter)" class="text-[#0556B4] mr-[0.8125rem]">Alle Filter entfernen</button>
<button class="text-cool-grey p-4 outline-none border-none bg-transparent" type="button" (click)="close.emit()">
<ui-icon icon="close" size="20px"></ui-icon>
</button>
</div>
<h1 class="text-3xl font-bold text-center py-4">Filter</h1>
<h1 class="text-3xl font-bold text-center py-4 -mt-8">Filter</h1>
<ng-container *ngIf="filter$ | async; let filter">
<ui-filter
[filter]="filter"
[loading]="store.fetching$ | async"
(search)="applyFilter(filter)"
[hint]="store.message$ | async"
resizeInputOptionsToElement="page-customer-search-filter .sticky-cta-wrapper"
[scanner]="true"
></ui-filter>
<ui-filter
[filter]="filter"
[loading]="store.fetching$ | async"
(search)="applyFilter(filter)"
[hint]="store.message$ | async"
resizeInputOptionsToElement="page-customer-search-filter .sticky-cta-wrapper"
[scanner]="true"
></ui-filter>
<div class="sticky-cta-wrapper">
<button class="cta-reset-filter" (click)="resetFilter()" [disabled]="store.fetching$ | async">
Filter zurücksetzen
</button>
<button class="apply-filter" (click)="applyFilter(filter)" [disabled]="store.fetching$ | async">
<ui-spinner [show]="store.fetching$ | async">
Filter anwenden
</ui-spinner>
</button>
</div>
</ng-container>
<div class="sticky-cta-wrapper">
<button class="cta-reset-filter" (click)="resetFilter()" [disabled]="store.fetching$ | async">
Filter zurücksetzen
</button>
<button class="apply-filter" (click)="applyFilter(filter)" [disabled]="store.fetching$ | async">
<ui-spinner [show]="store.fetching$ | async">
Filter anwenden
</ui-spinner>
</button>
</div>
</div>

View File

@@ -6,10 +6,6 @@
@apply relative mx-auto p-4;
}
.btn-close {
@apply absolute text-cool-grey top-3 p-4 right-4 outline-none border-none bg-transparent;
}
.sticky-cta-wrapper {
@apply fixed text-center inset-x-0 bottom-0;
bottom: 30px;

View File

@@ -45,6 +45,10 @@ export class CustomerSearchFilterComponent implements OnInit, OnDestroy {
});
}
clearFilter(value: UiFilter) {
value.unselectAll();
}
resetFilter() {
this.store.setDefaultFilter();
}

View File

@@ -1,11 +1,14 @@
<ui-checkbox
*ngIf="customerType !== 'b2b'"
[ngModel]="p4mUser"
(ngModelChange)="setValue({ p4mUser: !p4mUser })"
[disabled]="p4mReadonly || readonly"
>
Kundenkarte
</ui-checkbox>
<ng-container *ifRole="'Store'">
<ui-checkbox
*ngIf="customerType !== 'b2b'"
[ngModel]="p4mUser"
(ngModelChange)="setValue({ p4mUser: !p4mUser })"
[disabled]="p4mReadonly || readonly"
>
Kundenkarte
</ui-checkbox>
</ng-container>
<ng-container *ngFor="let option of filteredOptions$ | async">
<ui-checkbox
*ngIf="option?.enabled !== false"

View File

@@ -4,9 +4,10 @@ import { CommonModule } from '@angular/common';
import { CustomerTypeSelectorComponent } from './customer-type-selector.component';
import { FormsModule } from '@angular/forms';
import { UiCheckboxModule } from '@ui/checkbox';
import { AuthModule } from '@core/auth';
@NgModule({
imports: [CommonModule, FormsModule, UiCheckboxModule],
imports: [CommonModule, FormsModule, UiCheckboxModule, AuthModule],
exports: [CustomerTypeSelectorComponent],
declarations: [CustomerTypeSelectorComponent],
})

View File

@@ -41,6 +41,8 @@ import {
CollectOnDeliveryNoteActionHandler,
CreateReturnItemActionHandler,
PrintPriceDiffQrCodeLabelActionHandler,
CollectWithSmallAmountinvoiceActionHandler,
PrintSmallamountinvoiceActionHandler,
} from '@domain/oms';
@NgModule({
declarations: [GoodsInComponent],
@@ -85,6 +87,8 @@ import {
CollectOnDeliveryNoteActionHandler,
CreateReturnItemActionHandler,
PrintPriceDiffQrCodeLabelActionHandler,
CollectWithSmallAmountinvoiceActionHandler,
PrintSmallamountinvoiceActionHandler,
]),
],
exports: [GoodsInComponent],

View File

@@ -39,6 +39,8 @@ import {
ReOrderedActionHandler,
CollectOnDeliveryNoteActionHandler,
PrintPriceDiffQrCodeLabelActionHandler,
CollectWithSmallAmountinvoiceActionHandler,
PrintSmallamountinvoiceActionHandler,
} from '@domain/oms';
import { CoreCommandModule } from '@core/command';
import { ShellBreadcrumbModule } from '@shell/breadcrumb';
@@ -87,6 +89,8 @@ import { GoodsInRoutingModule } from './good-out-routing.module';
SupplierTemporarilyOutOfStockActionHandler,
CollectOnDeliveryNoteActionHandler,
PrintPriceDiffQrCodeLabelActionHandler,
CollectWithSmallAmountinvoiceActionHandler,
PrintSmallamountinvoiceActionHandler,
]),
],
exports: [GoodsOutComponent],

View File

@@ -25,7 +25,7 @@
</div>
<div class="package-details-list-item__product-details-2" *ngIf="showStockInfos$ | async">
<div class="package-details-list-item__inventory-quantity py-2">
<div class="package-details-list-item__inventory-quantity">
<div class="grow">
<ng-container *ngIf="showStockInfoTooltip$ | async">
<button
@@ -61,6 +61,7 @@
<span class="liefermenge isa-label bg-white text-black px-2 font-bold">{{ item?.quantity }}x</span>
</div>
</div>
<div class="package-details-list-item__ssc-text" *ngIf="item?.quantity == 0 && (sscText$ | async)">MS: {{ sscText$ | async }}</div>
</div>
</div>
</div>

View File

@@ -69,11 +69,15 @@
}
.package-details-list-item__inventory-quantity {
@apply leading-7 flex flex-row items-center;
@apply leading-8 flex flex-row items-center;
}
.package-details-list-item__package-quantity {
@apply leading-9 flex flex-row items-center;
@apply leading-8 flex flex-row items-center;
}
.package-details-list-item__ssc-text {
@apply leading-8 flex flex-row items-center;
}
:host.light {

View File

@@ -24,6 +24,8 @@ export class PackageDetailsListItemComponent implements OnChanges {
inStock$ = this._itemSubject.pipe(switchMap((item) => this._store.getInStockByEan$(item.product.ean)));
sscText$ = this._itemSubject.pipe(switchMap((item) => this._store.getSscTextByEan$(item.product.ean)));
showStockInfoTooltip$ = this._store.arrivalStatus$.pipe(map((arrivalStatus) => arrivalStatus === 0));
showStockInfos$ = this._store.arrivalStatus$.pipe(map((arrivalStatus) => arrivalStatus !== 8));

View File

@@ -65,6 +65,10 @@ export class PackageDetailsListStore extends ComponentStore<PackageDetailsListSt
}
};
static SSC_TEXT_BY_EAN = (ean: string) => (state: PackageDetailsListState) => {
return state.items.find((i) => i.product.ean === ean)?.ssc;
};
static COMPARTMENTY_BY_EAN = (ean: string) => (state: PackageDetailsListState) => {
const stockInfoCompartment = PackageDetailsListStore.STOCKINFO_BY_EAN(ean)(state)?.compartment;
@@ -145,6 +149,10 @@ export class PackageDetailsListStore extends ComponentStore<PackageDetailsListSt
return this.select(PackageDetailsListStore.IN_STOCK_BY_EAN_SELECTOR(ean));
}
getSscTextByEan$(ean: string) {
return this.select(PackageDetailsListStore.SSC_TEXT_BY_EAN(ean));
}
getCompartmentByEan(ean: string) {
return this.get(PackageDetailsListStore.COMPARTMENTY_BY_EAN(ean));
}

View File

@@ -1,5 +1,5 @@
<div class="w-80">
<span class="page-package-list-item__package-number">
<div class="w-64">
<span class="page-package-list-item__package-number truncate">
{{ package?.packageNumber }}
</span>
<span class="page-package-list-item__number-divider" *ngIf="package?.packageNumber && package?.deliveryNoteNumber"> | </span>
@@ -13,7 +13,7 @@
<div class="w-32 page-package-list-item__items-count">{{ package?.items }} <span class="font-normal">Exemplare</span></div>
<div class="text-right grow">
<div
class="rounded inline-block px-4 text-white page-package-list-item__arrival-status"
class="rounded inline-block px-4 text-white page-package-list-item__arrival-status whitespace-nowrap"
[class]="package?.arrivalStatus | arrivalStatusColorClass"
>
{{ package?.arrivalStatus | arrivalStatus }}

View File

@@ -37,7 +37,7 @@ export class WrongDestinationModalComponent {
async done(): Promise<void> {
let promises: Promise<PackageArrivalStatusDTO>[] = [];
for (const pkg of this.packages) {
promises.push(this._packageInspectionService.changePackageStatus(pkg, '0').toPromise());
promises.push(this._packageInspectionService.changePackageStatus(pkg, '8').toPromise());
}
await Promise.all(promises);

View File

@@ -21,7 +21,7 @@
korrekt?
</p>
</div>
<div class="bg-white" *ngSwitchCa se="'Fehlt'">
<div class="bg-white" *ngSwitchCase="'Fehlt'">
<p class="text-center text-xl py-10">
Prüfen Sie bitte stichprobenartig den Filialbestand <br />
des dargestellten Artikels. Ist der angezeigte Filialbestand <br />

View File

@@ -1,5 +1,5 @@
import { Injectable } from '@angular/core';
import { DomainPackageInspectionService } from '@domain/package-inspection';
import { Injectable, OnDestroy } from '@angular/core';
import { DomainPackageInspectionService, PackageStatusChangedEvent } from '@domain/package-inspection';
import { ComponentStore, OnStoreInit, tapResponse } from '@ngrx/component-store';
import { ListResponseArgsOfPackageDTO2, PackageDTO2, QuerySettingsDTO } from '@swagger/wws';
import { UiFilter } from '@ui/filter';
@@ -9,11 +9,15 @@ import { debounceTime, filter, switchMap, takeUntil, tap, withLatestFrom } from
import { INITIAL_RESULT_COMPONENT_STATE, PackageResultComponentState } from './package-result.component.state';
@Injectable()
export class PackageResultComponentStore extends ComponentStore<PackageResultComponentState> implements OnStoreInit {
export class PackageResultComponentStore extends ComponentStore<PackageResultComponentState> implements OnStoreInit, OnDestroy {
showFilter$ = this.select((state) => state.showFilter);
fetching$ = this.select((state) => state.fetching);
get fetching(): boolean {
return this.get((f) => f.fetching);
}
filter$ = this.select((state) => state.filter);
get filter(): UiFilter {
@@ -48,6 +52,17 @@ export class PackageResultComponentStore extends ComponentStore<PackageResultCom
ngrxOnStoreInit() {
this.fetchSettings();
this._packageInspectionService.events.pipe(takeUntil(this.destroy$)).subscribe((event) => {
if (event instanceof PackageStatusChangedEvent) {
this.refresh();
}
});
}
ngOnDestroy() {
super.ngOnDestroy();
this.cancelFetchPackages();
}
setShowFilter = this.updater((state, showFilter: boolean) => ({ ...state, showFilter }));
@@ -66,6 +81,19 @@ export class PackageResultComponentStore extends ComponentStore<PackageResultCom
setTotal = this.updater((state, total: number) => ({ ...state, total }));
refresh = this.effect(($) =>
$.pipe(
debounceTime(250),
withLatestFrom(this.filter$, this.packages$),
tap(() => this.cancelFetchPackages()),
switchMap(([_, filter, packages]) =>
this._packageInspectionService
.queryPackagesResponse({ ...filter.getQueryToken(), skip: 0, take: Math.max(packages.length, 50) })
.pipe(takeUntil(this._cancleFetchPackages$), tapResponse(this.onFetchPackagesResponse, this.onFetchError))
)
)
);
fetchSettings = this.effect(($) =>
$.pipe(
switchMap((_) => this._packageInspectionService.getQuerySettings().pipe(tapResponse(this.onFetchSettingsResponse, this.onFetchError)))

View File

@@ -14,6 +14,7 @@ import { filter, first, map } from 'rxjs/operators';
import { PackageListComponent } from '../components/package-list';
import { PackageResultCacheData } from './package-result-cache-data';
import { PackageResultComponentStore } from './package-result.component.store';
import { PackageInspectionNavigationService } from '../services/package-inspection-navigation.service';
@Component({
selector: 'page-package-result',
@@ -53,7 +54,8 @@ export class PackageResultComponent implements OnInit, AfterViewInit, OnDestroy
private _config: Config,
private _breadcrumb: BreadcrumbService,
private _router: Router,
private _cache: CacheService
private _cache: CacheService,
private _navigation: PackageInspectionNavigationService
) {}
ngOnInit(): void {
@@ -84,7 +86,7 @@ export class PackageResultComponent implements OnInit, AfterViewInit, OnDestroy
const filterSub = combineLatest([initialFilter$, queryParams$]).subscribe(([filter, queryParams]) => {
const restoredFilter = this.restoreFilterFromQueryParams(filter, queryParams);
const restoredData = this.restoreResultsFromCache(restoredFilter);
this.createBreadcrumbIfNotExists(this.store.filter);
this.createBreadcrumbIfNotExists(restoredFilter || this.store.filter);
this.fetchPackages(restoredFilter, { keep: true, take: restoredData?.packages?.length });
});
@@ -155,6 +157,8 @@ export class PackageResultComponent implements OnInit, AfterViewInit, OnDestroy
if (response.result.length === 0) {
this.hint$.next('Keine Pakete gefunden');
return;
} else if (response.result.length === 1) {
this._navigation.details(response.result[0].id).navigate();
}
this.cacheData({});

View File

@@ -1,15 +1,23 @@
import { Injectable } from '@angular/core';
import { PackageInspectionModule } from '../package-inspection.module';
import { Params, Router } from '@angular/router';
@Injectable({ providedIn: PackageInspectionModule })
export interface Navigation {
path: any[];
queryParams?: Params;
navigate: () => Promise<boolean>;
}
@Injectable({ providedIn: 'root' })
export class PackageInspectionNavigationService {
constructor() {}
constructor(private _router: Router) {}
navigateToPackageList(): Promise<void> {
return Promise.resolve();
}
details(id: string): Navigation {
const path = [`/filiale/package-inspection/packages/${id}`];
navigateToPackageDetails(): Promise<void> {
return Promise.resolve();
return {
path,
navigate: () => this._router.navigate(path),
};
}
}

View File

@@ -207,14 +207,18 @@ export class BranchSelectorComponent implements OnInit, OnDestroy, AfterViewInit
clear() {
this.setBranch();
this.emitValues();
this.complete.next('');
this.onChange(undefined);
this.onTouched();
this.valueChange.emit(undefined);
}
emitValues(branch?: BranchDTO) {
this.onChange(branch);
this.onTouched();
this.valueChange.emit(branch);
if (!!branch) {
this.onChange(branch);
this.onTouched();
this.valueChange.emit(branch);
}
}
closeAndClearAutocomplete() {

View File

@@ -18,7 +18,7 @@
</div>
<div class="goods-in-out-header-details">
<h2 class="goods-in-out-header-details-header">
<h2 class="goods-in-out-header-details-header text-2xl">
<div class="goods-in-out-header-details-header-name">
{{ orderItem?.organisation }}
<ng-container *ngIf="!!orderItem?.organisation && (!!orderItem?.firstName || !!orderItem?.lastName)"> - </ng-container>
@@ -30,10 +30,18 @@
</div>
</h2>
<div class="goods-in-out-header-details-paid-marker" *ngIf="orderItem?.features?.paid">
<div class="goods-in-out-header-details-paid-marker" *ngIf="orderItem?.features?.paid && !isKulturpass">
<ui-icon size="12px" icon="check"></ui-icon>
<strong> {{ orderItem?.features?.paid }} </strong>
</div>
<div class="goods-in-out-header-details-paid-marker text-[#26830C]" *ngIf="isKulturpass">
<svg class="fill-current mr-2" xmlns="http://www.w3.org/2000/svg" height="22" viewBox="0 -960 960 960" width="22">
<path
d="M880-740v520q0 24-18 42t-42 18H140q-24 0-42-18t-18-42v-520q0-24 18-42t42-18h680q24 0 42 18t18 42ZM140-631h680v-109H140v109Zm0 129v282h680v-282H140Zm0 282v-520 520Z"
/>
</svg>
<strong> Bezahlt über KulturPass </strong>
</div>
<div class="goods-in-out-header-details-wrapper">
<div class="detail" data-detail-id="VorgangId">
@@ -78,7 +86,7 @@
</div>
<div class="detail" data-detail-id="Bestellkanal">
<div class="label">Bestellkanal</div>
<div class="value">{{ orderItem?.clientChannel | environmentChannel }}</div>
<div class="value">{{ order?.features?.orderSource }}</div>
</div>
<div class="detail justify-space-between" [ngSwitch]="orderItem.processingStatus" data-detail-id="Geaendert">
<ng-container *ifRole="'Store'; else dateCallCenter">
@@ -251,7 +259,7 @@
<button
[uiOverlayTrigger]="deadlineDatepicker"
#deadlineDatepickerTrigger="uiOverlayTrigger"
[disabled]="!!orderItem?.features?.paid || (changeDateDisabled$ | async)"
[disabled]="!isKulturpass && (!!orderItem?.features?.paid || (changeDateDisabled$ | async))"
class="cta-pickup-deadline"
>
<strong>
@@ -280,7 +288,7 @@
<button
[uiOverlayTrigger]="preferredPickUpDatePicker"
#preferredPickUpDatePickerTrigger="uiOverlayTrigger"
[disabled]="!!orderItem?.features?.paid || (changeDateDisabled$ | async)"
[disabled]="(!isKulturpass && !!orderItem?.features?.paid) || (changeDateDisabled$ | async)"
class="cta-pickup-preferred"
>
<strong *ngIf="preferredPickUpDate$ | async; let pickUpDate; else: selectTemplate">

View File

@@ -35,6 +35,10 @@ export class SharedGoodsInOutOrderDetailsHeaderComponent implements OnChanges {
@Input()
selectedOrderItemId: number;
get isKulturpass() {
return this.order?.features?.orderSource === 'KulturPass';
}
minDateDatepicker = this.dateAdapter.addCalendarDays(this.dateAdapter.today(), -1);
today = this.dateAdapter.today();

View File

@@ -175,7 +175,7 @@ export class SharedGoodsInOutOrderDetailsItemComponent extends ComponentStore<Sh
switchMap(([done, orderItem]) =>
this._domainReceiptService
.getReceipts({
receiptType: 65 as ReceiptType,
receiptType: (1 + 64 + 128) as ReceiptType,
ids: [orderItem.orderItemSubsetId],
eagerLoading: 1,
})
@@ -184,7 +184,6 @@ export class SharedGoodsInOutOrderDetailsItemComponent extends ComponentStore<Sh
(res) => {
const receipts = res.result.map((r) => r.item3?.data).filter((f) => !!f);
this.receipts = receipts;
if (typeof done === 'function') {
done?.(receipts);
}

View File

@@ -177,16 +177,15 @@ export class SharedGoodsInOutOrderDetailsComponent extends SharedGoodsInOutOrder
return;
}
let receipts: ReceiptDTO[] = [];
if (action.command.includes('PRINT_SHIPPINGNOTE')) {
if (action.command.includes('PRINT_SHIPPINGNOTE') || action.command === 'PRINT_SMALLAMOUNTINVOICE') {
const receiptsPromise = this.orderDetailsItemComponents.toArray().map(
(timeComponent) =>
(itemCmp) =>
new Promise<ReceiptDTO[]>((resolve) => {
timeComponent.loadReceipts((r) => resolve(r));
itemCmp.loadReceipts((r) => resolve(r));
})
);
receipts = await Promise.all(receiptsPromise).then((r) => r.reduce((acc, val) => acc.concat(val), []));
receipts = unionBy(receipts, 'id');
}

View File

@@ -102,7 +102,7 @@
<button
[uiOverlayTrigger]="deadlineDatepicker"
#deadlineDatepickerTrigger="uiOverlayTrigger"
[disabled]="!!items[i]?.features?.paid"
[disabled]="!isKulturpass(items[i]) && !!items[i]?.features?.paid"
class="date-btn"
type="button"
>

View File

@@ -23,7 +23,6 @@ import { UiSelectOptionComponent } from '@ui/select';
import { BehaviorSubject, Observable, Subscription } from 'rxjs';
import { first, shareReplay } from 'rxjs/operators';
import { ProcessingStatusNameMap } from '../constants/processing-status-name.map';
import { EnvironmentChannelPipe } from '../pipes/environment-channel.pipe';
import { ProcessingStatusPipe } from '../pipes/processing-status.pipe';
import { validateSsc } from '../validators/ssc.validator';
@@ -32,7 +31,7 @@ import { validateSsc } from '../validators/ssc.validator';
templateUrl: 'goods-in-out-order-edit.component.html',
styleUrls: ['goods-in-out-order-edit.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush,
providers: [EnvironmentChannelPipe, ProcessingStatusPipe, DatePipe],
providers: [ProcessingStatusPipe, DatePipe],
})
export class SharedGoodsInOutOrderEditComponent implements OnChanges, OnDestroy {
@ViewChildren('autosize') autosize: QueryList<CdkTextareaAutosize>;
@@ -77,7 +76,6 @@ export class SharedGoodsInOutOrderEditComponent implements OnChanges, OnDestroy
constructor(
private fb: UntypedFormBuilder,
private environmentChannelPipe: EnvironmentChannelPipe,
private processingStatusPipe: ProcessingStatusPipe,
private datePipe: DatePipe,
private omsService: DomainOmsService,
@@ -101,7 +99,11 @@ export class SharedGoodsInOutOrderEditComponent implements OnChanges, OnDestroy
}
}
initForm(items: OrderItemListItemDTO[]) {
isKulturpass(item: OrderItemListItemDTO): boolean {
return item?.['externalRepositories']?.includes('kulturpass');
}
async initForm(items: OrderItemListItemDTO[]) {
if (items.length === 0) {
return;
}
@@ -111,7 +113,7 @@ export class SharedGoodsInOutOrderEditComponent implements OnChanges, OnDestroy
orderId: fb.control({ value: items[0].orderId, disabled: true }),
orderNumber: fb.control({ value: items[0].orderNumber, disabled: true }),
orderDate: fb.control({ value: this.datePipe.transform(items[0].orderDate), disabled: true }),
clientChannel: fb.control({ value: this.environmentChannelPipe.transform(items[0].clientChannel), disabled: true }),
clientChannel: fb.control({ value: (await this.getOrderSource()) ?? items[0].features?.orderSource, disabled: true }),
buyerNumber: fb.control({ value: items[0].buyerNumber, disabled: true }),
items: fb.array([]),
notificationChannel: this.notificationsGroup,
@@ -172,8 +174,7 @@ export class SharedGoodsInOutOrderEditComponent implements OnChanges, OnDestroy
}
async updateNotificationsGroup() {
const control = this.control?.getRawValue();
const orderId = control?.orderId;
const orderId = this.items[0].orderId;
try {
if (orderId) {
@@ -192,6 +193,11 @@ export class SharedGoodsInOutOrderEditComponent implements OnChanges, OnDestroy
await this.omsService.updateNotifications(orderId, this.notificationsGroup.getRawValue()).toPromise();
}
async getOrderSource() {
const orderId = this.items[0].orderId;
return this.omsService.getOrderSource(+orderId).toPromise();
}
changeEstimatedDeliveryDate(date: Date, item: OrderItemListItemDTO) {
if (!date) {
return;

View File

@@ -2,10 +2,18 @@
{{ item?.compartmentCode }}
{{ item.compartmentInfo && '_' + item.compartmentInfo }}
</div>
<div class="goods-in-out-order-group-item-paid" *ngIf="item.features?.paid">
<div class="goods-in-out-order-group-item-paid" *ngIf="item.features?.paid && !isKulturpass">
<ui-icon size="12px" icon="check"></ui-icon>
<strong> {{ item.features?.paid }} </strong>
</div>
<div class="goods-in-out-order-group-item-paid text-[#26830C]" *ngIf="isKulturpass">
<svg class="fill-current mr-2" xmlns="http://www.w3.org/2000/svg" height="22" viewBox="0 -960 960 960" width="22">
<path
d="M880-740v520q0 24-18 42t-42 18H140q-24 0-42-18t-18-42v-520q0-24 18-42t42-18h680q24 0 42 18t18 42ZM140-631h680v-109H140v109Zm0 129v282h680v-282H140Zm0 282v-520 520Z"
/>
</svg>
<strong> Bezahlt über KulturPass </strong>
</div>
<div class="goods-in-out-order-group-item-details">
<div class="goods-in-out-order-group-item-details-thumbnail">
<img loading="lazy" *ngIf="item?.product?.ean | productImage; let productImage" [src]="productImage" [alt]="item?.product?.name" />

View File

@@ -84,6 +84,10 @@ export class GoodsInOutOrderGroupItemComponent extends ComponentStore<GoodsInOut
return (this.item as any)?.cruda;
}
get isKulturpass() {
return this.item?.['externalRepositories']?.includes('kulturpass');
}
constructor() {
super({
selected: false,

View File

@@ -1,35 +0,0 @@
import { Injectable, Pipe, PipeTransform } from '@angular/core';
import { EnvironmentChannel } from '@swagger/oms';
@Pipe({
name: 'environmentChannel',
})
@Injectable()
export class EnvironmentChannelPipe implements PipeTransform {
channels = new Map([
[0, ''],
[2, 'Filiale'],
[4, 'HSC'],
[8, 'Online'],
]);
transform(clientChannel: EnvironmentChannel = 0, join: string = ' '): any {
if (clientChannel === 0) {
return this.channels.get(0);
}
const channelOnlineMobileApp: number = clientChannel;
if (channelOnlineMobileApp === 8 || channelOnlineMobileApp === 16 || channelOnlineMobileApp === 24) {
return 'hugendubel.de';
}
const channels: string[] = [];
this.channels.forEach((value, key) => {
// tslint:disable-next-line: no-bitwise
if ((clientChannel | key) === key) {
channels.push(value);
}
});
return channels.join(join);
}
}

View File

@@ -1,5 +1,4 @@
import { NgModule } from '@angular/core';
import { EnvironmentChannelPipe } from './environment-channel.pipe';
import { ProcessingStatusOptionsKeyValuePipe, ProcessingStatusOptionsPipe } from './processing-status-options.pipe';
import { PaymentTypePipe } from './payment-type.pipe';
import { ProcessingStatusPipe } from './processing-status.pipe';
@@ -18,7 +17,6 @@ import { AddToPreviousCompartmentCodeLabelPipe } from './add-to-prevoius-compart
ProcessingStatusPipe,
TitlePipe,
ShowCompartmentCodePipe,
EnvironmentChannelPipe,
ProcessingStatusOptionsKeyValuePipe,
PaymentTypePipe,
ShowTagsPipe,
@@ -33,7 +31,6 @@ import { AddToPreviousCompartmentCodeLabelPipe } from './add-to-prevoius-compart
ProcessingStatusPipe,
TitlePipe,
ShowCompartmentCodePipe,
EnvironmentChannelPipe,
ProcessingStatusOptionsKeyValuePipe,
PaymentTypePipe,
ShowTagsPipe,

View File

@@ -47,3 +47,19 @@
.fancy-checkbox:checked::after {
@apply opacity-100;
}
.shared-purchase-options-list-item__availability ui-svg-icon {
@apply bg-[#D8DFE5] w-7 h-7 rounded-card rounded-br-none grid items-center justify-center;
}
.shared-purchase-options-list-item__availability ui-svg-icon.active {
@apply bg-[#596470] text-white;
}
::ng-deep shared-purchase-options-list-item ui-select .ui-input-wrapper {
@apply h-full;
}
::ng-deep shared-purchase-options-list-item ui-select .ui-select-toggle {
@apply ml-2;
}

View File

@@ -48,8 +48,26 @@
{{ availability.data.estimatedShippingDate | date: 'dd. MMMM yyyy' }}
</ng-container>
<ng-container *ngSwitchCase="'pickup'">
<ui-svg-icon icon="isa-box-out" [size]="18"></ui-svg-icon>
<ui-svg-icon
icon="isa-box-out"
[size]="18"
#trigger="uiOverlayTrigger"
class="cursor-pointer"
[class.active]="trigger.opened"
[uiOverlayTrigger]="orderDedlineTooltip"
></ui-svg-icon>
{{ availability.data.estimatedShippingDate | date: 'dd. MMMM yyyy' }}
<ui-tooltip
#orderDedlineTooltip
yPosition="above"
xPosition="after"
[yOffset]="-11"
[xOffset]="4"
[warning]="true"
[closeable]="true"
>
<b>{{ availability.data?.orderDeadline | orderDeadline }}</b>
</ui-tooltip>
</ng-container>
<ng-container *ngSwitchCase="'in-store'">
<ui-svg-icon icon="isa-shopping-bag" [size]="18"></ui-svg-icon>
@@ -64,8 +82,50 @@
</div>
</div>
<div class="shared-purchase-options-list-item__price text-right ml-4 flex flex-col items-end">
<div class="shared-purchase-options-list-item__price-value font-bold text-xl" *ngIf="!(canEditPrice$ | async)">
{{ priceValue$ | async | currency: 'EUR':'code' }}
<div
class="shared-purchase-options-list-item__price-value font-bold text-xl flex flex-row items-center"
*ngIf="!(canEditPrice$ | async)"
>
<ui-svg-icon class="mr-3" [uiOverlayTrigger]="tooltip" icon="mat-info" *ngIf="priceMaintained$ | async"></ui-svg-icon>
<ui-tooltip #tooltip yPosition="above" xPosition="after" [yOffset]="-8" [xOffset]="5" [closeable]="true">
Günstigerer Preis aus Hugendubel Katalog wird übernommen
</ui-tooltip>
<ng-container *ngIf="!(setManualPrice$ | async); else setManualPrice">
{{ priceValue$ | async | currency: 'EUR':'code' }}
</ng-container>
<ng-template #setManualPrice>
<div class="relative flex flex-row items-start">
<ui-select
class="w-[6.5rem] min-h-[3.4375rem] p-4 rounded-card border border-solid border-[#AEB7C1] mr-4"
tabindex="-1"
[formControl]="manualVatFormControl"
[defaultLabel]="'MwSt'"
>
<ui-select-option *ngFor="let vat of vats$ | async" [label]="vat.name + '%'" [value]="vat.vatType"></ui-select-option>
</ui-select>
<shared-input-control [class.ml-8]="manualPriceFormControl?.invalid && manualPriceFormControl?.dirty">
<shared-input-control-indicator>
<ui-svg-icon *ngIf="manualPriceFormControl?.invalid && manualPriceFormControl?.dirty" icon="mat-info"></ui-svg-icon>
</shared-input-control-indicator>
<input
triggerOn="init"
#quantityInput
sharedInputControlInput
type="string"
class="w-24"
[formControl]="manualPriceFormControl"
placeholder="00,00"
(sharedOnInit)="quantityInput.focus()"
sharedNumberValue
/>
<shared-input-control-suffix>EUR</shared-input-control-suffix>
<shared-input-control-error error="required">Preis ist ungültig</shared-input-control-error>
<shared-input-control-error error="pattern">Preis ist ungültig</shared-input-control-error>
<shared-input-control-error error="max">Preis ist ungültig</shared-input-control-error>
</shared-input-control>
</div>
</ng-template>
</div>
<div class="shared-purchase-options-list-item__price-value font-bold text-xl" *ngIf="canEditPrice$ | async">
<div class="relative flex flex-col">

View File

@@ -21,9 +21,12 @@ import { UiQuantityDropdownModule } from '@ui/quantity-dropdown';
import { UiSpinnerModule } from '@ui/spinner';
import { UiTooltipModule } from '@ui/tooltip';
import { combineLatest, ReplaySubject, Subscription } from 'rxjs';
import { map, startWith, switchMap } from 'rxjs/operators';
import { map, take, shareReplay, startWith, switchMap, withLatestFrom, last } from 'rxjs/operators';
import { GIFT_CARD_MAX_PRICE, PRICE_PATTERN } from '../constants';
import { Item, PurchaseOptionsStore } from '../store';
import { OrderDeadlinePipeModule } from '@shared/pipes/order-deadline';
import { UiSelectModule } from '@ui/select';
import { KeyValueDTOOfStringAndString } from '@swagger/cat';
@Component({
selector: 'scale-content, [scaleContent]',
@@ -33,7 +36,7 @@ import { Item, PurchaseOptionsStore } from '../store';
styles: [
`
:host {
overflow: hidden;
overflow-y: hidden;
}
`,
],
@@ -43,7 +46,7 @@ export class ScaleContentComponent implements AfterContentInit {
fontSizeInEm = 1;
adjustmentSteps = 0.1;
adjustmentSteps = 0.05;
constructor(private _elementRef: ElementRef<HTMLElement>, private _renderer: Renderer2) {}
@@ -54,10 +57,12 @@ export class ScaleContentComponent implements AfterContentInit {
adjustFontSize() {
const element = this._elementRef.nativeElement;
const clientRect = element.getClientRects();
const scrollHeight = element.scrollHeight;
const clientRect = element?.getClientRects();
const scrollHeight = element?.scrollHeight;
if (clientRect[0].height < scrollHeight) {
const domRect = clientRect && clientRect[0];
if (domRect && Math.ceil(domRect?.height) < scrollHeight) {
this.fontSizeInEm -= this.adjustmentSteps;
} else {
return;
@@ -78,6 +83,7 @@ export class ScaleContentComponent implements AfterContentInit {
imports: [
CommonModule,
UiQuantityDropdownModule,
UiSelectModule,
ProductImageModule,
UiIconModule,
UiSpinnerModule,
@@ -88,6 +94,7 @@ export class ScaleContentComponent implements AfterContentInit {
UiTooltipModule,
UiCommonModule,
ScaleContentComponent,
OrderDeadlinePipeModule,
],
host: { class: 'shared-purchase-options-list-item' },
})
@@ -108,7 +115,15 @@ export class PurchaseOptionsListItemComponent implements OnInit, OnDestroy, OnCh
quantityFormControl = new FormControl<number>(null);
priceFormControl = new FormControl<string>(null, [Validators.required, Validators.min(1), Validators.max(GIFT_CARD_MAX_PRICE)]);
priceFormControl = new FormControl<string>(null, [
Validators.required,
Validators.min(1),
Validators.max(GIFT_CARD_MAX_PRICE),
Validators.pattern(PRICE_PATTERN),
]);
manualPriceFormControl = new FormControl<string>(null, [Validators.required, Validators.max(999.99), Validators.pattern(PRICE_PATTERN)]);
manualVatFormControl = new FormControl<string>('', [Validators.required]);
selectedFormControl = new FormControl<boolean>(false);
@@ -121,9 +136,27 @@ export class PurchaseOptionsListItemComponent implements OnInit, OnDestroy, OnCh
price$ = this.item$.pipe(switchMap((item) => this._store.getPrice$(item.id)));
priceMaintained$ = this.price$.pipe(map((price) => price?.fromCatalogue));
priceValue$ = this.price$.pipe(map((price) => price?.value?.value));
priceVat$ = this.price$.pipe(map((price) => price?.vat?.value));
vats$ = this._store.vats$.pipe(shareReplay());
// Ticket #4074 analog zu Ticket #2244
// take(2) um die Response des Katalogpreises und danach um die Response der OLAs abzuwarten
// Logik gilt ausschließlich für Archivartikel
setManualPrice$ = this.price$.pipe(
take(2),
map((price) => {
// Logik nur beim Hinzufügen über Kaufoptionen, da über Ändern im Warenkorb die Info fehlt ob das jeweilige ShoppingCartItem ein Archivartikel ist oder nicht
const features = this.item?.features as KeyValueDTOOfStringAndString[];
if (!!features && Array.isArray(features)) {
const isArchive = !!features?.find((feature) => feature?.enabled === true && feature?.key === 'ARC') ?? false;
return isArchive ? !price?.value?.value || price?.vat === undefined : false;
}
return false;
})
);
canEditPrice$ = this.item$.pipe(switchMap((item) => this._store.getCanEditPrice$(item.id)));
@@ -170,11 +203,11 @@ export class PurchaseOptionsListItemComponent implements OnInit, OnDestroy, OnCh
constructor(private _store: PurchaseOptionsStore) {}
// Wichtig für das korrekte Setzen des Preises an das Item für den Endpoint request
parsePrice(value: string) {
if (PRICE_PATTERN.test(value)) {
return parseFloat(value.replace(',', '.'));
}
this.priceFormControl.setErrors({ pattern: true });
}
stringifyPrice(value: number) {
@@ -193,6 +226,7 @@ export class PurchaseOptionsListItemComponent implements OnInit, OnDestroy, OnCh
this.initQuantitySubscription();
this.initPriceSubscription();
this.initSelectedSubscription();
this.initManualPriceSubscriptions();
}
ngOnChanges({ item }: SimpleChanges) {
@@ -206,6 +240,16 @@ export class PurchaseOptionsListItemComponent implements OnInit, OnDestroy, OnCh
this._subscriptions.unsubscribe();
}
// Ticket #4074 analog zu Ticket #2244
// Logik gilt ausschließlich für Archivartikel und über die Kaufoptionen. Nicht über den Warenkorb
async initManualPriceSubscriptions() {
const isManualPrice = await this.setManualPrice$.pipe(last()).toPromise();
if (!!isManualPrice) {
this.initManualPriceSubscription();
this.initManualVatSubscription();
}
}
initQuantitySubscription() {
const sub = this.item$.subscribe((item) => {
if (this.quantityFormControl.value !== item.quantity) {
@@ -250,6 +294,51 @@ export class PurchaseOptionsListItemComponent implements OnInit, OnDestroy, OnCh
this._subscriptions.add(valueChangesSub);
}
initManualPriceSubscription() {
const sub = this.price$.subscribe((price) => {
const priceStr = this.stringifyPrice(price?.value?.value);
if (priceStr === '') return;
if (this.parsePrice(this.manualPriceFormControl.value) !== price?.value?.value) {
this.manualPriceFormControl.setValue(priceStr);
}
});
const valueChangesSub = this.manualPriceFormControl.valueChanges.subscribe((value) => {
const price = this._store.getPrice(this.item.id);
const parsedPrice = this.parsePrice(value);
if (!parsedPrice) {
this._store.setPrice(this.item.id, null, true);
return;
}
if (price[this.item.id] !== parsedPrice) {
this._store.setPrice(this.item.id, this.parsePrice(value), true);
}
});
this._subscriptions.add(sub);
this._subscriptions.add(valueChangesSub);
}
initManualVatSubscription() {
const valueChangesSub = this.manualVatFormControl.valueChanges.pipe(withLatestFrom(this.vats$)).subscribe(([formVatType, vats]) => {
const price = this._store.getPrice(this.item.id);
const vat = vats.find((vat) => vat?.vatType === Number(formVatType));
if (!vat) {
this._store.setVat(this.item.id, null);
return;
}
if (price[this.item.id]?.vat?.vatType !== vat?.vatType) {
this._store.setVat(this.item.id, vat);
}
});
this._subscriptions.add(valueChangesSub);
}
initSelectedSubscription() {
const sub = this.item$
.pipe(switchMap((item) => this._store.selectedItemIds$.pipe(map((ids) => ids.includes(item.id)))))

View File

@@ -3,15 +3,6 @@
Wie möchten Sie die Artikel erhalten?
</p>
<div class="rounded p-4 shadow-card mt-4 grid grid-flow-col gap-4 justify-center items-center relative">
<!-- <ng-container *ngFor="let option of purchasingOptions$ | async">
<ng-container [ngSwitch]="option">
<app-delivery-purchase-options-tile *ngSwitchCase="'delivery'"> </app-delivery-purchase-options-tile>
<app-in-store-purchase-options-tile *ngSwitchCase="'in-store'"> </app-in-store-purchase-options-tile>
<app-pickup-purchase-options-tile *ngSwitchCase="'pickup'"> </app-pickup-purchase-options-tile>
<app-download-purchase-options-tile *ngSwitchCase="'download'"> </app-download-purchase-options-tile>
</ng-container>
</ng-container> -->
<ng-container *ngIf="!(isDownloadOnly$ | async)">
<ng-container *ngIf="!(isGiftCardOnly$ | async)">
<app-in-store-purchase-options-tile> </app-in-store-purchase-options-tile>
@@ -32,13 +23,18 @@
</div>
<div class="text-center -mx-4 border-t border-gray-200 p-4 border-solid">
<ng-container *ngIf="type === 'add'">
<button type="button" class="isa-cta-button" [disabled]="!(canContinue$ | async) || saving" (click)="save('continue-shopping')">
<button
type="button"
class="isa-cta-button"
[disabled]="!(canContinue$ | async) || saving || !(hasPrice$ | async)"
(click)="save('continue-shopping')"
>
Weiter einkaufen
</button>
<button
type="button"
class="ml-4 isa-cta-button isa-button-primary"
[disabled]="!(canContinue$ | async) || saving"
[disabled]="!(canContinue$ | async) || saving || !(hasPrice$ | async)"
(click)="save('continue')"
>
Fortfahren
@@ -48,7 +44,7 @@
<button
type="button"
class="ml-4 isa-cta-button isa-button-primary"
[disabled]="!(canContinue$ | async) || saving"
[disabled]="!(canContinue$ | async) || saving || !(hasPrice$ | async)"
(click)="save('continue')"
>
Fortfahren

View File

@@ -5,7 +5,7 @@ import { PurchaseOptionsModalData } from './purchase-options-modal.data';
import { PurchaseOptionsListHeaderComponent } from './purchase-options-list-header';
import { PurchaseOptionsListItemComponent } from './purchase-options-list-item';
import { CommonModule } from '@angular/common';
import { Subject } from 'rxjs';
import { Subject, zip } from 'rxjs';
import {
DeliveryPurchaseOptionTileComponent,
DownloadPurchaseOptionTileComponent,
@@ -13,7 +13,8 @@ import {
PickupPurchaseOptionTileComponent,
} from './purchase-options-tile';
import { isGiftCard, Item, PurchaseOptionsStore } from './store';
import { delay, map, shareReplay, skip, takeUntil } from 'rxjs/operators';
import { delay, map, shareReplay, skip, switchMap, takeUntil } from 'rxjs/operators';
import { KeyValueDTOOfStringAndString } from '@swagger/cat';
@Component({
selector: 'shared-purchase-options-modal',
@@ -44,6 +45,34 @@ export class PurchaseOptionsModalComponent implements OnInit, OnDestroy {
items$ = this.store.items$;
hasPrice$ = this.items$.pipe(
switchMap((items) =>
items.map((item) => {
let isArchive = false;
const features = item?.features as KeyValueDTOOfStringAndString[];
// Ticket #4074 analog zu Ticket #2244
// Ob Archivartikel kann nur über Kaufoptionen herausgefunden werden, nicht über Ändern im Warenkorb da am ShoppingCartItem das Archivartikel Feature fehlt
if (!!features && Array.isArray(features)) {
isArchive = !!features?.find((feature) => feature?.enabled === true && feature?.key === 'ARC') ?? false;
}
return zip(
this.store
?.getPrice$(item?.id)
.pipe(
map((price) =>
isArchive ? !!price?.value?.value && price?.vat !== undefined && price?.vat?.value !== undefined : !!price?.value?.value
)
)
);
})
),
switchMap((hasPrices) => hasPrices),
map((hasPrices) => {
const containsItemWithNoPrice = hasPrices?.filter((hasPrice) => hasPrice === false) ?? [];
return containsItemWithNoPrice?.length === 0;
})
);
purchasingOptions$ = this.store.getPurchaseOptionsInAvailabilities$;
isDownloadOnly$ = this.purchasingOptions$.pipe(

View File

@@ -6,4 +6,6 @@ export interface PurchaseOptionsModalData {
processId: number;
type: ActionType;
items: Array<ItemDTO | ShoppingCartItemDTO>;
pickupBranch?: BranchDTO;
inStoreBranch?: BranchDTO;
}

View File

@@ -1,12 +1,12 @@
<div class="purchase-options-tile__heading">
<div class="purchase-options-tile__heading" [class.muted]="hasNoInStoreAvailability$ | async">
<div class="icon-wrapper">
<ui-svg-icon icon="isa-truck"></ui-svg-icon>
</div>
<span class="purchase-option-name">Versand</span>
</div>
<div class="purchase-options-tile__body">
<div class="purchase-options-tile__body" [class.muted]="hasNoInStoreAvailability$ | async">
Artikel geliefert bekommen?
</div>
<div class="purchase-options-tile__actions">
<div class="purchase-options-tile__actions" [class.muted]="hasNoInStoreAvailability$ | async">
<span>Versandkostenfrei</span>
</div>

View File

@@ -3,6 +3,7 @@ import { Component, ChangeDetectionStrategy, ChangeDetectorRef } from '@angular/
import { UiIconModule } from '@ui/icon';
import { PurchaseOptionsStore } from '../store';
import { BasePurchaseOptionDirective } from './base-purchase-option.directive';
import { map, shareReplay } from 'rxjs/operators';
@Component({
selector: 'app-delivery-purchase-options-tile',
@@ -13,6 +14,11 @@ import { BasePurchaseOptionDirective } from './base-purchase-option.directive';
imports: [CommonModule, UiIconModule],
})
export class DeliveryPurchaseOptionTileComponent extends BasePurchaseOptionDirective {
hasNoInStoreAvailability$ = this.store.purchasingOptions$.pipe(
map((po) => !(po.includes('delivery') || po.includes('b2b-delivery') || po.includes('dig-delivery'))),
shareReplay(1)
);
constructor(protected store: PurchaseOptionsStore, protected cdr: ChangeDetectorRef) {
super('delivery');
}

View File

@@ -1,10 +1,10 @@
<div class="purchase-options-tile__heading">
<div class="purchase-options-tile__heading" [class.muted]="hasNoInStoreAvailability$ | async">
<div class="icon-wrapper">
<ui-svg-icon icon="isa-shopping-bag"></ui-svg-icon>
</div>
<span class="purchase-option-name">Rücklage</span>
</div>
<div class="purchase-options-tile__body">
<div class="purchase-options-tile__body" [class.muted]="hasNoInStoreAvailability$ | async">
Artikel zurücklegen lassen oder sofort mitnehmen?
</div>
<div class="purchase-options-tile__actions group">

View File

@@ -8,6 +8,7 @@ import { UiIconModule } from '@ui/icon';
import { PurchaseOptionsStore } from '../store';
import { BasePurchaseOptionDirective } from './base-purchase-option.directive';
import { AuthService } from '@core/auth';
import { map, shareReplay } from 'rxjs/operators';
@Component({
selector: 'app-in-store-purchase-options-tile',
@@ -21,6 +22,11 @@ import { AuthService } from '@core/auth';
export class InStorePurchaseOptionTileComponent extends BasePurchaseOptionDirective {
inStoreBranch$ = this.store.inStoreBranch$;
hasNoInStoreAvailability$ = this.store.purchasingOptions$.pipe(
map((po) => !po.includes('in-store')),
shareReplay(1)
);
constructor(
protected store: PurchaseOptionsStore,
protected cdr: ChangeDetectorRef,

View File

@@ -1,10 +1,10 @@
<div class="purchase-options-tile__heading">
<div class="purchase-options-tile__heading" [class.muted]="hasNoInStoreAvailability$ | async">
<div class="icon-wrapper">
<ui-svg-icon icon="isa-box-out"></ui-svg-icon>
</div>
<span class="purchase-option-name">Abholung</span>
</div>
<div class="purchase-options-tile__body">
<div class="purchase-options-tile__body" [class.muted]="hasNoInStoreAvailability$ | async">
Artikel in einer unserer Filialen abholen?
</div>
<div class="purchase-options-tile__actions group">

View File

@@ -8,6 +8,7 @@ import { UiIconModule } from '@ui/icon';
import { PurchaseOptionsStore } from '../store';
import { BasePurchaseOptionDirective } from './base-purchase-option.directive';
import { AuthService } from '@core/auth';
import { map, shareReplay } from 'rxjs/operators';
@Component({
selector: 'app-pickup-purchase-options-tile',
@@ -21,6 +22,11 @@ import { AuthService } from '@core/auth';
export class PickupPurchaseOptionTileComponent extends BasePurchaseOptionDirective {
pickupBranch$ = this.store.pickupBranch$;
hasNoInStoreAvailability$ = this.store.purchasingOptions$.pipe(
map((po) => !po.includes('pickup')),
shareReplay(1)
);
constructor(
protected store: PurchaseOptionsStore,
protected cdr: ChangeDetectorRef,

View File

@@ -18,6 +18,10 @@
@apply bg-[#D8DFE5] w-[36px] h-[36px] rounded grid items-center justify-center;
}
.purchase-options-tile__heading.muted .icon-wrapper {
@apply bg-transparent;
}
.purchase-options-tile__heading .purchase-option-name {
@apply ml-3 font-bold text-lg;
}
@@ -30,6 +34,10 @@
@apply text-center font-bold;
}
.muted {
@apply text-[#AEB7C1];
}
::ng-deep app-pickup-purchase-options-tile shared-branch-selector.focused,
::ng-deep app-in-store-purchase-options-tile shared-branch-selector.focused {
@apply visible;

View File

@@ -178,14 +178,16 @@ export function getItemsForList(state: PurchaseOptionsState): Item[] {
return items.filter((item) => itemIds.includes(item.id));
}
export function getAvailabilitiesForItem(itemId: number): (state: PurchaseOptionsState) => Availability[] {
export function getAvailabilitiesForItem(
itemId: number,
allDeliveryAvailabilities?: boolean
): (state: PurchaseOptionsState) => Availability[] {
return (state) => {
let availabilities = getAvailabilities(state);
availabilities = availabilities.filter((availability) => availability.itemId === itemId);
// if 'delivery', 'dig-delivery' and 'b2b-delivery' are present remove 'dig-delivery' and 'b2b-delivery'
if (availabilities.some((availability) => availability.purchaseOption === 'delivery')) {
if (!allDeliveryAvailabilities && availabilities.some((availability) => availability.purchaseOption === 'delivery')) {
availabilities = availabilities.filter(
(availability) => availability.purchaseOption !== 'dig-delivery' && availability.purchaseOption !== 'b2b-delivery'
);
@@ -204,7 +206,10 @@ export function getCanEditPrice(itemId: number): (state: PurchaseOptionsState) =
};
}
export function getPriceForPurchaseOption(itemId: number, purchaseOption: PurchaseOption): (state: PurchaseOptionsState) => PriceDTO {
export function getPriceForPurchaseOption(
itemId: number,
purchaseOption: PurchaseOption
): (state: PurchaseOptionsState) => PriceDTO & { fromCatalogue?: boolean } {
return (state) => {
const price = getPrices(state)[itemId];
@@ -212,6 +217,10 @@ export function getPriceForPurchaseOption(itemId: number, purchaseOption: Purcha
return price;
}
const item = getItems(state).find((item) => item.id === itemId);
const type = getType(state);
let availabilities = getAvailabilitiesForItem(itemId)(state);
let availability = availabilities.find((availability) => availability.purchaseOption === purchaseOption);
@@ -227,15 +236,26 @@ export function getPriceForPurchaseOption(itemId: number, purchaseOption: Purcha
}
availability = availability || digAvailability || b2bAvailability;
if (!availability?.data?.priceMaintained) {
// if price is not maintained, use the cheaper price
if (isItemDTO(item, type)) {
const catalogAvailability = item?.catalogAvailability?.price;
if (
catalogAvailability?.value?.value &&
availability?.data?.price?.value?.value &&
catalogAvailability?.value?.value < availability?.data?.price?.value?.value
) {
return { ...catalogAvailability, fromCatalogue: true };
}
}
}
}
if (availability && availability.data.price?.value?.value) {
return availability.data.price ?? DEFAULT_PRICE_DTO;
if (availability && availability.data.price?.value?.value && availability.data.price) {
return availability.data.price;
}
const item = getItems(state).find((item) => item.id === itemId);
const type = getType(state);
if (isItemDTO(item, type)) {
return item?.catalogAvailability?.price ?? DEFAULT_PRICE_DTO;
} else {
@@ -280,17 +300,17 @@ export function getAvailabilityWithPurchaseOption(
purchaseOption: PurchaseOption
): (state: PurchaseOptionsState) => Availability {
return (state) => {
let availabilities = getAvailabilitiesForItem(itemId)(state);
let availabilities = getAvailabilitiesForItem(itemId, true)(state);
let availability = availabilities.find((availability) => availability.purchaseOption === purchaseOption);
if (purchaseOption === 'delivery') {
const digAvailability = availabilities.find((availability) => availability.purchaseOption === 'dig-delivery');
const b2bAvailability = availabilities.find((availability) => availability.purchaseOption === 'b2b-delivery');
availability = availability || digAvailability || b2bAvailability;
if (!(digAvailability && b2bAvailability)) {
availability = digAvailability ?? b2bAvailability;
}
}
return availability;
};
}
@@ -327,7 +347,6 @@ export function canContinue(state: PurchaseOptionsState): boolean {
if (isGiftCard(item, getType(state))) {
const price = getPriceForPurchaseOption(item.id, purchaseOption)(state);
if (!(price?.value?.value > 0 && price?.value?.value <= GIFT_CARD_MAX_PRICE)) {
console.log('price', price);
return false;
}
}

View File

@@ -11,21 +11,27 @@ import {
UpdateShoppingCartItemDTO,
} from '@swagger/checkout';
import { Observable } from 'rxjs';
import { map, shareReplay, switchMap, take, tap } from 'rxjs/operators';
import { map, shareReplay, take } from 'rxjs/operators';
import { Branch, ItemData } from './purchase-options.types';
import { memorize } from '@utils/common';
import { AuthService } from '@core/auth';
import { ApplicationService } from '@core/application';
import { DomainOmsService } from '@domain/oms';
@Injectable({ providedIn: 'root' })
export class PurchaseOptionsService {
constructor(
private _availabilityService: DomainAvailabilityService,
private _checkoutService: DomainCheckoutService,
private _omsService: DomainOmsService,
private _auth: AuthService,
private _app: ApplicationService
) {}
getVats$() {
return this._omsService.getVATs();
}
getSelectedBranchForProcess(processId: number): Observable<Branch> {
return this._app.getSelectedBranch$(processId).pipe(take(1), shareReplay(1));
}

View File

@@ -26,9 +26,10 @@ import {
} from './purchase-options.helpers';
import { Observable } from 'rxjs';
import { switchMap } from 'rxjs/operators';
import { DEFAULT_PRICE_DTO, DEFAULT_PRICE_VALUE } from '../constants';
import { DEFAULT_PRICE_DTO, DEFAULT_PRICE_VALUE, DEFAULT_VAT_VALUE } from '../constants';
import { AddToShoppingCartDTO, EntityDTOContainerOfDestinationDTO, UpdateShoppingCartItemDTO } from '@swagger/checkout';
import { isEqual, uniqueId } from 'lodash';
import { uniqueId } from 'lodash';
import { VATDTO } from '@swagger/oms';
@Injectable()
export class PurchaseOptionsStore extends ComponentStore<PurchaseOptionsState> {
@@ -128,6 +129,10 @@ export class PurchaseOptionsStore extends ComponentStore<PurchaseOptionsState> {
fetchingAvailabilities$ = this.select(Selectors.getFetchingAvailabilities);
get vats$() {
return this._service.getVats$();
}
constructor(private _service: PurchaseOptionsService) {
super({
defaultBranch: undefined,
@@ -160,7 +165,7 @@ export class PurchaseOptionsStore extends ComponentStore<PurchaseOptionsState> {
};
});
async initialize({ items, processId, type }: PurchaseOptionsModalData) {
async initialize({ items, processId, type, inStoreBranch, pickupBranch }: PurchaseOptionsModalData) {
const selectedBranch = await this._service.getSelectedBranchForProcess(processId).toPromise();
const defaultBranch = await this._service.fetchDefaultBranch().toPromise();
const customerFeatures = await this._service.getCustomerFeatures(processId).toPromise();
@@ -170,8 +175,8 @@ export class PurchaseOptionsStore extends ComponentStore<PurchaseOptionsState> {
type: type,
items: items.map((item) => ({ ...item, quantity: item['quantity'] ?? 1 })),
defaultBranch: selectedBranch ?? defaultBranch,
pickupBranch: selectedBranch,
inStoreBranch: selectedBranch,
pickupBranch: pickupBranch ?? selectedBranch,
inStoreBranch: inStoreBranch ?? selectedBranch,
customerFeatures,
});
@@ -283,7 +288,6 @@ export class PurchaseOptionsStore extends ComponentStore<PurchaseOptionsState> {
try {
this.addFetchingAvailability({ id, itemId: itemData.sourceId, purchaseOption: 'delivery' });
const res = await this._service.fetchDeliveryAvailability(itemData, itemData.quantity).toPromise();
const availability: Availability = {
itemId: itemData.sourceId,
purchaseOption: 'delivery',
@@ -636,14 +640,32 @@ export class PurchaseOptionsStore extends ComponentStore<PurchaseOptionsState> {
return this.select(Selectors.getCanEditPrice(itemId));
}
setPrice(itemId: number, value: number) {
setPrice(itemId: number, value: number, manually: boolean = false) {
const prices = this.prices;
let price = prices[itemId];
if (price?.value?.value !== value) {
if (!price) {
price = { ...DEFAULT_PRICE_DTO, value: { ...DEFAULT_PRICE_VALUE, value } };
price = {
...DEFAULT_PRICE_DTO,
value: { ...DEFAULT_PRICE_VALUE, value },
vat: manually ? price?.vat : { ...DEFAULT_VAT_VALUE, ...price?.vat },
};
} else {
price = { ...price, value: { ...price.value, value } };
price = { ...price, value: { ...price.value, value }, vat: price?.vat };
}
this.patchState({ prices: { ...prices, [itemId]: price } });
}
}
setVat(itemId: number, vat: VATDTO) {
const prices = this.prices;
let price = prices[itemId];
if (price?.vat?.vatType !== vat?.vatType) {
if (!price) {
price = { ...DEFAULT_PRICE_DTO, value: { ...DEFAULT_PRICE_VALUE, ...price?.value }, vat: { ...DEFAULT_VAT_VALUE, ...vat } };
} else {
price = { ...price, value: price.value, vat: { ...price.vat, ...vat } };
}
this.patchState({ prices: { ...prices, [itemId]: price } });

View File

@@ -17,7 +17,7 @@ export type Branch = BranchDTO;
export type Availability = {
itemId: number;
purchaseOption: PurchaseOption;
data: AvailabilityDTO;
data: AvailabilityDTO & { priceMaintained?: boolean; orderDeadline?: string };
};
export type ItemData = AvailabilityItemData & { sourceId: number; quantity: number };

View File

@@ -0,0 +1,8 @@
import { NgModule } from '@angular/core';
import { OrderDeadlinePipe } from './order-deadline.pipe';
@NgModule({
imports: [OrderDeadlinePipe],
exports: [OrderDeadlinePipe],
})
export class OrderDeadlinePipeModule {}

View File

@@ -0,0 +1,33 @@
import { DatePipe } from '@angular/common';
import { Inject, LOCALE_ID, Pipe, PipeTransform } from '@angular/core';
@Pipe({
name: 'orderDeadline',
pure: true,
standalone: true,
})
export class OrderDeadlinePipe implements PipeTransform {
private datePipe: DatePipe;
constructor(@Inject(LOCALE_ID) private locale: string) {
this.datePipe = new DatePipe(this.locale);
}
transform(orderDeadline: string | Date | number): any {
if (!orderDeadline) return 'Unbekannte Bestelldeadline';
const date = new Date(orderDeadline);
const isToday = date.toDateString() === new Date().toDateString();
let timeString = '';
if (isToday) {
timeString = this.datePipe.transform(date, 'HH:mm', this.locale);
} else {
timeString = this.datePipe.transform(date, 'dd.MM HH:mm', this.locale);
}
return `Bestelldeadline ${timeString} Uhr`;
}
}

View File

@@ -0,0 +1,2 @@
export * from './lib/order-deadline.pipe';
export * from './lib/order-deadline-pipe.module';

View File

@@ -14,9 +14,11 @@ export interface AvailabilityDTO {
itemId?: number;
logistician?: string;
logisticianId?: number;
orderDeadline?: string;
orderReference?: string;
preferred?: number;
price?: PriceDTO;
priceMaintained?: boolean;
qty?: number;
requestMessage?: string;
requestReference?: string;

View File

@@ -14,9 +14,11 @@ export interface AvailabilityDTO2 {
itemId?: number;
logistician?: string;
logisticianId?: number;
orderDeadline?: string;
orderReference?: string;
preferred?: number;
price?: PriceDTO;
priceMaintained?: boolean;
qty?: number;
requestMessage?: string;
requestReference?: string;

View File

@@ -21,6 +21,8 @@ export interface OrderItemSubsetTaskListItemDTO {
orderItemSubsetTransition?: EntityDTOContainerOfOrderItemSubsetTransitionDTO;
orderNumber?: string;
pId?: string;
processingComment?: string;
processingReference?: string;
product?: ProductDTO;
type?: string;
}

View File

@@ -1,2 +1,2 @@
/* tslint:disable */
export type OrderItemType = 0 | 1 | 2 | 4 | 8 | 16 | 32 | 64;
export type OrderItemType = 0 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 256 | 512 | 1024;

View File

@@ -1,2 +1,2 @@
/* tslint:disable */
export type ReceiptType = 0 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128 | 256 | 512;
export type ReceiptType = 0 | 1 | 2 | 4 | 8 | 16 | 32 | 64 | 128 | 256 | 512 | 1024;

View File

@@ -63,6 +63,7 @@ class OrderService extends __BaseService {
static readonly OrderChangeStatusPath = '/order/{orderId}/orderitem/{orderItemId}/orderitemsubset/{orderItemSubsetId}/changestatus';
static readonly OrderChangeStockStatusCodePath = '/order/orderitem/orderitemsubset/changestockstatuscode';
static readonly OrderCollectOnDeliveryNotePath = '/order/orderitem/orderitemsubset/collectondeliverynote';
static readonly OrderCollectWithSmallAmountInvoicePath = '/order/orderitem/orderitemsubset/collectwithsmallamountinvoice';
static readonly OrderSetPreferredPickUpDatePath = '/order/orderitem/orderitemsubset/setpreferredpickupdate';
static readonly OrderQueryOrderItemSubsetTasksPath = '/order/item/subset/task/s';
static readonly OrderGetOrderItemSubsetTasksPath = '/order/{orderId}/item/{orderItemId}/subset/{orderItemSubsetId}/task';
@@ -1056,6 +1057,49 @@ class OrderService extends __BaseService {
);
}
/**
* @param params The `OrderService.OrderCollectWithSmallAmountInvoiceParams` containing the following parameters:
*
* - `data`:
*
* - `eagerLoading`:
*/
OrderCollectWithSmallAmountInvoiceResponse(params: OrderService.OrderCollectWithSmallAmountInvoiceParams): __Observable<__StrictHttpResponse<ResponseArgsOfIEnumerableOfEntityDTOContainerOfReceiptDTO>> {
let __params = this.newParams();
let __headers = new HttpHeaders();
let __body: any = null;
__body = params.data;
if (params.eagerLoading != null) __params = __params.set('eagerLoading', params.eagerLoading.toString());
let req = new HttpRequest<any>(
'POST',
this.rootUrl + `/order/orderitem/orderitemsubset/collectwithsmallamountinvoice`,
__body,
{
headers: __headers,
params: __params,
responseType: 'json'
});
return this.http.request<any>(req).pipe(
__filter(_r => _r instanceof HttpResponse),
__map((_r) => {
return _r as __StrictHttpResponse<ResponseArgsOfIEnumerableOfEntityDTOContainerOfReceiptDTO>;
})
);
}
/**
* @param params The `OrderService.OrderCollectWithSmallAmountInvoiceParams` containing the following parameters:
*
* - `data`:
*
* - `eagerLoading`:
*/
OrderCollectWithSmallAmountInvoice(params: OrderService.OrderCollectWithSmallAmountInvoiceParams): __Observable<ResponseArgsOfIEnumerableOfEntityDTOContainerOfReceiptDTO> {
return this.OrderCollectWithSmallAmountInvoiceResponse(params).pipe(
__map(_r => _r.body as ResponseArgsOfIEnumerableOfEntityDTOContainerOfReceiptDTO)
);
}
/**
* @param params The `OrderService.OrderSetPreferredPickUpDateParams` containing the following parameters:
*
@@ -1512,6 +1556,14 @@ module OrderService {
eagerLoading?: number;
}
/**
* Parameters for OrderCollectWithSmallAmountInvoice
*/
export interface OrderCollectWithSmallAmountInvoiceParams {
data: {[key: string]: number};
eagerLoading?: number;
}
/**
* Parameters for OrderSetPreferredPickUpDate
*/

View File

@@ -20,6 +20,8 @@ class ReceiptService extends __BaseService {
static readonly ReceiptQueryReceiptsPath = '/receipt/s';
static readonly ReceiptCreateShippingNotePath = '/receipt/shippingnote/fromorder';
static readonly ReceiptCreateShippingNote2Path = '/receipt/shippingnote/fromitems';
static readonly ReceiptCreateInvoicePath = '/receipt/invoice/fromorder';
static readonly ReceiptCreateInvoice2Path = '/receipt/invoice/fromitems';
static readonly ReceiptGetReceiptsByOrderItemSubsetPath = '/order/orderitem/orderitemsubset/receipts';
constructor(
@@ -168,6 +170,102 @@ class ReceiptService extends __BaseService {
);
}
/**
* @param params The `ReceiptService.ReceiptCreateInvoiceParams` containing the following parameters:
*
* - `orderId`:
*
* - `locale`:
*
* - `eagerLoading`:
*/
ReceiptCreateInvoiceResponse(params: ReceiptService.ReceiptCreateInvoiceParams): __Observable<__StrictHttpResponse<ResponseArgsOfReceiptDTO>> {
let __params = this.newParams();
let __headers = new HttpHeaders();
let __body: any = null;
__body = params.orderId;
if (params.locale != null) __params = __params.set('locale', params.locale.toString());
if (params.eagerLoading != null) __params = __params.set('eagerLoading', params.eagerLoading.toString());
let req = new HttpRequest<any>(
'POST',
this.rootUrl + `/receipt/invoice/fromorder`,
__body,
{
headers: __headers,
params: __params,
responseType: 'json'
});
return this.http.request<any>(req).pipe(
__filter(_r => _r instanceof HttpResponse),
__map((_r) => {
return _r as __StrictHttpResponse<ResponseArgsOfReceiptDTO>;
})
);
}
/**
* @param params The `ReceiptService.ReceiptCreateInvoiceParams` containing the following parameters:
*
* - `orderId`:
*
* - `locale`:
*
* - `eagerLoading`:
*/
ReceiptCreateInvoice(params: ReceiptService.ReceiptCreateInvoiceParams): __Observable<ResponseArgsOfReceiptDTO> {
return this.ReceiptCreateInvoiceResponse(params).pipe(
__map(_r => _r.body as ResponseArgsOfReceiptDTO)
);
}
/**
* @param params The `ReceiptService.ReceiptCreateInvoice2Params` containing the following parameters:
*
* - `orderItemStatusIds`:
*
* - `locale`:
*
* - `eagerLoading`:
*/
ReceiptCreateInvoice2Response(params: ReceiptService.ReceiptCreateInvoice2Params): __Observable<__StrictHttpResponse<ResponseArgsOfIEnumerableOfReceiptDTO>> {
let __params = this.newParams();
let __headers = new HttpHeaders();
let __body: any = null;
__body = params.orderItemStatusIds;
if (params.locale != null) __params = __params.set('locale', params.locale.toString());
if (params.eagerLoading != null) __params = __params.set('eagerLoading', params.eagerLoading.toString());
let req = new HttpRequest<any>(
'POST',
this.rootUrl + `/receipt/invoice/fromitems`,
__body,
{
headers: __headers,
params: __params,
responseType: 'json'
});
return this.http.request<any>(req).pipe(
__filter(_r => _r instanceof HttpResponse),
__map((_r) => {
return _r as __StrictHttpResponse<ResponseArgsOfIEnumerableOfReceiptDTO>;
})
);
}
/**
* @param params The `ReceiptService.ReceiptCreateInvoice2Params` containing the following parameters:
*
* - `orderItemStatusIds`:
*
* - `locale`:
*
* - `eagerLoading`:
*/
ReceiptCreateInvoice2(params: ReceiptService.ReceiptCreateInvoice2Params): __Observable<ResponseArgsOfIEnumerableOfReceiptDTO> {
return this.ReceiptCreateInvoice2Response(params).pipe(
__map(_r => _r.body as ResponseArgsOfIEnumerableOfReceiptDTO)
);
}
/**
* @param params The `ReceiptService.ReceiptGetReceiptsByOrderItemSubsetParams` containing the following parameters:
*
@@ -240,6 +338,24 @@ module ReceiptService {
eagerLoading?: number;
}
/**
* Parameters for ReceiptCreateInvoice
*/
export interface ReceiptCreateInvoiceParams {
orderId: number;
locale?: null | string;
eagerLoading?: number;
}
/**
* Parameters for ReceiptCreateInvoice2
*/
export interface ReceiptCreateInvoice2Params {
orderItemStatusIds: Array<number>;
locale?: null | string;
eagerLoading?: number;
}
/**
* Parameters for ReceiptGetReceiptsByOrderItemSubset
*/

View File

@@ -186,5 +186,6 @@ export { TermsOfDeliveryDTO2 } from './models/terms-of-delivery-dto2';
export { ReadOnlyEntityDTOOfDisplayOrderDTOAndIOrder } from './models/read-only-entity-dtoof-display-order-dtoand-iorder';
export { PrintRequestOfIEnumerableOfPriceQRCodeDTO } from './models/print-request-of-ienumerable-of-price-qrcode-dto';
export { PriceQRCodeDTO } from './models/price-qrcode-dto';
export { ResponseArgsOfIEnumerableOfString } from './models/response-args-of-ienumerable-of-string';
export { ListResponseArgsOfKeyValueDTOOfStringAndString } from './models/list-response-args-of-key-value-dtoof-string-and-string';
export { ResponseArgsOfIEnumerableOfKeyValueDTOOfStringAndString } from './models/response-args-of-ienumerable-of-key-value-dtoof-string-and-string';

View File

@@ -1,38 +1,10 @@
/* tslint:disable */
import { PriceValueDTO } from './price-value-dto';
/**
* PriceQRCodeDTO
*/
export interface PriceQRCodeDTO {
/**
* Abholfachnummer
*/
compartmentCode?: string;
/**
* Abholfachnummer Zusatz
*/
compartmentInfo?: string;
/**
* Anzahl Ausdrucke
*/
copies?: number;
/**
* EAN
*/
ean?: string;
/**
* Preis
*/
price?: PriceValueDTO;
/**
* Titel / Bezeichner
*/
title?: string;
}

View File

@@ -0,0 +1,5 @@
/* tslint:disable */
import { ResponseArgs } from './response-args';
export interface ResponseArgsOfIEnumerableOfString extends ResponseArgs{
result?: Array<string>;
}

View File

@@ -18,8 +18,11 @@ class OMSPrintService extends __BaseService {
static readonly OMSPrintAbholscheinByIdPath = '/print/abholschein';
static readonly OMSPrintAbholscheinPath = '/print/abholschein/data';
static readonly OMSPrintAbholfachetikettPath = '/print/abholfachetikett';
static readonly OMSPrintAbholfachetikettDirectPath = '/print-direct/abholfachetikett';
static readonly OMSPrintPriceQRCodePath = '/print/priceqrcode';
static readonly OMSPrintPriceQRCodeDirectPath = '/print-direct/priceqrcode';
static readonly OMSPrintLieferscheinPath = '/print/lieferschein';
static readonly OMSPrintKleinbetragsrechnungPath = '/print/kleinbetragsrechnung';
constructor(
config: __Configuration,
@@ -136,6 +139,42 @@ class OMSPrintService extends __BaseService {
);
}
/**
* Abholfachetikett (direkte Druckeransteuerung)
* @param data Bestellpostenteilmenge PKs
*/
OMSPrintAbholfachetikettDirectResponse(data: PrintRequestOfIEnumerableOfLong): __Observable<__StrictHttpResponse<ResponseArgs>> {
let __params = this.newParams();
let __headers = new HttpHeaders();
let __body: any = null;
__body = data;
let req = new HttpRequest<any>(
'POST',
this.rootUrl + `/print-direct/abholfachetikett`,
__body,
{
headers: __headers,
params: __params,
responseType: 'json'
});
return this.http.request<any>(req).pipe(
__filter(_r => _r instanceof HttpResponse),
__map((_r) => {
return _r as __StrictHttpResponse<ResponseArgs>;
})
);
}
/**
* Abholfachetikett (direkte Druckeransteuerung)
* @param data Bestellpostenteilmenge PKs
*/
OMSPrintAbholfachetikettDirect(data: PrintRequestOfIEnumerableOfLong): __Observable<ResponseArgs> {
return this.OMSPrintAbholfachetikettDirectResponse(data).pipe(
__map(_r => _r.body as ResponseArgs)
);
}
/**
* Abholfachpreis-Etikett
* @param data Bestellpostenteilmenge PKs
@@ -172,6 +211,42 @@ class OMSPrintService extends __BaseService {
);
}
/**
* Abholfachpreis-Etikett (direkte Druckeransteuerung)
* @param data Bestellpostenteilmenge PKs
*/
OMSPrintPriceQRCodeDirectResponse(data: PrintRequestOfIEnumerableOfPriceQRCodeDTO): __Observable<__StrictHttpResponse<ResponseArgs>> {
let __params = this.newParams();
let __headers = new HttpHeaders();
let __body: any = null;
__body = data;
let req = new HttpRequest<any>(
'POST',
this.rootUrl + `/print-direct/priceqrcode`,
__body,
{
headers: __headers,
params: __params,
responseType: 'json'
});
return this.http.request<any>(req).pipe(
__filter(_r => _r instanceof HttpResponse),
__map((_r) => {
return _r as __StrictHttpResponse<ResponseArgs>;
})
);
}
/**
* Abholfachpreis-Etikett (direkte Druckeransteuerung)
* @param data Bestellpostenteilmenge PKs
*/
OMSPrintPriceQRCodeDirect(data: PrintRequestOfIEnumerableOfPriceQRCodeDTO): __Observable<ResponseArgs> {
return this.OMSPrintPriceQRCodeDirectResponse(data).pipe(
__map(_r => _r.body as ResponseArgs)
);
}
/**
* Lieferschein
* @param data Lieferschein PKs
@@ -207,6 +282,42 @@ class OMSPrintService extends __BaseService {
__map(_r => _r.body as ResponseArgs)
);
}
/**
* Kleinbetragsrechnung
* @param data Kleinbetragsrechnung PKs
*/
OMSPrintKleinbetragsrechnungResponse(data: PrintRequestOfIEnumerableOfLong): __Observable<__StrictHttpResponse<ResponseArgs>> {
let __params = this.newParams();
let __headers = new HttpHeaders();
let __body: any = null;
__body = data;
let req = new HttpRequest<any>(
'POST',
this.rootUrl + `/print/kleinbetragsrechnung`,
__body,
{
headers: __headers,
params: __params,
responseType: 'json'
});
return this.http.request<any>(req).pipe(
__filter(_r => _r instanceof HttpResponse),
__map((_r) => {
return _r as __StrictHttpResponse<ResponseArgs>;
})
);
}
/**
* Kleinbetragsrechnung
* @param data Kleinbetragsrechnung PKs
*/
OMSPrintKleinbetragsrechnung(data: PrintRequestOfIEnumerableOfLong): __Observable<ResponseArgs> {
return this.OMSPrintKleinbetragsrechnungResponse(data).pipe(
__map(_r => _r.body as ResponseArgs)
);
}
}
module OMSPrintService {

View File

@@ -7,6 +7,7 @@ import { StrictHttpResponse as __StrictHttpResponse } from '../strict-http-respo
import { Observable as __Observable } from 'rxjs';
import { map as __map, filter as __filter } from 'rxjs/operators';
import { ResponseArgsOfIEnumerableOfString } from '../models/response-args-of-ienumerable-of-string';
import { ListResponseArgsOfKeyValueDTOOfStringAndString } from '../models/list-response-args-of-key-value-dtoof-string-and-string';
import { ResponseArgs } from '../models/response-args';
import { PrintRequest } from '../models/print-request';
@@ -15,11 +16,14 @@ import { PrintRequestOfString } from '../models/print-request-of-string';
providedIn: 'root',
})
class PrintService extends __BaseService {
static readonly PrintInitializeLabelPrintersPath = '/printers/label/init';
static readonly PrintInitializeOfficePrintersPath = '/printers/office/init';
static readonly PrintPrintersPath = '/printers';
static readonly PrintLabelPrintersPath = '/printers/label';
static readonly PrintOfficePrintersPath = '/printers/office';
static readonly PrintTestPath = '/print/test';
static readonly PrintPrinterDetailsPath = '/printers/details';
static readonly PrintDirectPrintTestPath = '/printers/directprinttest';
static readonly PrintPrintPdfPath = '/print/pdf';
static readonly PrintMonitorPath = '/print/monitor';
static readonly PrintStatusPath = '/print/status';
@@ -31,6 +35,72 @@ class PrintService extends __BaseService {
super(config, http);
}
/**
* Initialize label printers
*/
PrintInitializeLabelPrintersResponse(): __Observable<__StrictHttpResponse<ResponseArgsOfIEnumerableOfString>> {
let __params = this.newParams();
let __headers = new HttpHeaders();
let __body: any = null;
let req = new HttpRequest<any>(
'POST',
this.rootUrl + `/printers/label/init`,
__body,
{
headers: __headers,
params: __params,
responseType: 'json'
});
return this.http.request<any>(req).pipe(
__filter(_r => _r instanceof HttpResponse),
__map((_r) => {
return _r as __StrictHttpResponse<ResponseArgsOfIEnumerableOfString>;
})
);
}
/**
* Initialize label printers
*/
PrintInitializeLabelPrinters(): __Observable<ResponseArgsOfIEnumerableOfString> {
return this.PrintInitializeLabelPrintersResponse().pipe(
__map(_r => _r.body as ResponseArgsOfIEnumerableOfString)
);
}
/**
* Initialize office printers
*/
PrintInitializeOfficePrintersResponse(): __Observable<__StrictHttpResponse<ResponseArgsOfIEnumerableOfString>> {
let __params = this.newParams();
let __headers = new HttpHeaders();
let __body: any = null;
let req = new HttpRequest<any>(
'POST',
this.rootUrl + `/printers/office/init`,
__body,
{
headers: __headers,
params: __params,
responseType: 'json'
});
return this.http.request<any>(req).pipe(
__filter(_r => _r instanceof HttpResponse),
__map((_r) => {
return _r as __StrictHttpResponse<ResponseArgsOfIEnumerableOfString>;
})
);
}
/**
* Initialize office printers
*/
PrintInitializeOfficePrinters(): __Observable<ResponseArgsOfIEnumerableOfString> {
return this.PrintInitializeOfficePrintersResponse().pipe(
__map(_r => _r.body as ResponseArgsOfIEnumerableOfString)
);
}
/**
* Verfügbare Drucker
*/
@@ -202,6 +272,40 @@ class PrintService extends __BaseService {
);
}
/**
* @param data undefined
*/
PrintDirectPrintTestResponse(data: PrintRequest): __Observable<__StrictHttpResponse<ResponseArgs>> {
let __params = this.newParams();
let __headers = new HttpHeaders();
let __body: any = null;
__body = data;
let req = new HttpRequest<any>(
'POST',
this.rootUrl + `/printers/directprinttest`,
__body,
{
headers: __headers,
params: __params,
responseType: 'json'
});
return this.http.request<any>(req).pipe(
__filter(_r => _r instanceof HttpResponse),
__map((_r) => {
return _r as __StrictHttpResponse<ResponseArgs>;
})
);
}
/**
* @param data undefined
*/
PrintDirectPrintTest(data: PrintRequest): __Observable<ResponseArgs> {
return this.PrintDirectPrintTestResponse(data).pipe(
__map(_r => _r.body as ResponseArgs)
);
}
/**
* Druckt das übergebene Pdf-Dokument (base64)
* @param data Artikel

View File

@@ -8,6 +8,7 @@ export { EntityDTOBaseOfUserDTOAndIUser } from './models/entity-dtobase-of-user-
export { EntityDTOBase } from './models/entity-dtobase';
export { EntityDTO } from './models/entity-dto';
export { EntityStatus } from './models/entity-status';
export { CRUDA } from './models/cruda';
export { TouchedBase } from './models/touched-base';
export { EntityDTOReferenceContainer } from './models/entity-dtoreference-container';
export { ExternalReferenceDTO } from './models/external-reference-dto';

View File

@@ -0,0 +1,2 @@
/* tslint:disable */
export type CRUDA = 0 | 1 | 2 | 4 | 8 | 16;

View File

@@ -1,9 +1,11 @@
/* tslint:disable */
import { TouchedBase } from './touched-base';
import { CRUDA } from './cruda';
import { EntityStatus } from './entity-status';
export interface EntityDTO extends TouchedBase{
changed?: string;
created?: string;
cruda?: CRUDA;
id?: number;
pId?: string;
status?: EntityStatus;

View File

@@ -11,6 +11,7 @@ export interface PackageArrivalStatusDTO {
id: string;
misrouted?: string;
packageNumber?: string;
scanId?: string;
supplier?: string;
trackingNumber?: string;
}

View File

@@ -2,8 +2,11 @@
import { PackageArrivalStatusDTO } from './package-arrival-status-dto';
export interface PackageDTO2 extends PackageArrivalStatusDTO{
app?: string;
complainedEmail?: string;
creditRequestedEmail?: string;
items?: number;
itemsOrdered?: number;
missing?: string;
valueOfGoods?: number;
weight?: number;
}

View File

@@ -122,8 +122,7 @@ export class UiOverlayTriggerDirective implements OnInit, OnDestroy, OnChanges {
createOverlay() {
this.overlayRef = this.overlay.create({
positionStrategy: this.getPositionStrategy(),
hasBackdrop: true,
backdropClass: 'cdk-overlay-transparent-backdrop',
hasBackdrop: false,
});
this.overlayRef
.backdropClick()
@@ -170,4 +169,11 @@ export class UiOverlayTriggerDirective implements OnInit, OnDestroy, OnChanges {
updatePosition() {
this.overlayRef?.updatePositionStrategy(this.getPositionStrategy());
}
@HostListener('document:click', ['$event'])
documentClick(event: MouseEvent) {
if (this.viewRef && !this.overlayRef?.hostElement?.contains(event.target as HTMLElement)) {
this.close();
}
}
}

View File

@@ -22,6 +22,12 @@ export class UiFilterInputTextComponent extends AbstractUiFilterInputDirective i
this.control.setValue(this.value);
this.updateValidator();
const uiInputChangesSub = this.uiInput?.changes?.subscribe((changes) => {
if (this.control.value !== changes?.target?.value) this.control.setValue(changes?.target?.value);
this.updateValidator();
});
const onInputChangeSub = this.onUiInputChange$.subscribe((input) => {
if (this.control.value !== input.value) this.control.setValue(input.value);
@@ -32,6 +38,7 @@ export class UiFilterInputTextComponent extends AbstractUiFilterInputDirective i
if (this.value !== value) this.setValue(value);
});
this._subscriptions.add(uiInputChangesSub);
this._subscriptions.add(onInputChangeSub);
this._subscriptions.add(onControlValueChangeSub);
}

View File

@@ -191,6 +191,21 @@ export class UiFilter implements IUiFilter {
};
}
unselectAll() {
this.filter?.forEach((uiInputGroup) =>
uiInputGroup?.input?.forEach((uiInput) => {
uiInput?.setSelected(undefined);
uiInput?.setValue(undefined);
})
);
this.input?.forEach((uiInputGroup) =>
uiInputGroup?.input?.forEach((uiInput) => {
uiInput?.setSelected(undefined);
uiInput?.setValue(undefined);
})
);
}
static create(settings: IUiFilter) {
const target = new UiFilter();

View File

@@ -20,27 +20,7 @@ variables:
- group: 'GithubCMF'
jobs:
# - job: cibuild_fast
# displayName: ISAClient CI Fast
# pool:
# name: 'Default'
# demands:
# - Agent.OS -equals Linux
# - docker
# condition: and(ne(variables['Build.SourceBranch'], 'refs/heads/develop'), ne(variables['Build.SourceBranch'], 'refs/heads/integration'), ne(variables['Build.SourceBranch'], 'refs/heads/master'), not(startsWith(variables['Build.SourceBranch'], 'refs/heads/hotfix/')), not(startsWith(variables['Build.SourceBranch'], 'refs/heads/release/')))
# steps:
# - task: npmAuthenticate@0
# displayName: 'npm auth'
# inputs:
# workingFile: .npmrc
# - task: Docker@2
# displayName: 'build ISAClient Debug'
# inputs:
# command: 'build'
# Dockerfile: Dockerfile
# buildContext:
# tags: '$(Build.BuildNumber)-$(Build.SourceVersion)'
# arguments: '--build-arg SEMVERSION=$(Major).$(Minor).$(Patch)'
- job: unittests
displayName: Unit Tests
pool:
@@ -99,6 +79,7 @@ jobs:
fi
displayName: Remove docker image
condition: always()
- job: cibuild_debug
displayName: ISAClient CI Debug
pool:
@@ -107,7 +88,15 @@ jobs:
- Agent.OS -equals Linux
- docker
condition: and(ne(variables['Build.SourceBranch'], 'refs/heads/integration'), ne(variables['Build.SourceBranch'], 'refs/heads/master'), not(startsWith(variables['Build.SourceBranch'], 'refs/heads/hotfix/')), not(startsWith(variables['Build.SourceBranch'], 'refs/heads/release/')))
variables:
- name: DockerTagSourceBranch
value: $[replace(variables['Build.SourceBranch'], '/', '_')]
- name: 'DockerTag'
value: |
$(Build.BuildNumber)-$(Build.SourceVersion)
$(DockerTagSourceBranch)
steps:
- task: npmAuthenticate@0
displayName: 'npm auth'
inputs:
@@ -121,7 +110,7 @@ jobs:
command: 'build'
Dockerfile: Dockerfile
buildContext:
tags: '$(Build.BuildNumber)-$(Build.SourceVersion)'
tags: '$(DockerTag)'
arguments: |
--no-cache
--target publish
@@ -135,7 +124,7 @@ jobs:
containerRegistry: 'Harbor isa'
repository: 'isa/ui'
command: 'push'
tags: '$(Build.BuildNumber)-$(Build.SourceVersion)'
tags: '$(DockerTag)'
- task: PublishBuildArtifacts@1
displayName: 'Artefakt veröffentlichen: HelmValues'
@@ -169,6 +158,13 @@ jobs:
- Agent.OS -equals Linux
- docker
condition: or(eq(variables['Build.SourceBranch'], 'refs/heads/integration'), eq(variables['Build.SourceBranch'], 'refs/heads/master'), startsWith(variables['Build.SourceBranch'], 'refs/heads/hotfix/'), startsWith(variables['Build.SourceBranch'], 'refs/heads/release/'))
variables:
- name: DockerTagSourceBranch
value: $[replace(variables['Build.SourceBranch'], '/', '_')]
- name: 'DockerTag'
value: |
$(Build.BuildNumber)-$(Build.SourceVersion)
$(DockerTagSourceBranch)
steps:
- task: npmAuthenticate@0
displayName: 'npm auth'
@@ -183,7 +179,7 @@ jobs:
command: 'build'
Dockerfile: Dockerfile
buildContext:
tags: '$(Build.BuildNumber)-$(Build.SourceVersion)'
tags: '$(DockerTag)'
arguments: |
--no-cache
--target publish
@@ -198,7 +194,7 @@ jobs:
containerRegistry: 'Harbor isa'
repository: 'isa/ui'
command: 'push'
tags: '$(Build.BuildNumber)-$(Build.SourceVersion)'
tags: '$(DockerTag)'
- task: PublishBuildArtifacts@1