| @@ -12,6 +12,7 @@ import {DocumentsComponent} from "@app/documents/documents.component"; | |||||
| import {ContactsComponent} from "@app/contacts/contacts.component"; | import {ContactsComponent} from "@app/contacts/contacts.component"; | ||||
| import {ContactsDetailComponent} from "@app/contacts/contacts-detail/contacts-detail.component"; | import {ContactsDetailComponent} from "@app/contacts/contacts-detail/contacts-detail.component"; | ||||
| import {TasksComponent} from "@app/tasks/tasks.component"; | import {TasksComponent} from "@app/tasks/tasks.component"; | ||||
| import {SalesComponent} from "@app/sales/sales.component"; | |||||
| const accountModule = () => import('./account/account.module').then(x => x.AccountModule); | const accountModule = () => import('./account/account.module').then(x => x.AccountModule); | ||||
| const usersModule = () => import('./users/users.module').then(x => x.UsersModule); | const usersModule = () => import('./users/users.module').then(x => x.UsersModule); | ||||
| @@ -81,6 +82,14 @@ const routes: Routes = [ | |||||
| {path: '', component: DocumentsComponent, data: {dataType: 'document'}}, | {path: '', component: DocumentsComponent, data: {dataType: 'document'}}, | ||||
| ] | ] | ||||
| }, | }, | ||||
| { | |||||
| path: 'sales', | |||||
| component: TwoColumnComponent, | |||||
| canActivate: [AuthGuard], | |||||
| children: [ | |||||
| {path: '', component: SalesComponent, data: {dataType: 'sales'}}, | |||||
| ] | |||||
| }, | |||||
| // otherwise redirect to home | // otherwise redirect to home | ||||
| {path: '**', redirectTo: ''} | {path: '**', redirectTo: ''} | ||||
| @@ -43,6 +43,8 @@ import {MatFormFieldModule} from "@angular/material/form-field"; | |||||
| import {MatInputModule} from "@angular/material/input"; | import {MatInputModule} from "@angular/material/input"; | ||||
| import { NewTaskNoteComponent } from './tasks/new-task-note/new-task-note.component'; | import { NewTaskNoteComponent } from './tasks/new-task-note/new-task-note.component'; | ||||
| import { DocumentsDetailComponent } from './documents/documents-detail/documents-detail.component'; | import { DocumentsDetailComponent } from './documents/documents-detail/documents-detail.component'; | ||||
| import { SalesComponent } from './sales/sales.component'; | |||||
| import { SalesDetailComponent } from './sales/sales-detail/sales-detail.component'; | |||||
| export function apiConfigFactory(): Configuration { | export function apiConfigFactory(): Configuration { | ||||
| const params: ConfigurationParameters = { | const params: ConfigurationParameters = { | ||||
| @@ -106,7 +108,9 @@ export function HttpLoaderFactory(http: HttpClient) { | |||||
| NewProductComponent, | NewProductComponent, | ||||
| NewCommentComponent, | NewCommentComponent, | ||||
| NewTaskNoteComponent, | NewTaskNoteComponent, | ||||
| DocumentsDetailComponent | |||||
| DocumentsDetailComponent, | |||||
| SalesComponent, | |||||
| SalesDetailComponent | |||||
| ], | ], | ||||
| providers: [ | providers: [ | ||||
| {provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true}, | {provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true}, | ||||
| @@ -43,6 +43,13 @@ | |||||
| </div> | </div> | ||||
| </a> | </a> | ||||
| </li> | </li> | ||||
| <li class="nav-item mb-3"> | |||||
| <a class="card" routerLink="/sales" routerLinkActive="active"> | |||||
| <div class="card-body position-relative bi bi-journals"> | |||||
| <h3 class="position-absolute m-0">{{'basic.sales' | translate}}</h3> | |||||
| </div> | |||||
| </a> | |||||
| </li> | |||||
| </ul> | </ul> | ||||
| </div> | </div> | ||||
| <div class="col-10"> | <div class="col-10"> | ||||
| @@ -1,6 +1,6 @@ | |||||
| <div class="spt-container"> | <div class="spt-container"> | ||||
| <div class="d-flex justify-content-between align-items-start"> | <div class="d-flex justify-content-between align-items-start"> | ||||
| <h2>{{ partnerName }}</h2> | |||||
| <h2>{{ headline }}</h2> | |||||
| <button class="btn btn-primary" | <button class="btn btn-primary" | ||||
| (click)="openModalNewPartner()">{{ 'basic.new' | translate }} {{ partnerNameOne }} | (click)="openModalNewPartner()">{{ 'basic.new' | translate }} {{ partnerNameOne }} | ||||
| </button> | </button> | ||||
| @@ -33,7 +33,7 @@ export class PartnersComponent implements OnInit, AfterViewInit { | |||||
| protected websiteOrderAsc: OrderFilter; | protected websiteOrderAsc: OrderFilter; | ||||
| protected dataType!: string; | protected dataType!: string; | ||||
| protected partnerName: string; | |||||
| protected headline: string; | |||||
| protected partnerNameOne: string; | protected partnerNameOne: string; | ||||
| protected displayedColumns: string[]; | protected displayedColumns: string[]; | ||||
| @@ -71,7 +71,7 @@ export class PartnersComponent implements OnInit, AfterViewInit { | |||||
| this.partnersPageSize = 10; | this.partnersPageSize = 10; | ||||
| this.partnersPageIndex = 0; | this.partnersPageIndex = 0; | ||||
| this.partnerName = ""; | |||||
| this.headline = ""; | |||||
| this.partnerNameOne = ""; | this.partnerNameOne = ""; | ||||
| } | } | ||||
| @@ -79,7 +79,7 @@ export class PartnersComponent implements OnInit, AfterViewInit { | |||||
| this.dataType = this.route.snapshot.data['dataType']; | this.dataType = this.route.snapshot.data['dataType']; | ||||
| // this.translateService.use(this.translateService.getDefaultLang()); | // this.translateService.use(this.translateService.getDefaultLang()); | ||||
| this.translateService.get('basic.' + this.dataType).subscribe((translation: string) => { | this.translateService.get('basic.' + this.dataType).subscribe((translation: string) => { | ||||
| this.partnerName = translation; | |||||
| this.headline = translation; | |||||
| }); | }); | ||||
| this.translateService.get('basic.' + this.dataType + 'One').subscribe((translation: string) => { | this.translateService.get('basic.' + this.dataType + 'One').subscribe((translation: string) => { | ||||
| this.partnerNameOne = translation; | this.partnerNameOne = translation; | ||||
| @@ -0,0 +1 @@ | |||||
| <p>sales-detail works!</p> | |||||
| @@ -0,0 +1,23 @@ | |||||
| import { ComponentFixture, TestBed } from '@angular/core/testing'; | |||||
| import { SalesDetailComponent } from './sales-detail.component'; | |||||
| describe('SalesDetailComponent', () => { | |||||
| let component: SalesDetailComponent; | |||||
| let fixture: ComponentFixture<SalesDetailComponent>; | |||||
| beforeEach(async () => { | |||||
| await TestBed.configureTestingModule({ | |||||
| declarations: [SalesDetailComponent] | |||||
| }) | |||||
| .compileComponents(); | |||||
| fixture = TestBed.createComponent(SalesDetailComponent); | |||||
| component = fixture.componentInstance; | |||||
| fixture.detectChanges(); | |||||
| }); | |||||
| it('should create', () => { | |||||
| expect(component).toBeTruthy(); | |||||
| }); | |||||
| }); | |||||
| @@ -0,0 +1,10 @@ | |||||
| import { Component } from '@angular/core'; | |||||
| @Component({ | |||||
| selector: 'app-sales-detail', | |||||
| templateUrl: './sales-detail.component.html', | |||||
| styleUrl: './sales-detail.component.scss' | |||||
| }) | |||||
| export class SalesDetailComponent { | |||||
| } | |||||
| @@ -0,0 +1,96 @@ | |||||
| <div *ngFor="let saleSummary of saleSummaries"> | |||||
| <div>{{ saleSummary.ownerName }} - {{ saleSummary.turnover }} - {{ saleSummary.profit }}</div> | |||||
| </div> | |||||
| <div class="spt-container"> | |||||
| <div class="d-flex justify-content-between align-items-start"> | |||||
| <h2>{{ 'basic.sales' | translate }}</h2> | |||||
| <button class="btn btn-primary" (click)="openModalNewSale()">{{ 'basic.new-sale' | translate }}</button> | |||||
| </div> | |||||
| <table mat-table [dataSource]="dataSource" matSort (matSortChange)="onSortChange($event)" | |||||
| class="mat-elevation-z8 mb-3"> | |||||
| <ng-container matColumnDef="pos"> | |||||
| <th mat-header-cell *matHeaderCellDef> | |||||
| {{ 'overview.number' | translate }} | |||||
| </th> | |||||
| <td mat-cell | |||||
| *matCellDef="let element">{{ (pageSize * pageIndex) + dataSource.filteredData.indexOf(element) + 1 }} | |||||
| </td> | |||||
| </ng-container> | |||||
| <ng-container matColumnDef="user"> | |||||
| <th mat-header-cell *matHeaderCellDef mat-sort-header | |||||
| sortActionDescription="{{ 'overview.sort' | translate }}: {{ 'overview.sale-user' | translate }}"> | |||||
| {{ 'overview.sale-user' | translate }} | |||||
| </th> | |||||
| <td mat-cell *matCellDef="let element"> | |||||
| <span role="button" (click)="navigateToSaleDetails(element)"> | |||||
| {{ element.ownerName }} | |||||
| </span> | |||||
| </td> | |||||
| </ng-container> | |||||
| <ng-container matColumnDef="partner"> | |||||
| <th mat-header-cell *matHeaderCellDef mat-sort-header="address" | |||||
| sortActionDescription="{{ 'overview.sort' | translate }}: {{ 'overview.sale-partner' | translate }}"> | |||||
| {{ 'overview.sale-partner' | translate }} | |||||
| </th> | |||||
| <td mat-cell *matCellDef="let element"> | |||||
| {{ element.partnerName }} | |||||
| </td> | |||||
| </ng-container> | |||||
| <ng-container matColumnDef="product"> | |||||
| <th mat-header-cell *matHeaderCellDef mat-sort-header | |||||
| sortActionDescription="{{ 'overview.sort' | translate }}: {{ 'overview.productname' | translate }}"> | |||||
| {{ 'overview.productname' | translate }} | |||||
| </th> | |||||
| <td mat-cell *matCellDef="let element"> | |||||
| {{ element.productName }} | |||||
| </td> | |||||
| </ng-container> | |||||
| <ng-container matColumnDef="turnover"> | |||||
| <th mat-header-cell *matHeaderCellDef mat-sort-header | |||||
| sortActionDescription="{{ 'overview.sort' | translate }}: {{ 'overview.turnover' | translate }}"> | |||||
| {{ 'overview.turnover' | translate }} | |||||
| </th> | |||||
| <td mat-cell *matCellDef="let element"> | |||||
| {{ element.turnover }} | |||||
| </td> | |||||
| </ng-container> | |||||
| <ng-container matColumnDef="profit"> | |||||
| <th mat-header-cell *matHeaderCellDef mat-sort-header | |||||
| sortActionDescription="{{ 'overview.sort' | translate }}: {{ 'overview.profit' | translate }}"> | |||||
| {{ 'overview.profit' | translate }} | |||||
| </th> | |||||
| <td mat-cell *matCellDef="let element"> | |||||
| {{ element.profit }} | |||||
| </td> | |||||
| </ng-container> | |||||
| <ng-container matColumnDef="date"> | |||||
| <th mat-header-cell *matHeaderCellDef mat-sort-header | |||||
| sortActionDescription="{{ 'overview.sort' | translate }}: {{ 'overview.createdAt' | translate }}"> | |||||
| {{ 'overview.createdAt' | translate }} | |||||
| </th> | |||||
| <td mat-cell *matCellDef="let element"> | |||||
| {{ element.createdAt }} | |||||
| </td> | |||||
| </ng-container> | |||||
| <tr mat-header-row *matHeaderRowDef="displayedColumns"></tr> | |||||
| <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr> | |||||
| </table> | |||||
| <mat-paginator class="rounded-1" | |||||
| [pageSizeOptions]="[10,25,50]" | |||||
| [length]="length" | |||||
| (page)="handlePageEvent($event)" | |||||
| [pageSize]="pageSize" | |||||
| [pageIndex]="pageIndex" | |||||
| showFirstLastButtons> | |||||
| </mat-paginator> | |||||
| </div> | |||||
| @@ -0,0 +1,23 @@ | |||||
| import { ComponentFixture, TestBed } from '@angular/core/testing'; | |||||
| import { SalesComponent } from './sales.component'; | |||||
| describe('SalesComponent', () => { | |||||
| let component: SalesComponent; | |||||
| let fixture: ComponentFixture<SalesComponent>; | |||||
| beforeEach(async () => { | |||||
| await TestBed.configureTestingModule({ | |||||
| declarations: [SalesComponent] | |||||
| }) | |||||
| .compileComponents(); | |||||
| fixture = TestBed.createComponent(SalesComponent); | |||||
| component = fixture.componentInstance; | |||||
| fixture.detectChanges(); | |||||
| }); | |||||
| it('should create', () => { | |||||
| expect(component).toBeTruthy(); | |||||
| }); | |||||
| }); | |||||
| @@ -0,0 +1,154 @@ | |||||
| import {ChangeDetectorRef, Component, OnInit, ViewChild} from '@angular/core'; | |||||
| import { | |||||
| PartnerJsonld, ProductJsonld, | |||||
| SaleJsonld, | |||||
| SaleService, | |||||
| SaleSummaryJsonld, | |||||
| SaleSummaryService | |||||
| } from "@app/core/api/v1"; | |||||
| import {Subscription} from "rxjs"; | |||||
| import {MatPaginator, MatPaginatorIntl, PageEvent} from "@angular/material/paginator"; | |||||
| import {MatSort, Sort} from "@angular/material/sort"; | |||||
| import {TranslateService} from "@ngx-translate/core"; | |||||
| import {NewPartnerComponent} from "@app/partners/new-partner/new-partner.component"; | |||||
| import {ModalStatus} from "@app/_helpers/modal.states"; | |||||
| import {NgbModal, NgbModalOptions} from "@ng-bootstrap/ng-bootstrap"; | |||||
| import {MatTableDataSource} from "@angular/material/table"; | |||||
| import {OrderFilter} from "@app/_models/orderFilter"; | |||||
| import {ApiConverter} from "@app/_helpers/api.converter"; | |||||
| import {Router} from "@angular/router"; | |||||
| @Component({ | |||||
| selector: 'app-sales', | |||||
| templateUrl: './sales.component.html', | |||||
| styleUrl: './sales.component.scss' | |||||
| }) | |||||
| export class SalesComponent implements OnInit { | |||||
| @ViewChild(MatSort) sort; | |||||
| @ViewChild(MatPaginator) paginator: MatPaginator; | |||||
| protected displayedColumns: string[]; | |||||
| protected salesSub: Subscription; | |||||
| protected sales: Array<SaleJsonld>; | |||||
| protected salesSummarySub: Subscription; | |||||
| protected saleSummaries: Array<SaleSummaryJsonld>; | |||||
| protected dataSource; | |||||
| protected length: number; | |||||
| protected pageEvent: PageEvent; | |||||
| protected pageSize: number; | |||||
| protected pageIndex: number; | |||||
| protected modalOptions: NgbModalOptions = { | |||||
| centered: true | |||||
| }; | |||||
| constructor( | |||||
| private saleService: SaleService, | |||||
| private saleSummaryService: SaleSummaryService, | |||||
| private translateService: TranslateService, | |||||
| private modalService: NgbModal, | |||||
| private router: Router, | |||||
| ) { | |||||
| this.sort = new MatSort(); | |||||
| this.displayedColumns = ['pos', 'user', 'partner', 'product', 'turnover', 'profit', 'date']; | |||||
| this.salesSub = new Subscription(); | |||||
| this.sales = []; | |||||
| this.salesSummarySub = new Subscription(); | |||||
| this.saleSummaries = []; | |||||
| this.dataSource = new MatTableDataSource<SaleJsonld>(this.sales); | |||||
| this.paginator = new MatPaginator(new MatPaginatorIntl(), ChangeDetectorRef.prototype); | |||||
| this.length = 0; | |||||
| this.pageEvent = new PageEvent(); | |||||
| this.pageSize = 10; | |||||
| this.pageIndex = 0; | |||||
| } | |||||
| ngOnInit() { | |||||
| this.getSalesData(); | |||||
| this.getSalesSummaryData(); | |||||
| } | |||||
| getSalesData() { | |||||
| this.salesSub = this.saleService.salesGetCollection( | |||||
| this.pageIndex + 1, | |||||
| this.pageSize, | |||||
| ).subscribe( | |||||
| data => { | |||||
| this.sales = data["hydra:member"]; | |||||
| this.dataSource = new MatTableDataSource<SaleJsonld>(this.sales); | |||||
| this.length = Number(data["hydra:totalItems"]); | |||||
| this.paginator.length = this.length; | |||||
| console.log(this.sales); | |||||
| } | |||||
| ) | |||||
| } | |||||
| getSalesSummaryData() { | |||||
| this.salesSummarySub = this.saleSummaryService.saleSummariesGetCollection( | |||||
| 1, | |||||
| 50 | |||||
| ).subscribe( | |||||
| data => { | |||||
| this.saleSummaries = data["hydra:member"]; | |||||
| console.log(this.saleSummaries); | |||||
| } | |||||
| ) | |||||
| } | |||||
| onSortChange(sortState: Sort) { | |||||
| // Reset page index to first page | |||||
| this.pageIndex = 0; | |||||
| let order: OrderFilter; | |||||
| if (sortState.direction === "") { | |||||
| order = OrderFilter.Undefined; | |||||
| } else { | |||||
| order = sortState.direction; | |||||
| } | |||||
| // this.nameOrderAsc = OrderFilter.Undefined; | |||||
| // this.cityOrderAsc = OrderFilter.Undefined; | |||||
| // this.websiteOrderAsc = OrderFilter.Undefined; | |||||
| // switch (sortState.active) { | |||||
| // case "name": | |||||
| // this.nameOrderAsc = order; | |||||
| // break; | |||||
| // case "address": | |||||
| // this.cityOrderAsc = order; | |||||
| // break; | |||||
| // case "website": | |||||
| // this.websiteOrderAsc = order; | |||||
| // break; | |||||
| // } | |||||
| this.getSalesData(); | |||||
| } | |||||
| handlePageEvent(e: PageEvent) { | |||||
| this.pageEvent = e; | |||||
| this.length = e.length; | |||||
| this.pageIndex = e.pageIndex.valueOf(); | |||||
| this.pageSize = e.pageSize.valueOf(); | |||||
| this.getSalesData(); | |||||
| } | |||||
| navigateToSaleDetails(element: any) { | |||||
| const product: ProductJsonld = element as ProductJsonld; | |||||
| this.router.navigate(['/products', ApiConverter.extractId(product.id)]); | |||||
| } | |||||
| openModalNewSale() { | |||||
| const modalRefContact = this.modalService.open(NewPartnerComponent, this.modalOptions); | |||||
| let sale: SaleJsonld = {} as SaleJsonld; | |||||
| modalRefContact.componentInstance.partner = sale; | |||||
| modalRefContact.componentInstance.submit.subscribe((modalStatus: ModalStatus) => { | |||||
| if (modalStatus === ModalStatus.Submitted) { | |||||
| modalRefContact.dismiss(); | |||||
| this.getSalesData(); | |||||
| } | |||||
| }); | |||||
| } | |||||
| } | |||||
| @@ -18,6 +18,7 @@ | |||||
| "serviceOne": "Lieferant", | "serviceOne": "Lieferant", | ||||
| "products": "Produkte", | "products": "Produkte", | ||||
| "documents": "Dokumente", | "documents": "Dokumente", | ||||
| "sales": "Verkäufe", | |||||
| "tasks": "Aufgaben", | "tasks": "Aufgaben", | ||||
| "contacts": "Kontakte", | "contacts": "Kontakte", | ||||
| "posts": "Notizen", | "posts": "Notizen", | ||||
| @@ -30,6 +31,7 @@ | |||||
| "new-post": "Neue Notiz", | "new-post": "Neue Notiz", | ||||
| "new-comment": "Neuer Kommentar", | "new-comment": "Neuer Kommentar", | ||||
| "new-task-note": "Neue Anmerkung", | "new-task-note": "Neue Anmerkung", | ||||
| "new-sale": "Neuer Verkauf", | |||||
| "edit-before": "", | "edit-before": "", | ||||
| "edit-after": "bearbeiten", | "edit-after": "bearbeiten", | ||||
| "edit-product": "Produkt bearbeiten", | "edit-product": "Produkt bearbeiten", | ||||
| @@ -58,7 +60,7 @@ | |||||
| "address": "Adresse", | "address": "Adresse", | ||||
| "website": "Website", | "website": "Website", | ||||
| "image": "Bild", | "image": "Bild", | ||||
| "productname": "Produktname", | |||||
| "productname": "Produkt", | |||||
| "storage": "Lagerbestand", | "storage": "Lagerbestand", | ||||
| "number-long": "Nummer", | "number-long": "Nummer", | ||||
| "document": "Dokument", | "document": "Dokument", | ||||
| @@ -68,7 +70,12 @@ | |||||
| "createdBy": "Erstellt von", | "createdBy": "Erstellt von", | ||||
| "download": "Download", | "download": "Download", | ||||
| "partner": "Partner", | "partner": "Partner", | ||||
| "product": "Produkt" | |||||
| "product": "Produkt", | |||||
| "sale-user": "Verkäufer", | |||||
| "sale-partner": "Kunde", | |||||
| "turnover": "Umsatz", | |||||
| "profit": "Gewinn", | |||||
| "createdAt": "erstellt am" | |||||
| }, | }, | ||||
| "form": | "form": | ||||
| { | { | ||||