0% found this document useful (0 votes)
5 views46 pages

new-add-order.component

The document describes the implementation of a NewAddOrderComponent in an Angular application, which manages the creation and processing of new orders. It includes functionalities for initializing forms, handling user inputs, automating calculations for order pricing and weight, and managing subscriptions to observables to ensure efficient data handling. The component also supports cloning existing orders and validating order dates to ensure they are within an acceptable timeframe.

Uploaded by

vinay.somawat
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
5 views46 pages

new-add-order.component

The document describes the implementation of a NewAddOrderComponent in an Angular application, which manages the creation and processing of new orders. It includes functionalities for initializing forms, handling user inputs, automating calculations for order pricing and weight, and managing subscriptions to observables to ensure efficient data handling. The component also supports cloning existing orders and validating order dates to ensure they are within an acceptable timeframe.

Uploaded by

vinay.somawat
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as TXT, PDF, TXT or read online on Scribd
You are on page 1/ 46

import { Component, OnDestroy, OnInit } from '@angular/core';

import { HttpService } from 'src/app/service/http-service.service';


import { QuickShipPickupAddress } from '../../addorder.model';
import { IShippingAddress } from 'src/app/pages/orders/orders.model';
import { FormArray, FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Subject, Subscription, fromEvent } from 'rxjs';
import { AddOrdersService } from '../../addorders.service';
import { MatDialog } from '@angular/material/dialog';
import { PickupDialogComponent } from '../../pickup-dialog/pickup-
dialog.component';
import { minValueValidator, mobileNumberValidator, packageCountValidator } from
'../new-add-order-validators';
import { ToastrService } from 'ngx-toastr';
import { debounceTime } from 'rxjs/operators';
import { MatChipInputEvent } from '@angular/material/chips';
import { MatAutocompleteSelectedEvent } from '@angular/material/autocomplete';
import { COMMA, ENTER } from '@angular/cdk/keycodes';
import { ManagePackageCatalogComponent } from '../manage-package-catalog/manage-
package-catalog.component';
import { OrdersService } from 'src/app/pages/orders/orders.service';
import { getUser } from 'src/app/shared/ts/utills';
import { VerifyAddressComponent } from '../../pickup-dialog/verify-address/verify-
address.component';
import { ConfirmSecureShipmentPopComponent } from 'src/app/shared/component/secure-
shipment-confirmation-popup/confirm-secure-shipment-pop/confirm-secure-shipment-
pop.component';
import { Router } from '@angular/router';
import { IntercomService } from 'src/app/service/intercom.service';
import { DatePipe } from '@angular/common';
import { WeightInsightsComponent } from
'src/app/pages/orders/weight-insights/weight-insights.component';

@Component({
selector: 'app-new-add-order',
templateUrl: './new-add-order.component.html',
styleUrls: ['./new-add-order.component.scss']
})
export class NewAddOrderComponent implements OnInit, OnDestroy {
private sink = new Subscription();
createOrderForm!: FormGroup;
stateList: Array<any> = [];
pickUpAddress: QuickShipPickupAddress[] = [];
selectedPickupAddress: IShippingAddress | null = null;
hasAutoSecureShipmentAdopted: boolean = false;
productList: Array<any> = [];
userQuestionUpdate = new Subject<any>();
isOtherChargesOpen: boolean = false;
otherCharges: number = 0.00;
volumetricWeight: number = 0.00;
applicableweight: number = 0.00;
weightFreezeData: any = undefined;
packageList: Array<any> = [];
approvedPackageList: Array<any> = [];
savedPackageList: Array<any> = [];
channelList: Array<any> = [];
renderChannelList: Array<any> = [];
socialMediaChCount: number = 0;
showMore: boolean = false;
toggleOthrDetals: boolean = false;
selectedChips: any = [];
orderTagsList: Array<any> = [];
separatorKeysCodes: number[] = [ENTER, COMMA];
customFormControlFields: any = undefined;
todayDate: any;
last6mon: any;
weightInsights: any = undefined;
userData = getUser();
lastSearchProductString: string = '';
isDimensionDisabled: boolean = false; //When dimension is prefiilled by catalog
or packaae
isPrefilledDimension: boolean = false; //This will show edit button
isManagePackageOpen: boolean = false; //This will show save package option
selectedPackage: any = undefined; //This will show selected package
autoCompleteScrollEvent: any = undefined;
categoryList: Array<any> = [];
isServiceabilityVisible: boolean = false;
courierResponse: any = { undefined };
courierlist: any;
buyerLevelInfo: any = undefined;
shipmentDetails: any;
serviceabilityDataToSend: any;
customerDetailList: Array<any> = [];
isPackageingSuggestionOpen: boolean = false;
isGrawlVisible: boolean = false;
timeoutId: any;
minimumWeight: number = 0.1;
constructor(
private httpService: HttpService,
private fb: FormBuilder,
private addorderservice: AddOrdersService,
public dialog: MatDialog,
private toastr: ToastrService,
private ordersService: OrdersService,
private Router: Router,
private intercomService: IntercomService,
private datePipe: DatePipe
) {
this.calculateOrderDateValidity();
this.sink.add(this.addorderservice.getOptionalFields().subscribe((res) => {
if (res?.settings) this.customFormControlFields = res.settings;
}));
this.initializeCreateOrderForm();
this.isGrawlVisible = localStorage.isGrawlOpen == "OPENED" ? false : true;
}

/**
* Initializes the component by fetching required data, setting up observables, and
automating calculations.
*
* **Flow**:
* 1. Fetches the state list, pickup location, channels, order tags, and package
details.
* 2. Sets up cloned order data for clone order.
* 3. Subscribes to company details to configure auto-shipment insurance options.
* 4. Handles user input for fetching products or categories with debouncing to
improve performance.
* 5. Automates calculations for product pricing and package weight.
*
* **Purpose**: Prepares the component with all necessary data and logic when it is
initialized.
*
* **Side Effects**: Subscribes to multiple observables and updates class
properties.
*/
ngOnInit(): void {
this.fetchStateList('99');
this.getPickupLocation();
this.getChannels();
this.getOrderTags();
this.getPackageDetails([], undefined, true);
this.setCloneOrderData();
this.sink.add(this.addorderservice.getCompanyDetails().subscribe((res: any) =>
{
if (res?.data) {
this.hasAutoSecureShipmentAdopted =
res?.data?.allow_auto_shipment_insurance;
this.createOrderForm.patchValue({ is_insurance_opt:
this.hasAutoSecureShipmentAdopted });
}
}));

this.sink.add(this.userQuestionUpdate.pipe(debounceTime(200)).subscribe((data:
any) => {
if (data.type == 'product') {
this.fetchProductsByName(data.productName);
} else {
this.fetchCategory(data.categoryName)
}
}));

this.automateProductPriceCalculation();
this.automateWeightCalculation();
}

initializeCreateOrderForm() {
this.createOrderForm = this.fb.group({
shipping_phone: ['', [Validators.minLength(10), Validators.maxLength(10),
Validators.required, mobileNumberValidator()]],
shipping_customer_name: ['', [Validators.required, Validators.maxLength(50),
Validators.minLength(3)]],
shipping_address: ['', [Validators.required, Validators.minLength(3)]],
shipping_pincode: ['', [Validators.required, Validators.minLength(6),
Validators.maxLength(6), Validators.pattern(/^\d{6}$/)]],
shipping_city: ['', [Validators.required]],
shipping_state: ['', [Validators.required]],
shipping_country: ['India', [Validators.required]],
shipping_is_billing: [true, [Validators.required]],
shipping_address_2: [''],
customer_gstin: ['', [Validators.minLength(15), Validators.maxLength(15)]],
shipping_email: ['', [Validators.email]],
billing_alternate_phone: ['', [Validators.minLength(10),
Validators.maxLength(10)]],
company_name: [''],
shipping_charges: [''],

billing_phone: ['', [Validators.minLength(10), Validators.maxLength(10),


Validators.required, mobileNumberValidator()]],
billing_customer_name: ['', [Validators.required, Validators.maxLength(50),
Validators.minLength(3)]],
billing_address: ['', [Validators.required, Validators.minLength(3)]],
billing_pincode: ['', [Validators.required, Validators.minLength(6),
Validators.maxLength(6)]],
billing_city: ['', [Validators.required]],
billing_state: ['', [Validators.required]],
billing_country: ['India', [Validators.required]],
billing_address_2: [''],
billing_email: [''],

is_document: ['0', Validators.required],


pickup_location_id: ['', [Validators.required]],
giftwrap_charges: [''],
transaction_charges: [''],
total_discount: [''],
weight: ['', [Validators.required, minValueValidator(0.1)]],
length: ['', [Validators.required, minValueValidator(0.5)]],
breadth: ['', [Validators.required, minValueValidator(0.5)]],
height: ['', [Validators.required, minValueValidator(0.5)]],
payment_method: ['prepaid', [Validators.required]],
channel_id: ['', [Validators.required]],
order_tag: [],
reseller_name: [''],
custom_field: [''],
comment: [''],
is_order_revamp: [1],
is_web: [1, Validators.required],
is_insurance_opt: ['1', Validators.required],
currency: ['INR', Validators.required],
order_id: [this.addorderservice.generateOrderId(), [Validators.required]],
isd_code: ['+91', [Validators.required]],
order_items: this.fb.array([this.createProductGroup()]),
sub_total: [0.0, [Validators.required]],
order_value: [0.0, [Validators.required, Validators.min(0),
Validators.max(5000000)]],
order_date: [this.todayDate, [Validators.required]],
package_count: ['1', [packageCountValidator()]],
is_send_notification: [this.userData?.is_send_notification],
shipping_mode: ['none'],
delivery_challan: ['false'],
billing_last_name: [''], //blank field as per old design
landmark: [''], //blank field as per old design
latitude: [''], //blank field as per old design
longitude: [''], //blank field as per old design
shipping_last_name: [''], //blank field as per old design
order_type: [''], //blank field as per old design
product_category: [''], //blank field as per old design
ewaybill_no: [''], //blank field as per old design
purpose_of_shipment: [''], //blank field as per old design
});
}

// Create a FormGroup for each product with validation rules


createProductGroup(): FormGroup {
return this.fb.group({
name: ['', Validators.required],
selling_price: ['', [Validators.required, Validators.maxLength(10),
Validators.min(0.01)]],
units: ['1', [Validators.required, Validators.maxLength(10),
Validators.min(1)]],
discount: ['', [Validators.maxLength(10)]],
category_id: [''],
category_name: [''],
hsn: [''],
sku: [''],
tax: [''],
caetgroy_code: [''],
product_id: [''],
product_description: [''],
optionalValueStatus: [''],
insights: ['']
});
}

/**
* Recalculates the total order amount when specific form fields or the product
list change.
* Subscribes to changes in `shipping_charges`, `giftwrap_charges`,
`transaction_charges`, `total_discount`, and `products`.
* Uses `debounceTime` to avoid redundant calculations on rapid input. Manages
subscriptions with `sink.add()` to prevent memory leaks.
*/
automateProductPriceCalculation(): void {

this.sink.add(this.createOrderForm.get('shipping_charges')?.valueChanges.pipe(debou
nceTime(200)).subscribe(() => {
this.calculateOrderAmount();
}));

this.sink.add(this.createOrderForm.get('giftwrap_charges')?.valueChanges.pipe(debou
nceTime(200)).subscribe(() => {
this.calculateOrderAmount();
}));

this.sink.add(this.createOrderForm.get('transaction_charges')?.valueChanges.pipe(de
bounceTime(200)).subscribe(() => {
this.calculateOrderAmount();
}));

this.sink.add(this.createOrderForm.get('total_discount')?.valueChanges.pipe(debounc
eTime(200)).subscribe(() => {
this.calculateOrderAmount();
}));

this.sink.add(this.products.valueChanges.pipe(debounceTime(300)).subscribe(()
=> {
this.calculateOrderAmount();
}));
}

/**
* Recalculates volumetric weight when form fields (length, breadth, height,
weight) change.
* Subscribes to changes in these fields and triggers `calculateVolumetricWeight()`
with a 200ms debounce to reduce redundant calculations.
* Manages subscriptions using `this.sink.add()` to prevent memory leaks.
*/
automateWeightCalculation(): void {

this.sink.add(this.createOrderForm.get('length')?.valueChanges.pipe(debounceTime(20
0)).subscribe(() => {
this.calculateVolumerticWeight();
}));

this.sink.add(this.createOrderForm.get('breadth')?.valueChanges.pipe(debounceTime(2
00)).subscribe(() => {
this.calculateVolumerticWeight();
}));

this.sink.add(this.createOrderForm.get('height')?.valueChanges.pipe(debounceTime(20
0)).subscribe(() => {
this.calculateVolumerticWeight();
}));

this.sink.add(this.createOrderForm.get('weight')?.valueChanges.pipe(debounceTime(20
0)).subscribe(() => {
this.calculateVolumerticWeight();
}));
}

/**
* Calculates and sets the validity of the order date by updating `todayDate` and
`last6mon`.
*
* **Flow**:
* 1. Retrieves the current date and formats it as 'yyyy-MM-dd' for `todayDate`.
* 2. Calculates the date 6 months ago and formats it as 'yyyy-MM-dd' for
`last6mon`.
*
* **Purpose**: Ensures that the order date is within a valid time frame, typically
within the last 6 months.
*
* **Usage**: Used for date-based validation when creating or editing orders.
*
* **Side Effects**: Updates the `todayDate` and `last6mon` class properties.
*/
calculateOrderDateValidity(): void {
const currentDate = new Date();
this.todayDate = this.datePipe.transform(currentDate, 'yyyy-MM-dd');
currentDate.setMonth(currentDate.getMonth() - 6);
this.last6mon = this.datePipe.transform(currentDate, 'yyyy-MM-dd');
}
/**
* Retrieves cloned order data from localStorage, fetches order details from the
API, and patches the form with the fetched data.
*
* **Flow**:
* 1. Checks if `cloneData` exists in `localStorage`, retrieves and parses it.
* 2. Makes an API request using `ordersService.showApi()` to fetch order
details.
* 3. Patches the form with the fetched order data using
`createDataForPatchValue`.
* 4. Updates specific fields such as shipping pincode and product details.
* 5. Handles special logic if the order is a document, including setting
specific weight and dimension values.
* 6. Cleans up `cloneData` from `localStorage` after processing.
*
* **Purpose**: To initialize and populate the order form with data from a cloned
order.
*/
setCloneOrderData(): void {
const orderCloneData = localStorage.cloneData ?
JSON.parse(localStorage.cloneData) : undefined;
delete localStorage.cloneData;
if (orderCloneData) {
this.sink.add(
this.ordersService.showApi(`orders/show/${orderCloneData.id}`, { is_web: 1,
}).subscribe((res) => {
if (!res || !res?.data) return;
res.data.pickup_location_id = this.selectedPickupAddress?.id;
const formControlPatchValueData: any =
this.addorderservice.createDataForPatchValue(res.data,
orderCloneData?.channel_order_id);
this.createOrderForm.patchValue(formControlPatchValueData);
this.selectedChips = formControlPatchValueData?.order_tag || [];
formControlPatchValueData.shipping_pincode &&
this.fetchPincodeData({ value: formControlPatchValueData.shipping_pincode + "" },
'shipping', true);
this.setBillingAsShippingAddress()
this.productPatchValuesArray(formControlPatchValueData.order_items);
if (formControlPatchValueData?.order_items.length == 1) {
const selectedProdctData = {
weight: formControlPatchValueData.weight,
dimensions: { width: formControlPatchValueData.width, breadth:
formControlPatchValueData.breadth, length: formControlPatchValueData.length }
}
if (formControlPatchValueData.is_document != 1)
this.weightFreezeCheck(formControlPatchValueData?.order_items[0].product_id,
selectedProdctData);
}
if (formControlPatchValueData.is_document == 1) {
this.setWeightDiemnsionByShippingType(this.minimumWeight, { length: 10,
breadth: 10, height: 1 });
this.removeProductFormValidationForDocumentShipping();
}
},
() => { delete localStorage.cloneData; }
)
);
}
}

/**
* Clears existing product controls and patches new values from the provided
product list into the form.
*
* **Key Steps**:
* 1. Clears any existing product controls from the form array using
`this.products.clear()`.
* 2. Iterates over the `productList` array.
* 3. For each product, creates a new form group with `createProductGroup()`.
* 4. Patches the product data into the newly created form group.
* 5. Adds the patched form group to the `products` form array (`this.products`).
*
* **Purpose**: This method ensures that the form reflects the updated product
list by replacing the existing product data with the new values.
*/
productPatchValuesArray(productList: string[]) {
this.products.clear(); // Clear existing controls
productList.forEach((product: any) => {
const productGroup = this.createProductGroup();
productGroup.patchValue(product); // Patch values into the group
this.products.push(productGroup);
});
}

//Other details
toggleOtherDetails() {
this.toggleOthrDetals = !this.toggleOthrDetals;
}

/**
* Fetches order tags from the server and stores the response data in
`orderTagsList`.
*
* **Key Steps**:
* 1. Makes an asynchronous API call to fetch order tags using
`this.addorderservice.getOrderTags()`.
* 2. Subscribes to the observable and updates the `orderTagsList` with the `data`
property from the response.
* 3. If no data is found, the `orderTagsList` is updated with an empty array.
*
* **Purpose**: This method ensures that the order tags are fetched and stored in
the `orderTagsList` for use in the application.
*/
getOrderTags() {
this.sink.add(
this.addorderservice.getOrderTags().subscribe(
(resp) => {
this.orderTagsList = resp?.data || [];
},
)
);
}

add(event: MatChipInputEvent): void {


const value = (event.value || '').trim();
// Add our Order Tag
if (value) {
this.selectedChips.push(value);
this.createOrderForm.patchValue({ order_tag: this.selectedChips, });
}
// Clear the input value
event.chipInput!.clear();
}

remove(tag: string): void {


const index = this.selectedChips.indexOf(tag);
if (index >= 0) this.selectedChips.splice(index, 1);
this.orderTagsList.push({ tag: tag, });
}

selected(event: MatAutocompleteSelectedEvent): void {


const index = this.orderTagsList.indexOf(event?.option?.value);
if (index > -1) this.orderTagsList.splice(index, 1);
this.selectedChips.push(event.option.viewValue);
this.createOrderForm.patchValue({ order_tag: this.selectedChips, });
}

/**
* Fetches available channels data and updates the form with the first channel if
only one channel is available.
* It also manages the display of social media channels based on the response data.
*
* **Purpose**: This method is responsible for fetching the available channels
data, processing it,
* and updating the order form with the appropriate channel. It also determines how
to display the channel list
* based on conditions such as the number of social media channels available and
whether they should be pushed to the list.
*
* **Flow**:
* 1. **Fetching Data**: The method calls `getChannelsData()` from the
`addorderservice` to fetch channel data.
* It expects a response containing channel data that is processed into a
structured format using `genereateChannelsDataForAddOrder()`.
*
* 2. **Processing Channel List**:
* - The `chList` contains the list of available channels, `socialMediaChCount`
indicates the number of social media channels,
* and `dontPushSocialMedia` determines if social media channels should be
pushed into the channel list for display.
* - If the list of channels (`chList`) has only one item, the form is updated
with the `channel_id` of the first channel.
* - If the list contains multiple channels, the form is updated with the
`channel_id` of the first channel in the list.
*
* 3. **Social Media Channel Display**:
* - If `dontPushSocialMedia` is `false` and there are social media channels,
the method will display up to the first three channels from the list,
* along with the last few social media channels (up to the count defined by
`socialMediaChCount`).
* - If `dontPushSocialMedia` is `true`, all channels are rendered without
adding social media channels to the display list.
*
* 4. **Updating Form and UI**:
* - The form is updated with the `channel_id` field set to the ID of the first
channel.
* - The `channelList` is populated with all available channels, and the
`renderChannelList` is populated with the channels that should be displayed.
*
* **Usage**:
* - This method is typically called when the order form is being initialized or
updated with available channels. It is used to ensure the correct channel is
selected,
* and it manages the display of social media channels accordingly.
*
* **Side Effects**:
* - Updates the `createOrderForm` with the selected `channel_id`.
* - Populates `channelList` with the full list of available channels.
* - Populates `renderChannelList` with a subset of channels, including social
media channels if applicable.
*/
getChannels() {
this.sink.add(
this.addorderservice.getChannelsData().subscribe((resp) => {
const { chList, socialMediaChCount, dontPushSocialMedia } =
this.addorderservice.genereateChannelsDataForAddOrder(resp.data);
this.socialMediaChCount = socialMediaChCount;
if (chList && chList.length) {
if (chList.length == 1) {
// if (this.customChannelList[0].name == 'CUSTOM') {
this.createOrderForm.patchValue({
channel_id: chList[0].channel_id,
});
// }
} else {
this.createOrderForm.patchValue({ channel_id: chList[0].channel_id, });
}
this.channelList = chList;
if (!dontPushSocialMedia) {
if (chList?.length && socialMediaChCount > 0) {
this.renderChannelList = [...chList.slice(0, 4)];
} else {
this.renderChannelList = chList?.length ? chList.slice(0, 4) : [];
}
} else {
this.renderChannelList = chList;
}
}
})
);
}

toggleMoreChannel() {
this.showMore = !this.showMore;
}

selectChannel(ch: any) {
this.createOrderForm.patchValue({ channel_id: ch.channel_id });
}
//End Other details

/**
* Selects a package and updates the form with the package's dimensions, and
adjusts various UI states accordingly.
*
* **Purpose**: This method is used to select a package from a list of available
packages. It updates the form with the dimensions
* (height, length, breadth) of the selected package and handles the UI states
such as managing the package settings
* and prefill dimension states.
*
* **Flow**:
* 1. **Dimension Update**: The method first extracts the dimensions (height,
length, breadth) from the `packageData` parameter. If the `packageData` contains
the `dimensions` field, it is used to update the form. Otherwise, it defaults to
the individual `height`, `length`, and `breadth` values provided.
*
* 2. **Package Selection**: After updating the form with the package's
dimensions, the `selectedPackage` is set to the `packageData` object, indicating
which package is currently selected.
*
* 3. **UI State Management**:
* - The `changeInNoDiscrepancyDimensions` flag is set to `false`, indicating
that there is no discrepancy in the dimensions.
* - The `isManagePackageOpen` flag is set to `false`, indicating that the
package management UI should be closed.
* - The method calls `managePrefillDimensionAndEditIconState(true)` to adjust
the prefill dimension settings and edit icon visibility based on the selected
package.
*
* **Usage**:
* - This method is typically used when a user selects a package during an order
creation or shipping process. It ensures that the form is updated with the correct
package dimensions and that the UI reflects the current selection.
*
* **Side Effects**:
* - Updates the form with the package's dimensions.
* - Sets the `selectedPackage` to the chosen package data.
* - Adjusts the UI state to reflect the selected package and close the package
management interface.
* - Resets the discrepancy flag (`changeInNoDiscrepancyDimensions`) to `false`
and disables the package management interface (`isManagePackageOpen`).
*/
selectPackage(packageData: any) {
const { height, length, breadth } = packageData;
this.createOrderForm.patchValue(packageData?.dimensions ?
packageData?.dimensions : { height, length, breadth });
this.selectedPackage = packageData;
this.isManagePackageOpen = false;
this.managePrefillDimensionAndEditIconState(true);
}

/**
* Calculates the volumetric weight of the product and determines the applicable
weight based on the comparison
* between the volumetric weight and the actual weight provided in the form.
*
* **Purpose**: This method calculates the volumetric weight of a product based on
its length, breadth, and height.
* It then compares this calculated volumetric weight with the actual weight of the
product. The applicable weight is
* set to the greater of the two values, which is then stored in the
`applicableweight` field.
*
* **Flow**:
* 1. **Volumetric Weight Calculation**:
* - Volumetric weight is calculated using the formula: `(length * breadth *
height) / 5000`.
* - This formula assumes the volumetric weight calculation is based on
dimensional weight standards often used in shipping and logistics (where 5000 is
the divisor for cubic centimeters to kilograms).
*
* 2. **Applicable Weight Determination**:
* - If the calculated volumetric weight is greater than the actual weight
(`weight`), the `applicableweight` is set to the volumetric weight.
* - If the actual weight is greater than the volumetric weight, the
`applicableweight` is set to the actual weight.
* - Both weights are rounded to two decimal places for consistency.
*
* 3. **Handling Missing or Invalid Dimensions**:
* - If any of the dimensions (`length`, `breadth`, `height`) are missing or
invalid (zero or undefined), the volumetric weight is set to zero, and no
comparison occurs.
*
* **Usage**:
* - This method is typically used in shipping or logistics-related applications
where volumetric weight plays a role in determining shipping costs. It's useful for
calculating dimensional weight versus actual weight to determine the most
appropriate billing weight.
*
* **Side Effects**:
* - Updates the `volumetricWeight` and `applicableweight` properties with the
calculated values.
* - Ensures that the final weight used for shipping calculations reflects either
the volumetric weight or the actual weight, whichever is higher.
*/
calculateVolumerticWeight() {
const { length, breadth, height, weight } = this.createOrderForm?.value;
const numLength = Number(length);
const numBreadth = Number(breadth);
const numHeight = Number(height);
const numWeightt = Number(weight);
if (numLength && numBreadth && numHeight) {
this.volumetricWeight = (numLength * numBreadth * numHeight) / 5000;
if (this.volumetricWeight > numWeightt) {
this.applicableweight = Number(this.volumetricWeight.toFixed(2));
} else {
this.applicableweight = Number(numWeightt.toFixed(2));
}
this.volumetricWeight = Number(this.volumetricWeight.toFixed(2));
} else {
this.volumetricWeight = 0
}
}
//End Packgae details

/**
* Calculates the total order amount, including shipping, gift wrap, transaction
charges, and product-specific discounts.
*
* **Purpose**: This method computes the total order value based on the form
data, including product details (price, discount, units), shipping, gift wrap,
transaction charges, and any total discounts. It then updates the form fields to
reflect the calculated values.
*
* **Flow**:
* 1. **Other Charges Calculation**:
* - It first calculates the total of additional charges such as
`shipping_charges`, `giftwrap_charges`, and `transaction_charges`.
* - If the total of these charges is greater than 0, it ensures that the
value is rounded to two decimal places using `toFixed(2)`. If the total is 0 or
negative, it defaults to 0.
* - This value is then saved to the `otherCharges` variable.
*
* 2. **SubTotal Calculation**:
* - The method then iterates over each product in the `products` array,
calculating the subtotal by multiplying the `selling_price` of each product by its
`units`, and subtracting the discount (if any).
* - The subtotal value is then rounded to two decimal places and stored in
the `sub_total` field of the form.
*
* 3. **Order Value Calculation**:
* - The total order value (`order_value`) is calculated by adding the
`otherCharges` to the `subTotal` and subtracting the `total_discount` value from
the form.
* - The calculated `order_value` is also rounded to two decimal places and
updated in the form.
*
* **Usage**:
* - This method is intended to be used in an order calculation context, where
charges, discounts, and product prices need to be calculated to determine the final
order value.
* - It ensures that the calculated values are properly rounded and updated in
the form controls.
*
* **Side Effects**:
* - Updates the form with the calculated `sub_total` and `order_value`.
* - Ensures that charges and discounts are properly accounted for in the total
calculation.
*/
calculateOrderAmount() {
const { shipping_charges, giftwrap_charges, transaction_charges, total_discount
} = this.createOrderForm.value;
const otherChargesTotal = Number(shipping_charges) + Number(giftwrap_charges) +
Number(transaction_charges);
this.otherCharges = otherChargesTotal > 0 ?
Number(otherChargesTotal.toFixed(2)) : 0;
let subTotal = 0.0;
for (const row of this.products.value) {
subTotal = subTotal + Number(row.selling_price) * Number(row.units) -
Number(row.discount) * Number(row.units);
}
const orValue = this.otherCharges + subTotal - Number(total_discount);
this.createOrderForm.patchValue({ sub_total: Number(subTotal.toFixed(2)) });
this.createOrderForm.patchValue({ order_value: Number(orValue.toFixed(2)) });
}

toggleOtherCharges() {
this.isOtherChargesOpen = !this.isOtherChargesOpen;
}

/**
* Changes the quantity of a product in the order form based on user interaction.
*
* **Purpose**: This method is used to increase or decrease the quantity of a
product in the order form. It handles two types of operations: adding and
subtracting the quantity, while ensuring that the form remains consistent and that
certain validations are applied after the change.
*
* **Flow**:
* 1. **Reset Default Configuration**:
* - The `resetDefaultConfig()` method is called to clear any previously set
configurations, likely related to package dimensions or other settings.
* - The `managePrefillDimensionAndEditIconState(false)` method is also invoked
to disable pre-filling and edit icon states.
*
* 2. **Get Current Quantity**:
* - The current quantity value of the product at index `i` is retrieved from
the form control using `formControlName`.
* - If no value exists (i.e., it's `null` or `undefined`), it defaults to 1.
*
* 3. **Modify the Quantity**:
* - If the `type` is 'subtract' and the value is greater than 1, it decrements
the quantity by 1.
* - If the `type` is 'add', it increments the quantity by 1, ensuring that the
value is not negative.
*
* 4. **Update the Form**:
* - The updated quantity is patched into the form control for the product at
index `i`.
* - The `weight` form control is validated again with a minimum value of 0.5,
ensuring that the weight is correctly validated after a quantity change.
*
* **Usage**:
* - This method is intended for use in product quantity adjustments (e.g.,
incrementing or decrementing the quantity for a particular product in an order).
* - It ensures that the quantity stays within valid bounds and that form
validations are re-applied after changes.
*
* **Side Effects**:
* - The method affects the product quantity and ensures that the weight field is
re-validated.
* - Resets certain configurations related to package dimensions and disables pre-
filling.
*/
changeQuantity(formControlName: string, i: number, type: string) {
this.resetDefaultConfig();
this.managePrefillDimensionAndEditIconState(false);
const value = Number(this.products.controls[i].get(formControlName)?.value);
let parsedValue = value || 1;
if (type == 'subtract' && parsedValue > 1) {
parsedValue = --parsedValue;
} else if (type == 'add' && parsedValue >= 0) {
parsedValue = ++parsedValue;
}
this.products.controls[i].patchValue({ [formControlName]: parsedValue }); //
Update the form control
this.createOrderForm.get('weight')?.setValidators([Validators.required,
minValueValidator(this.minimumWeight)]);
this.createOrderForm.get('weight')?.updateValueAndValidity();
}

changePackageQuantity(type: string) {
const countVal = Number(this.createOrderForm.value.package_count);
let count = countVal || 1;
if (type == 'add') {
count = ++count;
} else if (type == 'subs') {
count = --count;
}
this.createOrderForm.patchValue({ package_count: (count <= 0 ? 1 : count) });
}

/**
* Fetches a list of products based on the provided input string and resets form
data if necessary.
*
* **Purpose**: This method is used to fetch and display a list of products that
match the input string. It also resets form data when certain conditions are met,
ensuring that the form is cleared if a specific reset flag (`resetAutoFillData`) is
passed.
*
* **Flow**:
* 1. **Reset Data if Conditions Met**:
* - If the current product list contains only one item and the
`resetAutoFillData` flag is `true`, it resets several properties:
* - Clears the selected package and disables package management.
* - Resets form fields (`weight`, `height`, `breadth`, `length`) to `null`
and applies validation to the `weight` field with a minimum value of `0.5`.
* - Calls `resetDefaultConfig()` to clear any default configuration (likely
related to package dimensions).
*
* 2. **Validate and Search for Products**:
* - It checks if the `productName` input string has a length greater than 3
characters. If so, it updates the `lastSearchProductString` with the current input
value and emits a `userQuestionUpdate` event with the product name.
* - If the `productName` is shorter than 3 characters, it clears the
`productList`, effectively hiding any product suggestions.
*
* **Usage**:
* - This method is ideal for handling user input in a product search field where
the product list is dynamically fetched based on the typed string.
* - It ensures that the form is reset when a new product is selected or when a
condition is met (e.g., when only one product exists).
*
* **Side Effects**:
* - If the input string length is greater than 3, it triggers a request to fetch
matching products.
* - If the product name length is less than 3, it clears the existing product
list, ensuring only relevant suggestions are displayed.
* - When resetting data, it clears and updates various form fields, ensuring that
the form remains consistent with the user's actions.
*/
fetchProductList(event: any, resetAutoFillData?: boolean) {
if (this.products.length == 1 && resetAutoFillData) {
this.changePreFillDimesnsionState(false);
this.selectedPackage = undefined;
this.isManagePackageOpen = false;
this.resetDefaultConfig();
this.createOrderForm.get('weight')?.setValidators([Validators.required,
minValueValidator(this.minimumWeight)]);
//Will remove below code
//this.createOrderForm.patchValue({ weight: null, height: null, breadth:
null, length: null });
}
const productName: string = event.value;
if (productName?.length > 3) {
this.lastSearchProductString = productName;
this.userQuestionUpdate.next({ productName, type: 'product' });
} else {
this.productList = [];
}
}

onFocusOutProductField() {
this.productList = [];
}

onFocusProductField($event: any) {
if ($event?.value !== this.lastSearchProductString)
this.fetchProductList($event);
}

/**
* Updates the positioning of the autocomplete dropdown relative to a reference
element during scroll.
*
* **Purpose**: This method is used to dynamically adjust the position of an
autocomplete dropdown when the user scrolls the page or container. It ensures the
dropdown stays aligned with the reference element (e.g., an input field).
*
* **Flow**:
* 1. **Find Scrollable Container**:
* - The method begins by selecting the `.sr-container` element, which is
assumed to be the scrollable container for the autocomplete dropdown.
* - If the container is found, the method subscribes to the `scroll` event of
this container.
*
* 2. **Calculate Reference Element Position**:
* - When a scroll event is triggered, the method checks the position of the
reference element (specified by the `id` argument) relative to the viewport. This
is done using `getBoundingClientRect().top`.
*
* 3. **Adjust Popup Position**:
* - If the reference element's position (`currentTop`) is available and a
popup is visible (`.cdk-overlay-pane`), it adjusts the popup's `top` style to
ensure it is properly aligned with the reference element, considering an offset of
36px (likely to accommodate other UI elements like padding or borders).
*
* **Usage**:
* - This method is useful in scenarios where an autocomplete dropdown needs to
remain in sync with its reference input field as the user scrolls through the page
or container.
*
* **Side Effects**:
* - Dynamically changes the positioning of the `.cdk-overlay-pane` (presumably
the autocomplete dropdown) based on the scroll event and reference element's
position.
* - This can improve user experience by keeping the dropdown aligned with the
input field, even if the page or container is scrolled.
*/
updateAutoCompleteElRef(id: string) {
const scrollableContainer = document.querySelector('.sr-container');
if (scrollableContainer) {
this.autoCompleteScrollEvent = this.sink.add(fromEvent(scrollableContainer,
'scroll').subscribe(() => {
const refrenceEl: any = document.querySelector(`#${id}`);
const currentTop = refrenceEl ? refrenceEl.getBoundingClientRect().top :
undefined;
const popup: any = document.querySelector('.cdk-overlay-pane');
if (popup && currentTop) popup.style.top = (currentTop + 36) + 'px';
}));
}
}

removeAutoConpleteEventListner() {
this.autoCompleteScrollEvent && this.autoCompleteScrollEvent.unsubscribe();
}

/**
* Selects a suggested product, updates the form values, and checks for weight
freeze status.
*
* **Purpose**: This method is invoked when a suggested product is selected. It
updates the form with the selected product details, resets certain configurations,
and checks if the product has a weight freeze applied.
*
* **Flow**:
* 1. **Reset Package and Configuration**:
* - Clears any previously selected package (`this.selectedPackage` set to
`undefined`).
* - Disables the package management state (`this.isManagePackageOpen` set to
`false`).
* - Resets any default configurations by calling `this.resetDefaultConfig()`.
* - Sets validation for the `weight` field to ensure it is a valid number
greater than or equal to `0.5`.
*
* 2. **Patch Product Data**:
* - Extracts the necessary properties (`id`, `name`, `hsn`, `selling_price`,
`sku`, `category_id`, `dimensions`, `weight`) from the `product` object.
* - Renames `breadth` to `width` in the `dimensions` object to maintain
consistency.
* - Prepares a `patchValueData` object containing the product's details, which
will be patched into the selected product in the form array
(`this.products.controls[index]`).
* - Applies `patchValueData` to the appropriate product in the form array
(`this.products.controls[index]`).

* 3. **Generate Missing SKU**:


* - Calls `this.generateMissingSKU(index)` to potentially generate or fill in
the missing SKU based on the selected product's data.
*
* 4. **Weight Freeze Check**:
* - If there is only one product in the list (`this.products?.length == 1`),
it invokes `this.weightFreezeCheck(id, {...dimensions, weight})` to check if the
product has a weight freeze applied.
* - This process ensures the product’s weight and dimensions are validated or
prefilled if frozen.
*
* **Usage**:
* - This method is typically used when selecting a product from a list of
suggested products, automatically filling in the form with relevant product details
and handling edge cases like weight freeze validation.
*
* **Side Effects**:
* - Modifies the `selectedPackage` and `isManagePackageOpen` states, affecting
the UI and interaction options for the user.
* - Updates the form with product details, including SKU and other properties.
* - Checks for a weight freeze and updates the form with the appropriate data if
applicable.
*/
selectSuggestedProduct(product: any, index: number) {
this.selectedPackage = undefined;
this.isManagePackageOpen = false;
this.resetDefaultConfig();
this.createOrderForm.get('weight')?.setValidators([Validators.required,
minValueValidator(this.minimumWeight)]);
const { id, name, hsn, selling_price, sku, category_id, dimensions, weight } =
product;
dimensions.breadth = dimensions.width;
const patchValueData = {
product_id: id,
name: name ? name : '',
hsn: hsn ? hsn : '',
category_id: category_id ? category_id : '',
selling_price: selling_price ? selling_price : '',
sku: sku ? sku : ''
}
this.products.controls[index].patchValue(patchValueData);
this.generateMissingSKU(index);
if (this.products?.length == 1) this.weightFreezeCheck(id, { ...dimensions,
weight });
}

/**
* Checks the weight freeze status for a given product ID and updates the form with
frozen dimensions and weight insights.
*
* **Purpose**: This method makes an API call to check if the weight and dimensions
of a product have been frozen. If the product is subject to a weight freeze, it
updates the form with the frozen weight and dimensions, along with any associated
insights. If the product is not frozen, it retrieves package details and updates
weight insights.
*
* **Flow**:
* 1. The method calls an API (`addorderservice.weightFreeze(id)`) to get the
freeze status and product dimensions for the given product ID.
* 2. If the `freeze_status` is `1` or `6`, indicating that the product has frozen
weight and dimensions:
* - It enables pre-filling of dimensions and editing icons
(`managePrefillDimensionAndEditIconState(true)`).
* - It sets the `weightFreezeData` to the response dimensions and weight.
* - It updates the form with the frozen dimensions and weight, including
insights.
* - It adds form validators to ensure that the weight field meets the frozen
weight condition.
* 3. If the `freeze_status` is not `1` or `6` (i.e., the product is not frozen):
* - It checks for any available insights and stores them in `weightInsights`.
* - If there are products in the form, it fetches the package details for the
provided product list and catalog dimension.
*
* **Usage**:
* - This method is used when checking if a product's weight and dimensions have
been frozen, typically when managing product packages and dimensions.
*
* **Side Effects**:
* - Updates the form values for `weight`, `length`, `breadth`, `height`, and
`insights` based on the response from the API.
* - Applies validation to the weight field to match the frozen weight if
applicable.
* - Modifies the `weightFreezeData` or `weightInsights` properties depending on
the freeze status.
*/
weightFreezeCheck(id: number, catalogDimension: any) {
this.sink.add(
this.addorderservice.weightFreeze(id).subscribe((resp: any) => {
const weightFreezeResponse = resp.response;
if (weightFreezeResponse?.freeze_status == 1 ||
weightFreezeResponse?.freeze_status == 6) {
this.managePrefillDimensionAndEditIconState(true);
this.weightFreezeData = { ...weightFreezeResponse.dimensions, weight:
Number(weightFreezeResponse.weight), insights: weightFreezeResponse.insights };
this.createOrderForm.patchValue({
weight: this.weightFreezeData.weight || 0.0,
length: this.weightFreezeData.length || 0.0,
breadth: this.weightFreezeData.width || 0.0,
height: this.weightFreezeData.height || 0.0,
insights: resp?.response?.insights,
});
} else {
if (weightFreezeResponse?.insights) {
this.weightInsights = {
...weightFreezeResponse.insights
}
}
this.products?.value?.length ?
this.getPackageDetails(this.products.value, catalogDimension, false) : null;
}
})
);
}

weightPredict(searchId: any) {
this.sink.add(
this.addorderservice.weightPredict(searchId).subscribe((resp) => {
if (resp && resp.success && resp.data) {
}
})
);
}

/**
* Checks the availability of managing the package based on the form's dimension
values and weight insights.
*
* This method evaluates whether the option to manage the package should be open or
not based on the dimensions (length, breadth, height) provided in the order form
and any existing weight-related insights.
* It updates the state of `isManagePackageOpen` to control the visibility of
package management options in the UI.
*
* **Flow**:
* 1. It checks whether weight insights exist. If not, the
`changeInNoDiscrepancyDimensions` flag is set to `true`.
* 2. It extracts and converts the length, breadth, and height values from the form
into numbers.
* 3. If the form dimensions are greater than 0.5 (excluding weight freeze data),
the package management option is enabled (`isManagePackageOpen = true`).
* 4. Otherwise, the package management option is disabled (`isManagePackageOpen =
false`).
*
* **Usage**:
* - This method is used to determine whether the package management option should
be visible based on the provided dimensions and weight insights in the order form.
*
* **Side Effects**:
* - Updates the `isManagePackageOpen` property to control the visibility of
package management functionality.
* - Modifies the `changeInNoDiscrepancyDimensions` flag based on the presence of
weight insights.
*/
checkForManagePackageAvailability() {
const { length, breadth, height } = this.createOrderForm?.value;
const numLength = Number(length);
const numBreadth = Number(breadth);
const numHeight = Number(height);
if (!this.weightFreezeData && numLength > 0.5 && numBreadth > 0.5 && numHeight
> 0.5) {
this.isManagePackageOpen = true;
} else {
this.isManagePackageOpen = false;
}
}

/**
* Fetches package details based on the provided product list and updates the
package data and form accordingly.
*
* This method retrieves the list of available packages based on the products in
the order and processes them
* to categorize the packages into saved or approved packages. It updates the form
with the correct package dimensions
* based on the response or defaults to catalog dimensions if no valid packages are
found.
*
* **Flow**:
* 1. The method extracts product IDs from the provided product list and makes a
request to fetch available packages.
* 2. If packages are found:
* - The packages are classified into saved (status 5) and approved packages.
* - Depending on whether it’s a saved package call or not, the package data is
either directly set or passed to the `setPackageData` method to update the form.
* 3. If no packages are found or there’s an error:
* - The dimension pre-fill and edit icon state are reset.
* - The form is updated with catalog dimensions if available.
*
* **Usage**:
* - This method is called when fetching package data for a list of products, and
it updates the package-related form values based on the available packages.
*
* **Parameters**:
* - `productList`: A list of products in the order.
* - `catalogDimension`: Default dimensions used when no valid packages are found.
* - `isSavedPackageCall`: A flag to determine if the call is for saved packages.
*
* **Side Effects**:
* - Updates the `savedPackageList`, `approvedPackageList`, and `packageList`
properties.
* - Modifies the form values (`createOrderForm`) to reflect package dimensions.
* - Controls UI states such as dimension pre-fill and edit icon visibility.
*/
getPackageDetails(productList: any, catalogDimension: any, isSavedPackageCall:
boolean) {
const productIdList: Array<any> = [];
productList.map((product: any) => product.product_id ?
productIdList.push(product.product_id) : null);
this.sink.add(
this.addorderservice.getPackagelistData(productIdList, true).subscribe(
(resp) => {
if (resp?.data?.length) {
this.savedPackageList = [];
this.approvedPackageList = [];
resp?.data.map((pckg) => {
if (pckg.status == 5) {
this.savedPackageList.push(pckg);
} else {
this.approvedPackageList.push(pckg);
}
});
isSavedPackageCall ? this.packageList = resp?.data || [] :
this.setPackageData(resp.data, catalogDimension);
} else {
this.managePrefillDimensionAndEditIconState(false);
(this.products.length == 1 && catalogDimension) ?
this.createOrderForm.patchValue(catalogDimension) : null;
}
},
(error) => {
this.managePrefillDimensionAndEditIconState(false);
(this.products?.length == 1 && catalogDimension) ?
this.createOrderForm.patchValue(catalogDimension) : null;
}
)
);
}

/**
* Sets the package data and updates the form with package dimensions based on the
provided data.
*
* **Flow**:
* 1. If there's one approved package and one product, it pre-fills the package
dimensions and enables the edit icon.
* 2. If multiple approved packages exist, it resets the dimensions and enables the
edit icon for manual selection.
* 3. If no approved packages are found, it sets the form dimensions to the catalog
dimensions (if available).
*
* **Usage**:
* - Used after fetching package data to update the form with dimensions.
*
* **Parameters**:
* - `data`: List of available packages.
* - `catalogDimension`: Default dimensions when no approved packages are found.
*
* **Side Effects**:
* - Updates the form values and UI states for package dimensions.
*/
setPackageData(data: any, catalogDimension: any) {
this.packageList = data || [];
const approvedPackageList =
this.addorderservice.filterApprovedPackages(this.packageList);
if (approvedPackageList?.length == 1 && this.products?.length == 1) {
this.managePrefillDimensionAndEditIconState(true);
this.selectedPackage = approvedPackageList[0];
this.createOrderForm.patchValue({ ...approvedPackageList[0].dimensions });
} else if (approvedPackageList?.length > 1) {
this.createOrderForm.patchValue({ length: null, width: null, breadth: null,
weight: null });
this.changePreFillDimesnsionState(false);
this.changeEditIconState(true);
this.selectedPackage = undefined;
} else {
this.managePrefillDimensionAndEditIconState(false);
this.selectedPackage = undefined;
(this.products.length == 1 && catalogDimension) ?
this.createOrderForm.patchValue(catalogDimension) : null;
}
}

/**
* Getter for accessing the 'order_items' FormArray from the createOrderForm.
*
* Provides access to the dynamic list of order items (products) in the form,
allowing operations like add, remove, or update individual products.
*
* **Returns**: A `FormArray` of `order_items`.
*/
get products(): FormArray {
return this.createOrderForm.get('order_items') as FormArray;
}

/**
* Adds a new product to the order form and adjusts configurations based on the
shipping type.
*
* Adds a product entry to the form, handles specific validation for document
shipments, and resets prefilled data for non-document shipments.
*
* **Key Steps**:
* - Adds a new product to the `products` form array.
* - Removes product validation for document shipments.
* - Resets prefilled data for non-document shipments when multiple products are
added.
*/
addProduct() {
this.products.push(this.createProductGroup());
if (this.createOrderForm.value.is_document == 1)
this.removeProductFormValidationForDocumentShipping();
if (this.products?.length > 1 && this.createOrderForm.value.is_document != 1)
this.resetPrefilledData();
}
/**
* Removes product form validation and resets values for document shipping
scenarios.
*
* Clears product-specific validations and resets fields (e.g., price, SKU,
category) for document shipments, where such details are not required.
*
* **Key Steps**:
* - Loops through all products in the form.
* - Removes validation and resets `selling_price` to `0`.
* - Generates and clears `sku` value.
* - Resets additional fields like `category_name`, `discount`, `hsn`, and `tax`
to default values.
*/
removeProductFormValidationForDocumentShipping() {
for (const product of this.products.controls) {
product.get('selling_price')?.setValidators([]);
product.get('selling_price')?.patchValue(0)
product.get('selling_price')?.updateValueAndValidity();
product.get('sku')?.setValidators([]);
product.get('sku')?.patchValue(this.addorderservice.createsku())
product.get('sku')?.patchValue('')
product.get('sku')?.updateValueAndValidity();
product.get('category_name')?.patchValue('')
product.get('discount')?.patchValue('')
product.get('hsn')?.patchValue('')
product.get('tax')?.patchValue(0)
}
}

/**
* Resets pre-filled data and reverts the form to its default state.
*
* Clears pre-filled data for dimensions and weight, unselects the package, resets
configurations,
* and restores validation rules to their default state.
*
* **Key Steps**:
* - Resets `length`, `breadth`, `height`, and `weight` fields to `null`.
* - Unselects the `selectedPackage` and clears related configurations.
* - Calls `resetDefaultConfig()` to clear weight and dimension flags.
* - Updates UI elements by calling
`managePrefillDimensionAndEditIconState(false)`.
* - Restores the weight validation rule with a minimum value of `0.5`.
*/
resetPrefilledData() {
this.createOrderForm.patchValue({ length: null, breadth: null, height: null,
weight: null });
this.selectedPackage = undefined;
this.resetDefaultConfig();
this.managePrefillDimensionAndEditIconState(false);
this.createOrderForm.get('weight')?.setValidators([Validators.required,
minValueValidator(0.1)]);
this.createOrderForm.get('weight')?.updateValueAndValidity();
}

/**
* Resets the default configuration values related to weight and dimensions.
*
* Clears properties used for managing weight, dimensions, and discrepancies,
* restoring them to their default initial state.
*
* **Key Steps**:
* - Resets `weightFreezeData` and `weightInsights` to `undefined`.
* - Sets `changeInNoDiscrepancyDimensions` flag to `false`.
*/
resetDefaultConfig() {
this.weightFreezeData = undefined;
this.weightInsights = undefined;
}

/**
* Removes a product from the product list at the specified index.
*
* Checks if there are more than one product in the `products` array before
removing the product at the given index.
* Ensures at least one product remains in the list.
*
* **Key Steps**:
* - Verifies if the `products` array has more than one product.
* - If true, removes the product at the given index using `removeAt()`.
*
* **Parameters**:
* - `index`: The index of the product to be removed from the `products` array.
*/
removeProduct(index: number): void {
this.products.length > 1 && this.products.removeAt(index);
}

/**
* Fetches product details based on the provided product name.
*
* Makes an API call to retrieve a list of products matching the specified product
name and stores the result in the `productList` array.
*
* **Key Steps**:
* - Calls `getProductDetails()` to fetch products based on `productName`.
* - Stores the fetched product data in the `productList` array, or assigns an
empty array if no data is found.
*
* **Parameters**:
* - `productName`: The name of the product to search for in the system.
*
* **Return**:
* - This method does not return any value. It updates the `productList` with the
fetched product details.
*/
fetchProductsByName(productName: string): void {

this.sink.add(this.addorderservice.getProductDetails(productName).subscribe((data)
=> { this.productList = data || []; }));
}

/**
* Updates the billing details based on the shipping address, if applicable.
*
* This method checks if the shipping address is being used as the billing
address (i.e., `shipping_is_billing` is true).
* If true, it updates the corresponding billing field in the form with the
provided value.
*
* **Key Steps**:
* - Checks if the `shipping_is_billing` flag is true. If false, it exits early.
* - If true, updates the `controlName` (billing field) in the `createOrderForm`
with `target.value`.
*
* **Parameters**:
* - `controlName`: The name of the billing field in the form to update (e.g.,
`billing_address`, `billing_phone`).
* - `target`: The event target whose value will update the form control.
*
* **Return**:
* - This method does not return any value. It updates the billing address field
in the form.
*/
updateBillingDetails(controlName: string, target: any): void {
if (!this.createOrderForm.value.shipping_is_billing) return;
this.createOrderForm.patchValue({ [controlName]: target.value });
}

/**
* Fetches the list of recent pickup addresses and updates the form with the
selected address.
*
* This method retrieves the most recent pickup addresses from the API, filters
out inactive addresses,
* and updates the form with the `pickup_location_id` of the most recent valid
address.
*
* **Key Steps:**
* - Makes an API call using `getRecentPickUpAddress()` to retrieve recent and
shipping addresses.
* - Filters out addresses with a `status` of 0 (inactive).
* - Adds filtered recent addresses to the `pickUpAddress` list and selects the
first valid address.
* - Updates the form with the selected pickup address's `pickup_location_id`.
*
* **Return**:
* - This method does not return any value. It updates the `pickUpAddress` list,
selects the address,
* and modifies the `createOrderForm` with the selected address.
*/
getPickupLocation() {
this.sink.add(
this.addorderservice.getRecentPickUpAddress().subscribe(
(resp: any) => {
const recentAddes = resp.data['recent_addresses'];
const recentAddresses = recentAddes.filter((value: any) => value.status !
= 0);
const datause1 = resp.data['shipping_address'];
this.pickUpAddress = datause1.filter((value: any) => value.status != 0);
(recentAddresses?.length) && this.pickUpAddress.push(...recentAddes);
this.selectedPickupAddress = recentAddresses.length ?
recentAddresses[0] : this.pickUpAddress.length ? this.pickUpAddress[0] : '';
if (this.selectedPickupAddress)
this.createOrderForm.patchValue({ pickup_location_id:
this.selectedPickupAddress.id, });
},
)
);
}

fetchStateList(params: any) {
this.sink.add(this.addorderservice.getStatesData(params).subscribe((resp) =>
{ if (resp?.data) this.stateList = resp.data || []; }));
}

/**
* Handles the selection of a shipping address and updates the order form with the
selected address's details.
*
* This method checks the validity of the selected address and updates the
`pickup_location_id` field in the order form.
*
* **Key Steps:**
* - If the address is valid, it updates the form with the `pickup_location_id` of
the selected address.
*
* **Parameters**:
* - `address`: The selected address (either an object or string).
*
* **Return**:
* - This method does not return any value. It updates the form state with the
selected address.
*/
onSelectAddress(address: IShippingAddress | string): void {
this.selectedPickupAddress = !address || typeof address === 'string' ? null :
address;
if (this.selectedPickupAddress)
this.createOrderForm.patchValue({ pickup_location_id: this.selectedPickupAddress.id
});
}

/**
* Opens a popup dialog to verify an address and triggers the pickup location
update upon dialog closure.
*
* This method opens a verification dialog for the provided address, allowing the
user to confirm or modify the details.
* After the dialog is closed, it triggers the `getPickupLocation()` method to
update the pickup location data.
*
* **Key Steps:**
* - Cleans up the phone number (`item.phone`) by removing newline characters.
* - Opens the `VerifyAddressComponent` dialog with the address data.
* - Calls `getPickupLocation()` after dialog closure to refresh the pickup
location details.
*
* **Parameters**:
* - `item`: The address object to be verified, passed to the dialog component.
*
* **Return**:
* - This method does not return any value. It handles dialog opening and triggers
actions after closure.
*/
verifyAddressPopup(item: any) {
item['phone'] = item['phone'].replace('\r\n', '');
const dialogRef = this.dialog.open(VerifyAddressComponent, {
data: { item: item },
});

this.sink.add(dialogRef.afterClosed().subscribe((result: any) => {


this.getPickupLocation();
}));
}

/**
* Sets weight and dimension validators for the order form based on the shipping
type.
*
* This method updates the form control validators for weight, length, breadth, and
height based on the provided weight and dimensions.
* It ensures that the fields are validated with a required validator and a custom
minimum value validator.
*
* **Key Steps:**
* - Applies `Validators.required` and a custom `minValueValidator` for weight and
each dimension (length, breadth, height).
* - Calls `updateValueAndValidity()` to re-validate the form controls after
applying the validators.
*
* **Parameters**:
* - `weight`: The minimum weight value for validation.
* - `dimensions`: Object containing `length`, `breadth`, and `height` values for
validation.
*
* **Return**:
* - This method does not return a value. It modifies the form control validation
rules.
*/
setWeightDiemnsionByShippingType(weight: number, dimensions: { length: number,
breadth: number, height: number }) {
this.createOrderForm.get('weight')?.setValidators([Validators.required,
minValueValidator(weight)]);
this.createOrderForm.get('weight')?.updateValueAndValidity();

this.createOrderForm.get('length')?.setValidators([Validators.required,
minValueValidator(dimensions.length)]);
this.createOrderForm.get('length')?.updateValueAndValidity();

this.createOrderForm.get('breadth')?.setValidators([Validators.required,
minValueValidator(dimensions.breadth)]);
this.createOrderForm.get('breadth')?.updateValueAndValidity();

this.createOrderForm.get('height')?.setValidators([Validators.required,
minValueValidator(dimensions.height)]);
this.createOrderForm.get('height')?.updateValueAndValidity();
}

/**
* Updates the shipping type and adjusts the form values, product list, and
shipping details accordingly.
*
* This method is triggered when the shipping type (specifically related to
document shipping) is updated. It handles the different
* configurations for document and non-document shipping types, updating form
values and product-related data accordingly.
*
* **Key Steps:**
* - For document shipping (`is_document == 1`), it sets specific dimensions and
weight, resets the product list, and configures payment as 'prepaid'.
* - For non-document shipping (`is_document != 1`), it resets the weight and
dimensions, and reinitializes the product list with a fresh product group.
*
* **Side Effects:**
* - Modifies the `createOrderForm` to update dimensions, weight, payment method,
and product list.
* - Resets or updates various configurations based on shipping type.
*
* **Parameters**:
* - None
*
* **Return**:
* - This method does not return a value. It modifies the form and product list in-
place.
*/
updateShippingType() {
if (this.createOrderForm.value.is_document == 1) {
this.setWeightDiemnsionByShippingType(0.1, { length: 10, breadth: 10, height:
1 });
this.createOrderForm.patchValue({ payment_method: 'prepaid', length: null,
breadth: null, height: null, weight: null });
this.selectedPackage = undefined;
this.resetDefaultConfig();
this.managePrefillDimensionAndEditIconState(false);
this.products.clear();
this.products.push(this.createProductGroup());
this.removeProductFormValidationForDocumentShipping();
} else {
this.setWeightDiemnsionByShippingType(0.1, { length: 0.00, breadth: 0.00,
height: 0.00 });
this.products.clear();
this.products.push(this.createProductGroup());
}
}

/**
* Fetches the postcode details based on the entered pincode and updates the
shipping and billing address fields accordingly.
*
* This method is triggered when a pincode is entered or updated. It makes an API
call to fetch the corresponding postcode details
* and updates the relevant address fields (shipping and/or billing) using the
`setShippingBillingAddress` method.
*
* **Parameters:**
* - `event`: The event triggered by input changes, containing the pincode value.
* - `addType`: A string representing the type of address to update (either
"shipping" or "billing").
* - `setBillingDetails` (optional): A boolean flag to determine if billing address
fields should be updated as well.
* - If `true`, the billing address will also be updated.
* - Defaults to `false` if not provided.
*
* **Key Steps:**
* 1. **Pincode Validation**: Checks if the entered pincode is at least 6
characters long before proceeding with the API call.
* 2. **API Call**: If valid, the method fetches postcode details using an API
call.
* 3. **Updating Address**: On success, the `setShippingBillingAddress` method is
called to update the address fields.
* 4. **Error Handling**: Displays an error message if the API call fails or
returns invalid data.
*
* **Side Effects:**
* - Updates the `createOrderForm` with the fetched address details (city, state)
for either the shipping or billing address.
* - Shows an error message if the API call fails or returns invalid data.
*/
fetchPincodeData(event: any, addType: string, setBillingDetails?: boolean): void
{
const pincode = event.value;
if (pincode?.length < 6 || !this.createOrderForm.get(`$
{addType}_pincode`)?.valid) return;
this.sink.add(
this.addorderservice.getPostCode(pincode).subscribe(
(resp: any) => {
if (resp?.postcode_details)
this.setShippingBillingAddress(resp.postcode_details, addType, setBillingDetails);
},
(err) => {
this.toastr.error(err?.error == undefined ? err?.message :
err?.error?.message);
this.createOrderForm.patchValue({
shipping_pincode: '',
shipping_city: '',
shipping_state: '',
billing_pincode: '',
billing_city: '',
billing_state: ''
})
}
)
);
}

/**
* Sets the shipping and billing address details in the order form based on the
provided postcode details.
*
* This method updates the `createOrderForm` with the city and state details from
the postcode data and optionally updates the billing address if needed.
*
* **Parameters:**
* - `postcodeDetails`: Object containing the city and state info.
* - `addType`: Address type to update ("shipping" or "billing").
* - `setBillingDetails`: Optional flag to also update the billing address.
*
* **Key Steps:**
* 1. Updates the city and state for the specified address type.
* 2. Optionally updates the billing address if `setBillingDetails` is `true` or if
shipping and billing are the same.
*/
setShippingBillingAddress(postcodeDetails: any, addType: string,
setBillingDetails?: boolean): void {
const city = postcodeDetails.city;
const formControlCity = `${addType}_city`;
this.createOrderForm.patchValue({ [formControlCity]: city, });
(setBillingDetails || this.createOrderForm.value.shipping_is_billing) ?
this.createOrderForm.patchValue({ 'billing_city': city, }) : null;
const shipState1 = this.stateList.filter((state: any) => { return state.code
=== postcodeDetails.state_code; })[0];
if (shipState1) {
const formControlState = `${addType}_state`;
this.createOrderForm.patchValue({ [formControlState]: shipState1.name });
(setBillingDetails || this.createOrderForm.value.shipping_is_billing) ?
this.createOrderForm.patchValue({ 'billing_state': shipState1.name, }) : null;
}
}

/**
* Handles input changes and sanitizes the input value to allow only numeric
characters.
*
* This method is triggered whenever the user types in an input field. It cleans
the input value by removing any
* non-numeric characters, ensuring that only digits are allowed in the field.
*
* **Parameters:**
* - `event`: The input change event, which contains the target input element.
*
* **Key Steps:**
* 1. **Extract Input Value**: The method accesses the `value` property of the
input element from the event.
* 2. **Sanitize the Input**: It replaces any non-numeric characters in the input
value using a regular expression (`/[^0-9]/g`).
*
* This approach ensures that the input field accepts only numeric values (0-9),
which can be useful for fields like phone numbers, zip codes, etc.
*/
onInputChange(event: Event) {
const input = event.target as HTMLInputElement;
if (input) input.value = input.value.replace(/[^0-9]/g, ''); // Allow only
numbers
}

/**
* Fetches customer details based on the provided mobile number and updates the
`customerDetailList`.
*
* This method is triggered when a mobile number is entered (or changed). It
validates the length of the number,
* and if it's a valid number, it sends a request to the backend service to fetch
the corresponding customer details.
* The result is stored in `customerDetailList` for further use, such as displaying
it in a dropdown or form.
*
* **Parameters:**
* - `event`: The event object, which contains the value of the mobile number input
field.
*
* **Key Steps:**
* 1. **Validate Mobile Number**: Checks if the provided mobile number is valid
(i.e., it should be at least 10 digits long).
* 2. **Fetch Customer Details**: If the mobile number is valid, it sends a request
to `getCustomerDetails()` method of the `addorderservice` service to fetch customer
details from the backend.
* 3. **Update Customer List**: If the request is successful, the
`customerDetailList` is populated with the data received from the response.
*
* **Edge Case Handling**:
* - If the mobile number is less than 10 digits or empty, it clears the customer
details list.
*/
fetchCustomDetailsByMobileNumber(event: any) {
const mobileNumber = event?.value;
if (!mobileNumber || mobileNumber.length < 10) {
this.customerDetailList = [];
return;
}

this.sink.add(this.addorderservice.getCustomerDetails(mobileNumber).subscribe((res:
any) => {
this.customerDetailList = res?.data || [];
})
);
}

/**
* Populates the shipping and billing details in the createOrderForm based on the
selected customer detail.
*
* This method sets the form fields for both shipping and billing addresses based
on the provided `customDetail` object.
* It also checks the value of `is_document` in the form to determine whether to
copy the billing information from the shipping details.
*
* **Parameters:**
* - `customDetail`: An object containing the customer details (e.g., mobile, name,
pincode, address, etc.).
*
* **Key Steps:**
* 1. **Populate Shipping Details**: The form is patched with values from
`customDetail` (mobile, name, pincode, address, email, alternate phone).
* 2. **Fetch Pincode Data**: If a valid pincode is provided in `customDetail`, the
method triggers the `fetchPincodeData()` method to retrieve additional details for
that pincode.
* 3. **Copy Billing Details (Conditionally)**: If `is_document` is not `0`, the
billing details are not copied from the shipping details. Otherwise, the billing
details are patched with the same values as the shipping details.
*/
selectSuggestedCustomerDetail(customDetail: any) {
this.createOrderForm.patchValue({
shipping_phone: customDetail.mobile,
shipping_customer_name: customDetail.fname,
shipping_pincode: customDetail.pincode ? customDetail.pincode : '',
shipping_address: customDetail.address,
shipping_email: customDetail.email,
billing_alternate_phone: customDetail.alternate_phone,
});
customDetail.pincode && this.fetchPincodeData({ value: customDetail.pincode +
"" }, 'shipping', true);
if (this.createOrderForm.value.is_document != 0) return;
this.createOrderForm.patchValue({
billing_phone: customDetail.mobile,
billing_customer_name: customDetail.fname,
billing_pincode: customDetail.pincode ? customDetail.pincode : '',
billing_address: customDetail.address,
});
}

/**
* Sanitizes input for numeric values, allowing only numbers and one decimal
point with optional decimal precision.
*
* Ensures input contains only digits and one decimal point, with an optional
limit on decimal places.
* Replaces a standalone decimal with "0.".
*/
sanitizeGenericNumberInput(event: any, formControlName: string, index: any,
decimalLimit?: number): void {
const input = event.target as HTMLInputElement;
input.value = input.value.replace(/[^0-9.]/g, ''); // Allow only numbers and
decimal point
input.value = input.value.replace(/(\..*?)\..*/g, '$1'); // Allow only one
decimal point
const regex = new RegExp(`^(\\d+\\.?\\d{0,${decimalLimit ? decimalLimit :
2}}).*`);
input.value = input.value.replace(regex, '$1');
if (input.value === '.') {
input.value = '0.'; // Replace standalone decimal
}
this.updateSanitizedValue(index, formControlName, input);; // Update the form
control
}

/**
* Sanitizes input for quantity fields by removing non-numeric characters.
*
* Strips out non-numeric characters and updates the form control with the
sanitized value.
*/
sanitizeQuantityInput(event: any, formControlName: string, index: any): void {
const input = event.target as HTMLInputElement;
input.value = input.value.replace(/[^0-9]/g, '');
this.updateSanitizedValue(index, formControlName, input); // Update the form
control
}

/**
* Checks if the input field is empty or contains a value of '0', and adjusts the
value accordingly.
*
* This method is designed to handle changes to specific form controls, such as
"units". It ensures that the input value is sanitized and valid by performing the
following actions:
* - If the `formControlName` is 'units', it resets the default configuration and
adjusts dimensions accordingly.
* - If the input value is '0', it changes it to '1' to ensure a valid value.
* - If the input is empty, it sets the value to '0' to prevent empty values.
* - Updates the form control with the sanitized input value.
*
* **Parameters:**
* - `$event`: The event object representing the input change.
* - `formControlName`: The name of the form control being updated (e.g., 'units').
* - `index`: The index of the product in the `products` array (optional) to
identify which product form to update.
*
* **Key Steps:**
* - Resets configurations and edits icon states when the 'units' field is updated.
* - Sanitizes the input by ensuring it is never empty or zero, converting '0' to
'1' and empty values to '0'.
* - Updates the form control with the sanitized value using the
`updateSanitizedValue()` method.
*/
checkEmptyField($event: any, formControlName: string, index: any): void {
const input = $event.target as HTMLInputElement;
if (formControlName == 'units') {
this.resetDefaultConfig();
this.managePrefillDimensionAndEditIconState(false);
if (Number(input.value) == 0) input.value = 1 + "";
}
if (!input.value) input.value = 0 + ''; // Remove any non-numeric characters
this.updateSanitizedValue(index, formControlName, input); // Update the form
control
}

/**
* Updates a form control value in the main order form or a specific product form.
*/
updateSanitizedValue(index: number, formControlName: string, input: any): void {
(index == undefined) ? this.createOrderForm.patchValue({ [formControlName]:
input.value }) :
this.products.controls[index].patchValue({ [formControlName]:
input.value, });
}
// End Delivery Details

// Pickup address

editPickUpAddress(selectedPickupAddress?: any): void {


selectedPickupAddress ? this.openPickupLocationDialog(selectedPickupAddress) :
undefined;
}

/**
* Opens a dialog to select or edit a pickup address and refreshes the location
upon closure.
*/
openPickupLocationDialog(item?: any): void {
const dialogRef = this.dialog.open(PickupDialogComponent, {
id: 'pickupDialog',
width: '80%',
height: '100%',
panelClass: ['pickUpAnimated', 'pickUpSlideInRight'],
data: { pickupaddresses: this.pickUpAddress, edit: item, },
position: { right: '0px' },
});
this.sink.add(dialogRef.afterOpened().subscribe((result: any) => {
dialogRef.removePanelClass('pickUpSlideInRight');
}));

this.sink.add(dialogRef.beforeClosed().subscribe((result: any) => {


dialogRef.addPanelClass('pickUpSlideOutRight');
}));

this.sink.add(dialogRef.afterClosed().subscribe((result: any) => {


if (result != false) {
this.getPickupLocation();
}
}));
}
// End Pickup address

/**
* Marks the billing address based on the 'shipping_is_billing' field value.
*/
markBillingDetails(): void {
const isBillingSameAddress =
this.createOrderForm.controls['shipping_is_billing'].value;
isBillingSameAddress ? this.setBillingAsShippingAddress() :
this.clearBillingAddress();
}

clearBillingAddress() {
const billingAddress = {
billing_phone: null,
billing_customer_name: null,
billing_address: null,
billing_pincode: null,
billing_city: null,
billing_state: null,
billing_address_2: null,
};
this.createOrderForm.patchValue(billingAddress);
}

setBillingAsShippingAddress() {
const {
shipping_phone,
shipping_customer_name,
shipping_address,
shipping_pincode,
shipping_city,
shipping_state,
shipping_address_2,
} = this.createOrderForm.value;

const billingAddress = {
billing_phone: shipping_phone,
billing_customer_name: shipping_customer_name,
billing_address: shipping_address,
billing_address_2: shipping_address_2,
billing_pincode: shipping_pincode,
billing_city: shipping_city,
billing_state: shipping_state,
};
this.createOrderForm.patchValue(billingAddress);
}

/**
* Validates the order form, checks certain conditions, and creates the order based
on the provided type ('shipNow' or 'manual').
*
* This method performs the following actions:
* 1. Marks all form fields as touched to trigger validation.
* 2. If the form status is 'INVALID' or the phone number is not verified
(`selectedPickupAddress?.phone_verified == 0`), it exits early without proceeding.
* 3. Retrieves the order data (`payload`) from the form and checks if the
`sub_total` exceeds 50 lakhs (5,000,000). If it does, it shows an error message and
exits.
* 4. If the conditions are met, it creates the payload for the order using the
`addorderservice.creatingPayloadDataForCreateOrder` method.
* 5. If the `type` is `'shipNow'`, it fetches the courier list by calling the
`fetchCourierList` method with the prepared payload and call shipnow.
* 6. If the `type` is `'manual'`, it creates the manual order by calling the
`createManualOrder` method with the payload.
*
* @param type - The type of order ('shipNow' or 'manual') to determine whether to
fetch courier list or create a manual order.
*/
postCreateOrder(type: string) {
this.checkMissingSKU();
this.createOrderForm.markAllAsTouched();
if (this.createOrderForm.status == 'INVALID') {
this.scrollToFirstError();
return;
}
if (this.selectedPickupAddress?.phone_verified == 0) {
this.toastr.error('Please select verifed pickup address');
return;
}
let payload = this.createOrderForm.value
if(this.weightFreezeData?.weight > this.applicableweight){
this.toastr.error('Applicable weight cannot be less than weight-free weight.
');
return;
}
if (payload?.sub_total > 5000000) {
this.toastr.error('Order Amount should be less than 50L');
return;
}
payload = this.addorderservice.creatingPayloadDataForCreateOrder(payload,
this.customFormControlFields);
if (type == 'shipNow') {
this.fetchCourierList(payload);
} else {
this.createManualOrder(payload);
}
}

checkMissingSKU() {
for (let i = 0; i < this.products.length; i++) {
if (this.products.at(i) && this.products.at(i).value.sku === '' ||
this.products.at(i).value.sku === undefined) {
this.products.controls[i].patchValue({ sku:
this.addorderservice.createsku() });
}
}
}

scrollToFirstError() {
try {
const invalidControl = Object.keys(this.createOrderForm.controls).find((key)
=> {
const control = this.createOrderForm.get(key);
return control?.invalid;
});
if (invalidControl) {
if (invalidControl == 'order_id' || invalidControl == 'order_date') {
this.toggleOthrDetals = true;
}
// Find the DOM element associated with the invalid control
if (invalidControl == "order_items") {
let productInvalidControl;
this.products.controls.map((key: any, index: number) => {
productInvalidControl = Object.keys(key.controls).find((nkey) => {
const control = key.get(nkey);
return control?.invalid;
});
productInvalidControl = `${productInvalidControl}_${index}`;
return productInvalidControl
});
productInvalidControl &&
this.findInvalidElAndScroll(productInvalidControl);
} else {
this.findInvalidElAndScroll(invalidControl);
}
}
} catch (er) {
console.log(er)
}
}

findInvalidElAndScroll(invalidControl: string) {
this.timeoutId && clearTimeout(this.timeoutId);
this.timeoutId = setTimeout(() => {
const controlElement: any = document.getElementById(invalidControl);
if (controlElement) {
controlElement.scrollIntoView({ behavior: 'smooth', block: 'center' });
controlElement.focus();
}
}, 100)
}

/**
* Creates a manual order by sending the provided payload to the `saveOrderData`
service, handles the response, and provides feedback to the user.
*
* This method performs the following actions:
* 1. Sends the provided `payload` to the `addorderservice.saveOrderData` method to
create order
* 2. If the response contains a `new_channel`, displays a success message
indicating that the new channel was added successfully.
* 3. Displays a success message indicating that the order was created
successfully.
* 4. Tracks the "Add_order_manually" event using
`intercomService.intercomTrackEvent`.
* 5. Navigates the user to the `/orders/new` route after the order is created
successfully.
* 6. In case of an error, it checks if the error contains validation errors
(`err.errors`) and displays each error message.
* 7. If no validation errors are found, displays a generic error message.
*
* @param payload - The order data to be sent to the server for creating the manual
order.
*/
createManualOrder(payload: any): void {
this.sink.add(this.addorderservice.saveOrderData(payload).subscribe(
(res) => {
res?.new_channel ? this.toastr.success(`Channel ${res.new_channel} added
successfully`) : '';
this.toastr.success('Order Created Successfully');
this.intercomService.intercomTrackEvent("Add_order_manually");
this.Router.navigate(['/orders/new']);
},
(err) => {
if (err?.errors) {
for (const [key, value] of Object.entries(err.errors)) {
const lcl: any = value;
this.toastr.error(lcl[0]);
}
} else {
this.toastr.error(err.error?.message ? err.error?.message :
err?.message);
}
}
));
}

/**
* Manages the package creation or update process based on the checkbox event and
triggers the appropriate dialog for package management.
*
* This method performs the following actions:
* 1. Checks if the checkbox (`$event.target.checked`) is selected.
* 2. Retrieves the `length`, `breadth`, and `height` from the `createOrderForm`.
* 3. Initializes the `managePackageData` object with the retrieved dimensions and
other default values.
* 4. Checks if a package is already selected and if there is only one product in
the `products` array:
* - If the conditions are met, the method sets `managePackageData.type` to
`'update_catalog'` and includes additional product data (like `product_id` and
`weight`).
* - Otherwise, it sets `managePackageData.type` to `'create'`.
* 5. Opens the `ManagePackageCatalogComponent` dialog with the prepared
`managePackageData` and waits for its callback upon closing.
* 6. Based on the returned callback data:
* - If the `type` is `'create'`, the package data is added to `packageList`,
and the `updatePackageDimensionOnCallBack` method is called to update the
dimensions.
* - If the `type` is `'update'`, it updates the relevant package in the
`packageList`.
* - If the `type` is `'closePopup'`, it calls `updateSavePackageState` to
trigger saving of the package.
* - If the `type` is `'catalogUpdated'`, it calls `updateSavePackageState` and
updates the prefilled dimension state.
*
* @param $event - The checkbox event that determines whether to open the package
management dialog.
*/
managePackage($event: any): void {
if ($event.target.checked) {
const { length, breadth, height } = this.createOrderForm.value;
const managePackageData: any = { type: '', caseType: '', ...{ length,
breadth, height }, name: 'Box-01' };
const products = this.products
if (!this.selectedPackage && products?.length == 1 &&
products?.value[0]?.product_id) {
managePackageData.type = 'update_catalog';
managePackageData.product_id = products.value[0].product_id;
managePackageData.weight = this.applicableweight;
} else if (this.selectedPackage && this.selectedPackage.status == 5) {
managePackageData.type = 'update_saved_package';
managePackageData.name = this.selectedPackage.package_name
managePackageData.package_id = this.selectedPackage.package_id;
} else {
managePackageData.type = 'create';
}
const managePackageCallBack = this.dialog.open(
ManagePackageCatalogComponent,
{ disableClose: true, width: '450px', panelClass: 'nao-mp-padding', data:
managePackageData }
);
this.sink.add(managePackageCallBack.afterClosed().subscribe((callBackData:
any) => {
if (callBackData?.type == 'create') {
this.packageList.length == 0 ? (this.packageList = [callBackData.data]) :
this.packageList.push({ ...callBackData.data });
this.updatePackageDimensionOnCallBack(callBackData);
} else if (callBackData?.type == 'update_saved_package') {
this.updateSavePackageState();
this.managePrefillDimensionAndEditIconState(false);
} else if (callBackData?.type == 'update') {
this.updatePackageDimensionOnCallBack(callBackData);
const updatedArray = this.packageList.map(
(obj) => obj.id === callBackData.data.package_id ?
{ ...obj, ...callBackData.data } : obj
);
} else if (callBackData?.type == 'closePopup') {
this.updateSavePackageState();
} else if (callBackData?.type == 'catalogUpdated') {
this.updateSavePackageState();
this.managePrefillDimensionAndEditIconState(true);
}
}));
}
}

/**
* Updates the package dimension details and manages the UI state based on the
callback data.
*
* This method performs the following actions:
* 1. Updates the `selectedPackage` with the data received in the `callBackData`.
* 2. Patches the `createOrderForm` with the `dimensions` data from the callback,
applying it to the form fields.
* 3. Calls `managePrefillDimensionAndEditIconState(true)` to enable the
appropriate UI state for dimension-related fields and edit icons.
* 4. Sets the `isManagePackageOpen` flag to `false`, likely closing a package
management checkbox
*
* @param callBackData - The data object containing the updated package details,
including `dimensions`.
*/
updatePackageDimensionOnCallBack(callBackData: any): void {
this.selectedPackage = callBackData.data;
this.createOrderForm.patchValue({ ...callBackData.data.dimensions });
this.managePrefillDimensionAndEditIconState(true);
this.isManagePackageOpen = false;
}

/**
* This uncheck the checkbox when close popup
* Triggers a click event on the element with the ID 'savepackage'.
*
* This method checks if the DOM element with the ID 'savepackage' exists. If the
element is found, it programmatically triggers a click event on it.
* If the element does not exist, the method does nothing.
*/
updateSavePackageState(): void {
const savePackage: any = document.getElementById('savepackage');
savePackage ? savePackage.click() : null;
}

changeEditIconState(flag: boolean) {
this.isDimensionDisabled = flag;
}

changePreFillDimesnsionState(flag: boolean) {
this.isPrefilledDimension = flag;
}

/**
* Manages the state of dimension-related fields and the edit icon based on the
provided flag.
*
* This method performs the following actions:
* 1. Sets the `isDimensionDisabled` property to the value of `flag`, which
controls whether dimension fields should be disabled or enabled.
* 2. Sets the `isPrefilledDimension` property to the value of `flag`, which
controls whether the dimension fields are prefilled or not.
*
* @param flag - A boolean value that determines the state of dimension-related
fields and the edit icon.
*/
managePrefillDimensionAndEditIconState(flag: boolean) {
this.isDimensionDisabled = flag;
this.isPrefilledDimension = flag;
}
/**
* Confirms if secure shipment is required based on order details and opens a
confirmation dialog if needed.
*
* This method performs the following actions:
* 1. Retrieves the `sub_total` and `is_insurance_opt` values from the form
(`createOrderForm`).
* 2. If the insurance option is not selected (`is_insurance_opt == 0`) and secure
shipment has not been auto-adopted (`hasAutoSecureShipmentAdopted` is `false`), and
the subtotal exceeds the minimum insurance threshold for the user:
* - Opens a confirmation dialog (`ConfirmSecureShipmentPopComponent`) to ask
the user if they want to opt for insurance.
* - Passes the `sub_total` value to the dialog.
* - Once the dialog is closed, updates the form's `is_insurance_opt` value
based on the result and proceeds to the `postCreateOrder` method with the given
`type`.
* 3. If the conditions are not met, directly proceeds to the `postCreateOrder`
method.
*
* @param type - The type of the action, which is passed to the `postCreateOrder`
method.
*/
confirmSecureShipment(type: string): void {
const { sub_total, is_insurance_opt } = this.createOrderForm.value;
if (is_insurance_opt == 0 && !this.hasAutoSecureShipmentAdopted && sub_total >
this.userData.minimum_insurance_threshold) {
const dialog = this.dialog.open(ConfirmSecureShipmentPopComponent, {
panelClass: 'shipment-secure-popup',
data: { sub_total: sub_total }
});
this.sink.add(dialog.afterClosed().subscribe((result) => {
this.createOrderForm.patchValue({ is_insurance_opt: result.is_insurance_opt
+ "" });
this.postCreateOrder(type);
}));
} else {
this.postCreateOrder(type);
}
}

/**
* Generates a missing SKU and related product information for a specific product
row in the form.
*
* This method performs the following actions:
* 1. Retrieves the form values for the product at the specified `index`.
* 2. If the product name is too short (less than 3 characters) or `productList` is
not available, the method exits early.
* 3. Searches for a matching product in `productList` based on the product name
(case-insensitive match).
* - If a matching product is found, the product's `hsn`, `sku`,
`optionalValueStatus`, and `product_id` are patched into the form for the current
product.
* - If a duplicate SKU is found for the current row (checked by
`checkDuplicateSku`), the method exits early.
* 4. If no matching product is found in `productList`, the method generates a new
SKU using `addorderservice.createsku()` and sets the `optionalValueStatus` to 'Auto
Generated'.
*
* @param index - The index of the product in the `products` form array for which
SKU generation is needed.
*/
generateMissingSKU(index: any): void {
const rowValues = this.products.controls[index]?.value;
if (rowValues?.name?.length < 1 || !this.productList) return;
let productIndex = this.productList.findIndex((product: { name: string; }) => {
if (!product || !product.name) return false;
return product.name.toLowerCase() === rowValues.name.toLowerCase();
});
if (productIndex > -1) {
const selctedProduct = this.productList[productIndex];
this.products.controls[index].patchValue({
hsn: selctedProduct.hsn,
sku: selctedProduct.sku,
optionalValueStatus: `Fetched from product ${selctedProduct.name}`,
product_id: selctedProduct.id
});
if (this.checkDuplicateSku(selctedProduct.sku, index)) return;
} else {
this.products.controls[index].patchValue({
sku: this.addorderservice.createsku(),
optionalValueStatus: 'Auto Generated'
});
}
}

/**
* Checks if the provided SKU code already exists in the list of products,
excluding the current product at the given index.
*
* This method performs the following actions:
* 1. Initializes a flag `isDuplicateSkuFound` to `false` to track if a duplicate
SKU is found.
* 2. If `sku_code` is falsy (e.g., `null` or empty), the method immediately
returns `false` indicating no duplicate.
* 3. Sets the `length` variable to the total number of products, but excludes the
current product at the provided index when checking for duplicates.
* 4. Loops through all products and compares the `sku` value of each product with
the provided `sku_code`:
* - If a match is found and the index does not match the current product's
index, it sets `isDuplicateSkuFound` to `true`, resets the current product form
control, and sets the `units` field to `1`.
* - Displays an error toast message indicating the duplicate SKU and the
product index where the duplicate was found.
* 5. Returns `true` if a duplicate SKU was found, otherwise returns `false`.
*
* @param sku_code - The SKU code to check for duplicates.
* @param index - The index of the current product in the `products` array, which
should be excluded from the duplicate check.
* @returns {boolean} - `true` if a duplicate SKU is found, `false` otherwise.
*/
checkDuplicateSku(sku_code: any, index: number): boolean {
let isDuplicateSkuFound = false
if (!sku_code) return isDuplicateSkuFound;
let length = index < this.products.length - 1 ? this.products.length :
this.products.length - 1;
for (let i = 0; i < length; i++) {
if (this.products.at(i) && this.products.at(i).value.sku === sku_code &&
index != i) {
isDuplicateSkuFound = true;
this.products.controls[index].reset();
this.products.controls[index].patchValue({ units: 1 });
this.toastr.error(`Sku code - "${sku_code}" already exist in Product ${i +
1}`);
}
}
return isDuplicateSkuFound;
}

/**
* Opens a dialog for smart weight insights and handles updates to the order form
upon closure.
*
* This method performs the following actions:
* 1. Constructs the `data` object containing:
* - `pidInsights`: Weight insights data.
* - `orderData`: Order details (e.g., order ID, dimensions, weight, volumetric
weight) extracted from the form.
* - `isNewOrder`: A flag indicating whether this is a new order.
* 2. Opens a dialog using the `WeightInsightsComponent` with the specified `data`
and a custom panel class.
* 3. Subscribes to the `afterClosed` event of the dialog:
* - If the dialog returns a result, updates the order form with new values for
breadth, height, length, and weight based on the weight insights.
* 4. Manages the subscription by adding it to the `sink` for proper cleanup.
*/
openSmartWeightInsights(): void {
const data = {
pidInsights: this.weightInsights,
orderData: {
orderId: this.createOrderForm.value.order_id,
weight: this.createOrderForm.value.weight,
length: this.createOrderForm.value.length,
breadth: this.createOrderForm.value.breadth,
height: this.createOrderForm.value.height,
volumetric_weight: this.volumetricWeight
},
isNewOrder: true
}
const dialog = this.dialog.open(WeightInsightsComponent, {
panelClass: 'scroll-dialog',
data: data
});

this.sink.add(
dialog.afterClosed().subscribe((result) => {
if (result) this.createOrderForm.patchValue({ breadth: 10, height: 10,
length: 10, weight: this.weightInsights?.suggested_weight || 0 });
})
);
}

/**
* Handles the search event for categories and triggers the search operation if the
input meets criteria.
*
* This method performs the following actions:
* 1. Extracts the `categoryName` from the `event` object.
* 2. Checks if the `categoryName` is defined and its length exceeds 3 characters:
* - If true, emits an update to `userQuestionUpdate` with the `categoryName`
and type set to `'category'`.
* - If false, clears the `categoryList` by assigning an empty array.
*
* @param event - The event object containing the value of the category search
input.
*/
searchCategory(event: any): void {
const categoryName: string = event.value;
categoryName?.length > 3 ? this.userQuestionUpdate.next({ categoryName, type:
'category' }) : this.categoryList = [];
}

/**
* Fetches category details based on the provided category name and updates the
category list.
*
* This method performs the following actions:
* 1. Calls the `getCategoyDetails` method of the `addorderservice`, passing the
given `categoryName`.
* 2. Subscribes to the response and updates the `categoryList` property with the
returned data.
* 3. If the response is `null` or `undefined`, initializes `categoryList` as an
empty array.
* 4. Manages the subscription by adding it to the `sink` for proper cleanup.
*
* @param categoryName - The name of the category for which details are to be
fetched.
*/
fetchCategory(categoryName: string): void {

this.sink.add(this.addorderservice.getCategoyDetails(categoryName).subscribe((data)
=> { this.categoryList = data || []; }))
}

/**
* Updates the category name for a specific product in the form based on a selected
suggestion.
*
* This method performs the following actions:
* 1. Identifies the product control at the specified index (`i`) in the `products`
form array.
* 2. Updates the `category_name` field of the selected product with the
`category_name` from the provided category object.
*
* @param category - The selected category object containing the `category_name` to
be assigned.
* @param i - The index of the product in the `products` form array to be updated.
*/
selectSuggestedCategory(category: any, i: number): void {
this.products.controls[i].patchValue({ category_name:
category.category_name });
}

/**
* This method add the active border class to the input when mouse is in
* Adds the active CSS class (`nao-prefix-frm-cntl-act`) to an HTML element with
the specified ID.
*
* This method performs the following actions:
* 1. Retrieves the element by its ID using `document.getElementById`.
* 2. Uses a `setTimeout` to delay the addition of the CSS class by 60
milliseconds.
* 3. If the element exists, adds the `nao-prefix-frm-cntl-act` class; otherwise,
does nothing.
*
* @param elId - The ID of the HTML element to which the CSS class should be added.
*/
setPrefixActiveCss(elId: string): void {
const preFixEl: any = document.getElementById(elId);
setTimeout(() => {
preFixEl ? preFixEl.classList.add('active-pre-post-input') : null;
}, 60)
}

/**
* This method removes the active border class from the input when mouse is out
* Removes the active CSS class (`nao-prefix-frm-cntl-act`) from an HTML element
with the specified ID.
*
* This method performs the following actions:
* 1. Retrieves the element by its ID using `document.getElementById`.
* 2. Uses a `setTimeout` to delay the removal of the CSS class by 80 milliseconds.
* 3. If the element exists, removes the `nao-prefix-frm-cntl-act` class;
otherwise, does nothing.
*
* @param elId - The ID of the HTML element from which the CSS class should be
removed.
*/
removePrefixActiveCss(elId: string): void {
const preFixEl: any = document.getElementById(elId);
setTimeout(() => {
preFixEl ? preFixEl.classList.remove('active-pre-post-input') : null;
}, 80)
}

/**
* Sends buyer information to the server for processing.
*
* This method performs the following actions:
* 1. Constructs a `data` object containing:
* - `delivery_postcode`: The delivery postcode extracted from the provided
`value`.
* - `mobile`: The buyer's phone number.
* 2. Makes an HTTP POST request to the `courier/buyerlevelinfo` endpoint using the
constructed `data`.
* 3. Subscribes to the response and updates the `buyerLevelInfo` property with the
returned data.
* 4. Manages the subscription by adding it to the `sink` for cleanup.
*
* @param value - An object containing buyer's delivery details (e.g.,
`delivery_postcode`).
* @param number - The buyer's mobile phone number.
*/
sendBuyerInfo(value: any, number: any): void {
const data = {
delivery_postcode: value.delivery_postcode,
mobile: number
};
this.sink.add(this.httpService.post('courier/buyerlevelinfo',
data).subscribe((res) => {
this.buyerLevelInfo = res.data;
}));
}

/**
* Fetches the list of couriers available for serviceability based on the provided
payload.
*
* This method performs the following actions:
* 1. Generates the required `data` and `shipmentDetails` by calling
`creatingPayloadDataForShipNow` from the `addorderservice`,
* using the provided payload, selected pickup address, applicable weight, and
auto-secure shipment adoption status.
* 2. Updates the component's `shipmentDetails` and `serviceabilityDataToSend`
properties with the generated values.
* 3. Sends buyer information by calling `sendBuyerInfo`, passing the
serviceability data and shipping phone.
* 4. Makes an HTTP GET request to the `courier/serviceability` endpoint using the
generated `data`:
* - If the response status is `404`, it displays an error message using
`toastr` and clears the courier list.
* - Otherwise, it updates the courier list, stores the full response in
`courierResponse`, and sets `isServiceabilityVisible` to `true`.
* 5. Subscribes to the HTTP request within a sink to manage subscriptions
effectively.
*
* @param payload - The data used to generate courier serviceability details.
*/
fetchCourierList(payload: any): void {
const { data, shipmentDetails } =
this.addorderservice.creatingPayloadDataForShipNow(payload,
this.selectedPickupAddress, this.applicableweight,
this.hasAutoSecureShipmentAdopted);
this.shipmentDetails = shipmentDetails;
this.serviceabilityDataToSend = data;
this.sendBuyerInfo(data, payload.shipping_phone);
this.sink.add(this.httpService.get('courier/serviceability',
data).subscribe((res) => {
if (res?.status === 404) {
this.toastr.error(res.message);
this.courierlist = [];
} else {
this.courierlist = res.data;
this.courierResponse = res;
this.isServiceabilityVisible = true;
}
}));
}

/**
* Handles the close action for the serviceability section.
*
* This method performs the following action:
* - Sets the `isServiceabilityVisible` flag to `false` to hide the serviceability
section or dise drawer.
*/
handleClose(): void {
this.isServiceabilityVisible = false;
}

/**
* Clears the currently selected package and resets related form fields.
*
* This method performs the following actions:
* 1. Resets the dimensions (`length`, `height`, and `breadth`) in the
`createOrderForm` to `null`.
* 2. Sets the `selectedPackage` property to `undefined`, effectively clearing
any previously selected package.
* 3. Calls `managePrefillDimensionAndEditIconState` with `false` to update the
UI state for dimension prefill and edit icons.
*/
clearSelectedPackage(): void {
this.createOrderForm.patchValue({ length: null, height: null, breadth: null });
this.selectedPackage = undefined;
this.managePrefillDimensionAndEditIconState(false);
}

grawlIntent(closeGrawl: boolean): void {


this.isGrawlVisible = false;
localStorage.setItem('isGrawlOpen' , "OPENED")
closeGrawl && this.Router.navigate(['settings/additional-settings/custom-add-
orders']);
}

ngOnDestroy(): void {
delete localStorage.cloneData;
this.sink && this.sink.unsubscribe()
}
}

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy