diff --git a/.dockerignore b/.dockerignore
new file mode 100644
index 0000000..bfbc341
--- /dev/null
+++ b/.dockerignore
@@ -0,0 +1,20 @@
+node_modules
+npm-debug.log
+dist
+.git
+.gitignore
+.angular
+.env
+.env.local
+.env.*.local
+README.md
+LICENSE.md
+.vscode
+.idea
+*.swp
+*.swo
+*~
+.DS_Store
+karma.conf.js
+tsconfig.spec.json
+src/**/*.spec.ts
diff --git a/DOCKER.md b/DOCKER.md
new file mode 100644
index 0000000..3a8d742
--- /dev/null
+++ b/DOCKER.md
@@ -0,0 +1,339 @@
+# Docker Setup Guide
+
+This project is now containerized with Docker. Below are instructions for building and running the application in Docker.
+
+## Quick Start
+
+### Production
+
+```bash
+docker-compose up -d app
+# Visit http://localhost
+```
+
+### Staging
+
+```bash
+docker-compose --profile staging up -d staging
+# Visit http://localhost:8080
+```
+
+### Development
+
+```bash
+docker-compose --profile dev up dev
+# Visit http://localhost:4200
+```
+
+## Prerequisites
+
+- Docker installed (version 20.10+)
+- Docker Compose installed (version 2.0+)
+
+## Production Build
+
+### Build the Docker Image
+
+```bash
+docker build -t sakai-ng:latest .
+```
+
+### Run the Container
+
+```bash
+docker run -d -p 80:80 --name sakai-ng sakai-ng:latest
+```
+
+The application will be available at `http://localhost`
+
+### Run with Docker Compose
+
+```bash
+docker-compose up -d app
+```
+
+## Staging Build
+
+### Build for Staging
+
+```bash
+docker build -f Dockerfile.staging -t sakai-ng:staging .
+```
+
+### Run Staging Container
+
+```bash
+docker run -d -p 8080:80 --name sakai-ng-staging sakai-ng:staging
+```
+
+### Run with Docker Compose
+
+```bash
+docker-compose --profile staging up -d staging
+```
+
+The application will be available at `http://localhost:8080`
+
+## Development Mode
+
+### Using Docker
+
+For development with hot reload, use the `dev` profile:
+
+```bash
+docker-compose --profile dev up dev
+```
+
+The application will be available at `http://localhost:4200`
+
+### Building Development Image Directly
+
+```bash
+docker build -f Dockerfile.dev -t sakai-ng:dev .
+docker run -it -v $(pwd):/app -p 4200:4200 sakai-ng:dev
+```
+
+## Environment Variables
+
+To use specific environment configurations, you can build with different targets. Currently, the Dockerfile builds for production. To build for staging:
+
+### Build for Staging
+
+Create a separate `Dockerfile.staging` if needed, or manually use:
+
+```bash
+docker run -e NODE_ENV=staging sakai-ng:latest
+```
+
+## Common Commands
+
+### Docker Compose Commands
+
+```bash
+# Start production
+docker-compose up -d app
+
+# Start staging
+docker-compose --profile staging up -d staging
+
+# Start development
+docker-compose --profile dev up dev
+
+# Start all services
+docker-compose --profile staging --profile dev up -d
+
+# Stop services
+docker-compose down
+
+# View logs
+docker-compose logs -f app # Production logs
+docker-compose logs -f staging # Staging logs
+docker-compose logs -f dev # Development logs
+
+# Rebuild images
+docker-compose build --no-cache
+```
+
+### Docker CLI Commands
+
+```bash
+# View Logs
+
+# Production
+docker logs sakai-ng
+
+# Staging
+docker logs sakai-ng-staging
+
+# Development
+docker logs sakai-ng-dev
+
+# Stop Container
+
+# Production
+docker stop sakai-ng
+docker rm sakai-ng
+
+# Staging
+docker stop sakai-ng-staging
+docker rm sakai-ng-staging
+
+# Development
+docker stop sakai-ng-dev
+docker rm sakai-ng-dev
+
+# List running containers
+docker ps
+
+# List all containers
+docker ps -a
+```
+
+### Clean Up
+
+```bash
+# Remove stopped containers
+docker container prune
+
+# Remove unused images
+docker image prune
+
+# Remove everything (careful!)
+docker system prune -a
+```
+
+## Image Size Optimization
+
+The Docker setup uses a multi-stage build:
+1. **Builder stage**: Compiles the Angular application
+2. **Production stage**: Uses nginx to serve the compiled application
+
+This approach keeps the final image small (typically <50MB) by excluding build dependencies.
+
+## Nginx Configuration
+
+The `nginx.conf` file includes:
+- Gzip compression for better performance
+- Security headers (X-Frame-Options, X-Content-Type-Options, etc.)
+- Proper caching headers for static assets
+- Angular routing support (SPA routing)
+
+## Port Configuration
+
+- **Production**: Port 80
+- **Development**: Port 4200
+
+To use different ports:
+
+```bash
+docker run -d -p 8080:80 sakai-ng:latest
+# App available at http://localhost:8080
+```
+
+## Building for Different Environments
+
+This project includes specialized Dockerfiles for different environments:
+
+### Production
+
+```bash
+docker build -t sakai-ng:prod .
+docker run -d -p 80:80 sakai-ng:prod
+```
+
+**Dockerfile**: `Dockerfile`
+**Base Image**: `nginx:alpine`
+**Configuration**: Production optimized build
+
+### Staging
+
+```bash
+docker build -f Dockerfile.staging -t sakai-ng:staging .
+docker run -d -p 8080:80 sakai-ng:staging
+```
+
+**Dockerfile**: `Dockerfile.staging`
+**Base Image**: `nginx:alpine`
+**Configuration**: Staging configuration with debugging enabled
+**Port**: 8080
+
+### Development
+
+```bash
+docker build -f Dockerfile.dev -t sakai-ng:dev .
+docker run -it -v $(pwd):/app -p 4200:4200 sakai-ng:dev
+```
+
+**Dockerfile**: `Dockerfile.dev`
+**Base Image**: `node:20-alpine`
+**Features**: Hot reload, source maps, development server
+**Port**: 4200
+**Volume**: Mounts current directory for live updates
+
+## Security Notes
+
+- The production image is minimal and only contains what's necessary to run the application
+- Security headers are configured in nginx
+- Health checks are built in to ensure container availability
+- Update base images regularly: `docker pull nginx:alpine` and `docker pull node:20-alpine`
+
+## Troubleshooting
+
+### Container exits immediately
+
+Check logs for errors:
+```bash
+docker logs sakai-ng
+```
+
+### Port already in use
+
+Use a different port:
+```bash
+docker run -d -p 8080:80 sakai-ng:latest
+```
+
+### Build fails on pnpm install
+
+Ensure `pnpm-lock.yaml` is up to date:
+```bash
+pnpm install --frozen-lockfile
+```
+
+## CI/CD Integration
+
+### GitHub Actions Example
+
+```yaml
+name: Build and Push Docker Image
+
+on:
+ push:
+ branches: [main, staging]
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@v3
+
+ - name: Build Docker image
+ run: |
+ if [ "${{ github.ref }}" = "refs/heads/staging" ]; then
+ docker build -f Dockerfile.staging -t sakai-ng:staging .
+ else
+ docker build -t sakai-ng:latest .
+ fi
+
+ - name: Push to registry
+ run: |
+ docker tag sakai-ng:latest your-registry/sakai-ng:latest
+ docker push your-registry/sakai-ng:latest
+```
+
+### Generic CI/CD
+
+```bash
+#!/bin/bash
+BUILD_ID=${CI_COMMIT_SHA:0:8}
+REGISTRY=${DOCKER_REGISTRY:-docker.io}
+
+# Build
+docker build -t $REGISTRY/sakai-ng:$BUILD_ID .
+
+# Test (optional)
+docker run --rm $REGISTRY/sakai-ng:$BUILD_ID wget --spider http://localhost
+
+# Push
+docker push $REGISTRY/sakai-ng:$BUILD_ID
+
+# Tag as latest
+docker tag $REGISTRY/sakai-ng:$BUILD_ID $REGISTRY/sakai-ng:latest
+docker push $REGISTRY/sakai-ng:latest
+```
+
+## References
+
+- [Docker Documentation](https://docs.docker.com/)
+- [Nginx Documentation](https://nginx.org/)
+- [Angular Deployment Guide](https://angular.io/guide/build)
diff --git a/Dockerfile b/Dockerfile
new file mode 100644
index 0000000..2118913
--- /dev/null
+++ b/Dockerfile
@@ -0,0 +1,38 @@
+# Build stage
+FROM node:20-alpine AS builder
+
+WORKDIR /app
+
+# Install pnpm
+RUN npm install -g pnpm
+
+# Copy package files
+COPY package.json pnpm-lock.yaml ./
+
+# Install dependencies
+RUN pnpm install --frozen-lockfile
+
+# Copy source code
+COPY . .
+
+# Build the application for production
+RUN pnpm run build
+
+# Production stage - serve with nginx
+FROM nginx:alpine
+
+# Copy nginx configuration
+COPY nginx.conf /etc/nginx/nginx.conf
+
+# Copy built application from builder stage
+COPY --from=builder /app/dist/pos.client/browser /usr/share/nginx/html
+
+# Expose port
+EXPOSE 80
+
+# Health check
+HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
+ CMD wget --quiet --tries=1 --spider http://localhost:80/ || exit 1
+
+# Start nginx
+CMD ["nginx", "-g", "daemon off;"]
diff --git a/Dockerfile.dev b/Dockerfile.dev
new file mode 100644
index 0000000..bb90a16
--- /dev/null
+++ b/Dockerfile.dev
@@ -0,0 +1,22 @@
+# Development Dockerfile with hot reload support
+FROM node:20-alpine
+
+WORKDIR /app
+
+# Install pnpm
+RUN npm install -g pnpm
+
+# Copy package files
+COPY package.json pnpm-lock.yaml ./
+
+# Install dependencies
+RUN pnpm install --frozen-lockfile
+
+# Copy source code
+COPY . .
+
+# Expose port for Angular dev server
+EXPOSE 4200
+
+# Start Angular development server
+CMD ["pnpm", "start"]
diff --git a/Dockerfile.staging b/Dockerfile.staging
new file mode 100644
index 0000000..61aa436
--- /dev/null
+++ b/Dockerfile.staging
@@ -0,0 +1,38 @@
+# Build stage for staging environment
+FROM node:20-alpine AS builder
+
+WORKDIR /app
+
+# Install pnpm
+RUN npm install -g pnpm
+
+# Copy package files
+COPY package.json pnpm-lock.yaml ./
+
+# Install dependencies
+RUN pnpm install --frozen-lockfile
+
+# Copy source code
+COPY . .
+
+# Build the application for staging
+RUN pnpm run build -- --configuration staging
+
+# Production stage - serve with nginx
+FROM nginx:alpine
+
+# Copy nginx configuration
+COPY nginx.conf /etc/nginx/nginx.conf
+
+# Copy built application from builder stage
+COPY --from=builder /app/dist/pos.client/browser /usr/share/nginx/html
+
+# Expose port
+EXPOSE 80
+
+# Health check
+HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
+ CMD wget --quiet --tries=1 --spider http://localhost:80/ || exit 1
+
+# Start nginx
+CMD ["nginx", "-g", "daemon off;"]
diff --git a/docker-compose.yml b/docker-compose.yml
new file mode 100644
index 0000000..6d4eb1e
--- /dev/null
+++ b/docker-compose.yml
@@ -0,0 +1,70 @@
+version: "3.8"
+
+services:
+ # Production service
+ app:
+ build:
+ context: .
+ dockerfile: Dockerfile
+ container_name: sakai-ng-prod
+ ports:
+ - "80:80"
+ environment:
+ - NODE_ENV=production
+ restart: unless-stopped
+ healthcheck:
+ test:
+ ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"]
+ interval: 30s
+ timeout: 3s
+ retries: 3
+ start_period: 5s
+ networks:
+ - sakai-network
+
+ # Staging service
+ staging:
+ build:
+ context: .
+ dockerfile: Dockerfile.staging
+ container_name: sakai-ng-staging
+ ports:
+ - "8080:80"
+ environment:
+ - NODE_ENV=staging
+ restart: unless-stopped
+ healthcheck:
+ test:
+ ["CMD", "wget", "--quiet", "--tries=1", "--spider", "http://localhost/"]
+ interval: 30s
+ timeout: 3s
+ retries: 3
+ start_period: 5s
+ profiles:
+ - staging
+ networks:
+ - sakai-network
+
+ # Development service with hot reload
+ dev:
+ build:
+ context: .
+ dockerfile: Dockerfile.dev
+ container_name: sakai-ng-dev
+ working_dir: /app
+ volumes:
+ - .:/app
+ - /app/node_modules
+ ports:
+ - "4200:4200"
+ environment:
+ - NODE_ENV=development
+ restart: unless-stopped
+ profiles:
+ - dev
+ networks:
+ - sakai-network
+
+networks:
+ sakai-network:
+ driver: bridge
diff --git a/nginx.conf b/nginx.conf
new file mode 100644
index 0000000..b4dd243
--- /dev/null
+++ b/nginx.conf
@@ -0,0 +1,69 @@
+user nginx;
+worker_processes auto;
+error_log /var/log/nginx/error.log warn;
+pid /var/run/nginx.pid;
+
+events {
+ worker_connections 1024;
+}
+
+http {
+ include /etc/nginx/mime.types;
+ default_type application/octet-stream;
+
+ log_format main '$remote_addr - $remote_user [$time_local] "$request" '
+ '$status $body_bytes_sent "$http_referer" '
+ '"$http_user_agent" "$http_x_forwarded_for"';
+
+ access_log /var/log/nginx/access.log main;
+
+ sendfile on;
+ tcp_nopush on;
+ tcp_nodelay on;
+ keepalive_timeout 65;
+ types_hash_max_size 2048;
+ client_max_body_size 20M;
+
+ gzip on;
+ gzip_vary on;
+ gzip_proxied any;
+ gzip_comp_level 6;
+ gzip_types text/plain text/css text/xml text/javascript
+ application/json application/javascript application/xml+rss
+ application/rss+xml font/truetype font/opentype
+ application/vnd.ms-fontobject image/svg+xml;
+
+ include /etc/nginx/conf.d/*.conf;
+
+ server {
+ listen 80;
+ server_name localhost;
+ charset utf-8;
+
+ root /usr/share/nginx/html;
+ index index.html index.htm;
+
+ # Security headers
+ add_header X-Frame-Options "SAMEORIGIN" always;
+ add_header X-Content-Type-Options "nosniff" always;
+ add_header X-XSS-Protection "1; mode=block" always;
+ add_header Referrer-Policy "strict-origin-when-cross-origin" always;
+
+ # Cache control for static assets
+ location ~* \.(js|css|png|jpg|jpeg|gif|ico|svg|woff|woff2|ttf|eot)$ {
+ expires 1y;
+ add_header Cache-Control "public, immutable";
+ }
+
+ # Angular routing - serve index.html for all routes
+ location / {
+ try_files $uri $uri/ /index.html;
+ }
+
+ # Don't cache index.html
+ location = /index.html {
+ expires -1;
+ add_header Cache-Control "no-store, no-cache, must-revalidate, proxy-revalidate";
+ }
+ }
+}
diff --git a/src/app/domains/consumer/modules/businessActivities/components/poses/form.component.html b/src/app/domains/consumer/modules/businessActivities/components/poses/form.component.html
index d902c50..c4d25d4 100644
--- a/src/app/domains/consumer/modules/businessActivities/components/poses/form.component.html
+++ b/src/app/domains/consumer/modules/businessActivities/components/poses/form.component.html
@@ -8,13 +8,6 @@
>
diff --git a/src/app/domains/consumer/modules/businessActivities/components/poses/form.component.ts b/src/app/domains/consumer/modules/businessActivities/components/poses/form.component.ts
index 0a35407..20f11ab 100644
--- a/src/app/domains/consumer/modules/businessActivities/components/poses/form.component.ts
+++ b/src/app/domains/consumer/modules/businessActivities/components/poses/form.component.ts
@@ -34,35 +34,8 @@ export class ConsumerPosFormComponent extends AbstractFormDialog {
const form = this.fb.group({
name: [this.initialValues?.name || '', [Validators.required]],
- serial: [this.initialValues?.serial || '', [Validators.required]],
- // model: [this.initialValues?.model || '', [Validators.required]],
- pos_type: [this.initialValues?.pos_type || '', [Validators.required]],
- device_id: [this.initialValues?.device?.id || ''],
- provider_id: [this.initialValues?.provider?.id || ''],
- });
- form.controls.pos_type.valueChanges.subscribe((value) => {
- if (value === 'PSP') {
- form.addControl(
- 'device_id',
- this.fb.control(this.initialValues?.device?.id ?? '', {
- nonNullable: true,
- validators: [Validators.required],
- }),
- );
- form.addControl(
- 'provider_id',
- this.fb.control(this.initialValues?.provider?.id ?? '', {
- nonNullable: true,
- validators: [Validators.required],
- }),
- );
- } else {
- // @ts-ignore
- form.removeControl('device_id');
- // @ts-ignore
- form.removeControl('provider_id');
- }
});
+
return form;
};
diff --git a/src/app/domains/consumer/modules/businessActivities/components/salesInvoices/list.component.html b/src/app/domains/consumer/modules/businessActivities/components/salesInvoices/list.component.html
new file mode 100644
index 0000000..953e2e5
--- /dev/null
+++ b/src/app/domains/consumer/modules/businessActivities/components/salesInvoices/list.component.html
@@ -0,0 +1,8 @@
+
+
diff --git a/src/app/domains/consumer/modules/businessActivities/components/salesInvoices/list.component.ts b/src/app/domains/consumer/modules/businessActivities/components/salesInvoices/list.component.ts
new file mode 100644
index 0000000..f882c48
--- /dev/null
+++ b/src/app/domains/consumer/modules/businessActivities/components/salesInvoices/list.component.ts
@@ -0,0 +1,47 @@
+// import { CatalogRoleTagComponent } from '@/shared/catalog/roles';
+import { AbstractList } from '@/shared/abstractClasses/abstract-list';
+import {
+ IColumn,
+ PageDataListComponent,
+} from '@/shared/components/pageDataList/page-data-list.component';
+import { Component, inject, Input } from '@angular/core';
+import { ISalesInvoicesResponse } from '../../models';
+import { ConsumerSalesInvoicesService } from '../../services/invoices.service';
+
+@Component({
+ selector: 'consumer-salesInvoices-list',
+ templateUrl: './list.component.html',
+ imports: [PageDataListComponent],
+})
+export class ConsumerSalesInvoicesComponent extends AbstractList {
+ @Input({ required: true }) complexId!: string;
+ @Input({ required: true }) posId!: string;
+ @Input() fullHeight?: boolean;
+ @Input() header: IColumn[] = [
+ { field: 'id', header: 'شناسه', type: 'id' },
+ { field: 'code', header: 'کد رهگیری' },
+ { field: 'total_amount', header: 'مبلغ کل', type: 'price' },
+ {
+ field: 'items_count',
+ header: 'تعداد کالاها',
+ customDataModel(item) {
+ return `${item.items.length} عدد`;
+ },
+ },
+ {
+ field: 'invoice_date',
+ header: 'تاریخ',
+ type: 'date',
+ },
+ ];
+
+ private readonly service = inject(ConsumerSalesInvoicesService);
+
+ override setColumns(): void {
+ this.columns = this.header;
+ }
+
+ override getDataRequest() {
+ return this.service.getAll(this.complexId, this.posId);
+ }
+}
diff --git a/src/app/domains/consumer/modules/businessActivities/constants/apiRoutes/posSalesInvoices.ts b/src/app/domains/consumer/modules/businessActivities/constants/apiRoutes/posSalesInvoices.ts
new file mode 100644
index 0000000..5948d61
--- /dev/null
+++ b/src/app/domains/consumer/modules/businessActivities/constants/apiRoutes/posSalesInvoices.ts
@@ -0,0 +1,8 @@
+const baseUrl = (complexId: string, posId: string) =>
+ `/api/v1/consumer/complexes/${complexId}/poses/${posId}/sales_invoices`;
+
+export const POS_SALES_INVOICES_API_ROUTES = {
+ list: (complexId: string, posId: string) => `${baseUrl(complexId, posId)}`,
+ single: (complexId: string, posId: string, invoiceId: string) =>
+ `${baseUrl(complexId, posId)}/${invoiceId}`,
+};
diff --git a/src/app/domains/consumer/modules/businessActivities/models/index.ts b/src/app/domains/consumer/modules/businessActivities/models/index.ts
index cf6f041..c505459 100644
--- a/src/app/domains/consumer/modules/businessActivities/models/index.ts
+++ b/src/app/domains/consumer/modules/businessActivities/models/index.ts
@@ -1,3 +1,4 @@
export * from './complexes_io';
export * from './io';
export * from './poses_io';
+export * from './salesInvoices_io';
diff --git a/src/app/domains/consumer/modules/businessActivities/models/poses_io.d.ts b/src/app/domains/consumer/modules/businessActivities/models/poses_io.d.ts
index 1c76cae..17d9029 100644
--- a/src/app/domains/consumer/modules/businessActivities/models/poses_io.d.ts
+++ b/src/app/domains/consumer/modules/businessActivities/models/poses_io.d.ts
@@ -15,10 +15,4 @@ export interface IPosResponse extends IPosRawResponse {}
export interface IPosRequest {
name: string;
- serial: string;
- model?: string;
- status: string;
- pos_type: string;
- device_id: string;
- provider_id: string;
}
diff --git a/src/app/domains/consumer/modules/businessActivities/models/salesInvoices_io.d.ts b/src/app/domains/consumer/modules/businessActivities/models/salesInvoices_io.d.ts
new file mode 100644
index 0000000..b054f03
--- /dev/null
+++ b/src/app/domains/consumer/modules/businessActivities/models/salesInvoices_io.d.ts
@@ -0,0 +1,51 @@
+import ISummary from '@/core/models/summary';
+import { TCustomerInfo, TPosOrderGoodPayload } from '@/domains/pos/modules/landing/models';
+import { UnitType } from '@/utils';
+
+export interface ISalesInvoicesRawResponse {
+ id: string;
+ code: string;
+ invoice_date: string;
+ total_amount: string;
+ items: Item[];
+ payments: Payment[];
+ customer?: TCustomerInfo;
+ notes?: string;
+ unknown_customer?: UnknownCustomer;
+}
+export interface ISalesInvoicesResponse extends ISalesInvoicesRawResponse {}
+
+export interface ISalesInvoicesRequest {}
+
+interface UnknownCustomer {
+ name?: string;
+ national_code?: string;
+}
+
+interface Payment {
+ amount: string;
+ paid_at: string;
+ payment_method: string;
+}
+
+interface Item {
+ unit_type: UnitType;
+ discount: string;
+ quantity: string;
+ total_amount: string;
+ unit_price: string;
+ payload: TPosOrderGoodPayload;
+ good: Good;
+ notes?: string;
+}
+
+interface Good {
+ id: string;
+ name: string;
+ sku: string;
+ barcode: null;
+ local_sku: null;
+ pricing_model: string;
+ unit_type: string;
+ category: ISummary;
+}
diff --git a/src/app/domains/consumer/modules/businessActivities/services/invoices.service.ts b/src/app/domains/consumer/modules/businessActivities/services/invoices.service.ts
new file mode 100644
index 0000000..aa58367
--- /dev/null
+++ b/src/app/domains/consumer/modules/businessActivities/services/invoices.service.ts
@@ -0,0 +1,28 @@
+import { IPaginatedResponse } from '@/core/models/service.model';
+import { HttpClient } from '@angular/common/http';
+import { Injectable } from '@angular/core';
+import { Observable } from 'rxjs';
+import { POS_SALES_INVOICES_API_ROUTES } from '../constants/apiRoutes/posSalesInvoices';
+import { ISalesInvoicesRawResponse, ISalesInvoicesResponse } from '../models';
+
+@Injectable({ providedIn: 'root' })
+export class ConsumerSalesInvoicesService {
+ constructor(private http: HttpClient) {}
+
+ private apiRoutes = POS_SALES_INVOICES_API_ROUTES;
+
+ getAll(complexId: string, posId: string): Observable> {
+ return this.http.get>(
+ this.apiRoutes.list(complexId, posId),
+ );
+ }
+ getSingle(
+ complexId: string,
+ posId: string,
+ invoiceId: string,
+ ): Observable {
+ return this.http.get(
+ this.apiRoutes.single(complexId, posId, invoiceId),
+ );
+ }
+}
diff --git a/src/app/domains/consumer/modules/businessActivities/views/poses/single.component.html b/src/app/domains/consumer/modules/businessActivities/views/poses/single.component.html
index afdc3f2..165f996 100644
--- a/src/app/domains/consumer/modules/businessActivities/views/poses/single.component.html
+++ b/src/app/domains/consumer/modules/businessActivities/views/poses/single.component.html
@@ -6,10 +6,17 @@
+
+
{{ good.name }}
-
+ @if (!good.is_default_guild_good) {
+ *
+ }
-
diff --git a/src/app/domains/pos/modules/landing/components/list-view.component.html b/src/app/domains/pos/modules/landing/components/list-view.component.html
index 4f4588a..e700d65 100644
--- a/src/app/domains/pos/modules/landing/components/list-view.component.html
+++ b/src/app/domains/pos/modules/landing/components/list-view.component.html
@@ -13,6 +13,9 @@
{{ good.name }}
+ @if (!good.is_default_guild_good) {
+ *
+ }
@@ -22,7 +25,7 @@
-
+
}
diff --git a/src/app/domains/pos/modules/landing/components/order/order-section.component.html b/src/app/domains/pos/modules/landing/components/order/order-section.component.html
index 9b113f5..d8baa15 100644
--- a/src/app/domains/pos/modules/landing/components/order/order-section.component.html
+++ b/src/app/domains/pos/modules/landing/components/order/order-section.component.html
@@ -1,10 +1,5 @@
-
-
-
+
@@ -20,7 +15,7 @@
هیچ سفارشی ثبت نشده است.
} @else {
-
+
@for (inOrderGood of inOrderGoods(); track inOrderGood.id) {
@if (inOrderGood.good.pricing_model === "GOLD") {
@@ -31,20 +26,25 @@
}
-
diff --git a/src/app/domains/pos/modules/landing/components/order/order-section.component.ts b/src/app/domains/pos/modules/landing/components/order/order-section.component.ts
index c271765..300ffc2 100644
--- a/src/app/domains/pos/modules/landing/components/order/order-section.component.ts
+++ b/src/app/domains/pos/modules/landing/components/order/order-section.component.ts
@@ -4,6 +4,7 @@ import { KeyValueComponent } from '@/shared/components';
import { Component, computed, inject, signal } from '@angular/core';
import { FormsModule } from '@angular/forms';
import { ButtonDirective } from 'primeng/button';
+import { Card } from 'primeng/card';
import images from 'src/assets/images';
import { PosLandingStore } from '../../store/main.store';
import { PosOrderCustomerDialogComponent } from '../customers/customer-dialog.component';
@@ -24,6 +25,7 @@ import { POSOrderPriceInfoCardComponent } from './price-info-card.component';
PosOrderCustomerDialogComponent,
KeyValueComponent,
PosPaymentFormDialogComponent,
+ Card,
],
})
export class PosOrderSectionComponent {
@@ -44,13 +46,13 @@ export class PosOrderSectionComponent {
switch (type) {
case 'UNKNOWN':
- customerNameToShow = `${info?.customerUnknown?.name || 'نامشخص'} (نوع دوم)`;
+ customerNameToShow = `${info?.customer_unknown?.name || 'نامشخص'} (نوع دوم)`;
break;
case 'INDIVIDUAL':
- customerNameToShow = `${info?.customerIndividuals ? info?.customerIndividuals.first_name + ' ' + info?.customerIndividuals.last_name : 'نامشخص'} (نوع اول)`;
+ customerNameToShow = `${info?.customer_individual ? info?.customer_individual.first_name + ' ' + info?.customer_individual.last_name : 'نامشخص'} (نوع اول)`;
break;
case 'LEGAL':
- customerNameToShow = `${info?.customerLegals?.company_name || 'نامشخص'} (نوع اول)`;
+ customerNameToShow = `${info?.customer_legal?.company_name || 'نامشخص'} (نوع اول)`;
break;
}
diff --git a/src/app/domains/pos/modules/landing/components/payment/form-dialog.component.html b/src/app/domains/pos/modules/landing/components/payment/form-dialog.component.html
index 3006830..bd03078 100644
--- a/src/app/domains/pos/modules/landing/components/payment/form-dialog.component.html
+++ b/src/app/domains/pos/modules/landing/components/payment/form-dialog.component.html
@@ -43,7 +43,6 @@
submitLabel="ثبت اطلاعات پرداخت و ادامه"
[loading]="submitOrderLoading()"
(onCancel)="close()"
- (onSubmit)="submit()"
/>
diff --git a/src/app/domains/pos/modules/landing/components/payment/form-dialog.component.ts b/src/app/domains/pos/modules/landing/components/payment/form-dialog.component.ts
index e6371b7..56523dd 100644
--- a/src/app/domains/pos/modules/landing/components/payment/form-dialog.component.ts
+++ b/src/app/domains/pos/modules/landing/components/payment/form-dialog.component.ts
@@ -6,7 +6,7 @@ import { Component, computed, inject, signal } from '@angular/core';
import { ReactiveFormsModule, Validators } from '@angular/forms';
import { ButtonDirective } from 'primeng/button';
import { Dialog } from 'primeng/dialog';
-import { Observable } from 'rxjs';
+import { catchError, throwError } from 'rxjs';
import { IPayment, TOrderPaymentTypes } from '../../models';
import { PosLandingStore } from '../../store/main.store';
@@ -90,6 +90,8 @@ export class PosPaymentFormDialogComponent extends AbstractFormDialog
0) {
return this.toastServices.warn({
@@ -100,16 +102,19 @@ export class PosPaymentFormDialogComponent extends AbstractFormDialog {
- if (res) {
+ this.store
+ .submitOrder()
+ .pipe(
+ catchError((err) => {
+ return throwError(() => err);
+ }),
+ )
+ .subscribe(() => {
this.close();
this.toastServices.success({
text: 'فاکتور شما با موفقیت ایجاد شد',
});
- }
- });
-
- return new Observable((observer) => observer.next(payment));
+ });
}
override onSuccess(response: IPayment): void {}
diff --git a/src/app/domains/pos/modules/landing/models/customer.ts b/src/app/domains/pos/modules/landing/models/customer.ts
index 0df00e5..953cd64 100644
--- a/src/app/domains/pos/modules/landing/models/customer.ts
+++ b/src/app/domains/pos/modules/landing/models/customer.ts
@@ -20,9 +20,9 @@ export interface IUnknownCustomer {
}
export interface ICustomer {
- customerIndividuals?: IIndividualCustomer;
- customerLegals?: ILegalCustomer;
- customerUnknown?: IUnknownCustomer;
+ customer_individual?: IIndividualCustomer;
+ customer_legal?: ILegalCustomer;
+ customer_unknown?: IUnknownCustomer;
}
export type TCustomerInfo = IIndividualCustomer | ILegalCustomer | IUnknownCustomer;
diff --git a/src/app/domains/pos/modules/landing/models/io.d.ts b/src/app/domains/pos/modules/landing/models/io.d.ts
index 8f0bfb1..a780ca3 100644
--- a/src/app/domains/pos/modules/landing/models/io.d.ts
+++ b/src/app/domains/pos/modules/landing/models/io.d.ts
@@ -10,7 +10,7 @@ export interface IPosOrderRequest {
items: IPosOrderItem[];
notes?: string;
customer_type?: CustomerType;
- customerId?: string;
+ customer_id?: string;
customer?: ICustomer;
}
diff --git a/src/app/domains/pos/modules/landing/store/main.store.ts b/src/app/domains/pos/modules/landing/store/main.store.ts
index 9c348f5..3f5c1a8 100644
--- a/src/app/domains/pos/modules/landing/store/main.store.ts
+++ b/src/app/domains/pos/modules/landing/store/main.store.ts
@@ -4,7 +4,7 @@ import { IGoodCategoryResponse, IGoodResponse } from '@/domains/pos/models/good.
import { CustomerType } from '@/shared/localEnum/constants/customerTypes';
import { createUUID, JALALI_DATE_FORMATS, nowJalali, parseJalali } from '@/utils';
import { computed, inject, Injectable, signal } from '@angular/core';
-import { catchError, finalize, map, Observable, throwError } from 'rxjs';
+import { catchError, finalize, map, Observable } from 'rxjs';
import { ICustomer, IPayment, IPosOrderRequest, IPosOrderResponse } from '../models';
import { IPosInOrderGood, IPosOrderItem } from '../models/types';
import { PosService } from '../services/main.service';
@@ -208,10 +208,10 @@ export class PosLandingStore {
this.setState({ payments });
}
- async submitOrder() {
+ submitOrder() {
this.setState({ submitOrderLoading: true });
const orderPayload: IPosOrderRequest = {
- customerId: this.customer().customer_id,
+ customer_id: this.customer().customer_id,
customer_type: this.customer().type,
customer: this.customer().info,
items: this.inOrderGoods().map(({ good, ...rest }) => ({
@@ -226,24 +226,30 @@ export class PosLandingStore {
let res: Maybe = null;
- await this.service
- .submitOrder(orderPayload)
- .pipe(
- finalize(() => {
- this.setState({ submitOrderLoading: false });
- }),
- catchError(() => {
- return throwError('');
- }),
- )
- .subscribe((_res) => {
- this.reset();
- res = _res;
- });
+ return this.service.submitOrder(orderPayload).pipe(
+ finalize(() => {
+ this.setState({ submitOrderLoading: false });
+ }),
+ catchError(() => {
+ return new Observable((observer) => observer.error());
+ }),
- if (res) {
- return new Observable((observer) => observer.next(res!));
- }
- return new Observable((observer) => observer.error());
+ map((_res) => {
+ // this.setState({
+ // inOrderGoods: [],
+ // customerDetails: {
+ // type: CustomerType.UNKNOWN,
+ // },
+ // invoiceDate: nowJalali(JALALI_DATE_FORMATS.NUMERIC),
+ // payments: {
+ // cash: 0,
+ // set_off: 0,
+ // terminal: 0,
+ // },
+ // orderNote: '',
+ // });
+ return _res;
+ }),
+ );
}
}
diff --git a/src/app/modules/pos/store/index.ts b/src/app/modules/pos/store/index.ts
index d63476f..0588075 100644
--- a/src/app/modules/pos/store/index.ts
+++ b/src/app/modules/pos/store/index.ts
@@ -251,7 +251,7 @@ export class POSStore {
submitOrder() {
const orderPayload = {
- customerId: this.selectedCustomer()?.id,
+ customer_id: this.selectedCustomer()?.id,
items: this.inOrderProducts()?.map((item) => ({
productId: item.productId,
count: item.count,
diff --git a/src/app/shared/components/input/input.component.ts b/src/app/shared/components/input/input.component.ts
index 360085f..f553fca 100644
--- a/src/app/shared/components/input/input.component.ts
+++ b/src/app/shared/components/input/input.component.ts
@@ -184,8 +184,6 @@ export class InputComponent {
}
onPriceInput($event: InputNumberInputEvent) {
- console.log($event);
-
this.onInput($event.originalEvent, true);
}
onInput($event: Event, isPriceFormat?: boolean) {
@@ -198,7 +196,6 @@ export class InputComponent {
if (this.inputMode === 'numeric') {
let newValue = parseFloat(value);
- // console.log(value, newValue);
const minValidator = (this.min || this.min === 0) && parseFloat(value) < this.min!;
const maxValidator = (this.max || this.max === 0) && parseFloat(value) > this.max;
diff --git a/src/app/shared/components/key-value.component/key-value.component.ts b/src/app/shared/components/key-value.component/key-value.component.ts
index 50e7455..18d3995 100644
--- a/src/app/shared/components/key-value.component/key-value.component.ts
+++ b/src/app/shared/components/key-value.component/key-value.component.ts
@@ -89,7 +89,7 @@ export class KeyValueComponent {
}
switch (this.type) {
case 'simple':
- return this.value || '';
+ return this.value || '-';
}
return value;
}
diff --git a/src/app/shared/components/pageDataList/page-data-list.component.ts b/src/app/shared/components/pageDataList/page-data-list.component.ts
index c126315..39ff44f 100644
--- a/src/app/shared/components/pageDataList/page-data-list.component.ts
+++ b/src/app/shared/components/pageDataList/page-data-list.component.ts
@@ -26,7 +26,7 @@ export interface IColumn {
width?: string;
minWidth?: string;
canCopy?: boolean;
- type?: 'text' | 'price' | 'boolean' | 'date' | 'nested' | 'index';
+ type?: 'text' | 'price' | 'boolean' | 'date' | 'nested' | 'index' | 'id';
nestedPath?: string;
customDataModel?: TemplateRef | ((item: T) => string | number | boolean);
}
@@ -139,6 +139,9 @@ export class PageDataListComponent {
}
const data = item[String(field)];
switch (column.type) {
+ case 'id':
+ if (!data) return '-';
+ return `${data.slice(0, 5)}...${data.slice(data.length - 5, data.length)}`;
case 'date':
if (!data) return '-';
return formatJalali(data);