Skip to content

Commit

Permalink
Add service for stock endpoints
Browse files Browse the repository at this point in the history
  • Loading branch information
saschaheinl-fft authored May 27, 2024
1 parent 6da4f8e commit 9f14338
Show file tree
Hide file tree
Showing 6 changed files with 327 additions and 4 deletions.
2 changes: 1 addition & 1 deletion src/common/httpClient/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export enum HttpMethod {
OPTIONS = 'OPTIONS',
}

export type QueryParams = Record<string, string>;
export type QueryParams = Record<string, string | string[]>;

export interface HttpRequestConfiguration {
method: HttpMethod;
Expand Down
1 change: 1 addition & 0 deletions src/fft-api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ export * from './routing-plan';
export * from './shipment';
export * from './subscription';
export * from './types';
export * from './stock';
253 changes: 253 additions & 0 deletions src/fft-api/stock/fftStockService.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,253 @@
import { Logger } from 'tslog';
import { FftApiClient } from '../common';
import {
FacilityServiceType,
FacilityStatus,
Stock,
StockDistribution,
StockForCreation,
StockForUpdate,
StockPaginatedResult,
StocksForUpsert,
StockSummaries,
StockUpsertOperationResult,
} from '../types';
import { CustomLogger, QueryParams } from '../../common';
import { ResponseError } from 'superagent';

export class FftStockService {
private readonly path: string = 'stocks';
private readonly logger: Logger<FftStockService> = new CustomLogger<FftStockService>();

constructor(private readonly apiClient: FftApiClient) {}

public async createStock(stockForCreation: StockForCreation): Promise<Stock> {
try {
return await this.apiClient.post<Stock>(this.path, { ...stockForCreation });
} catch (error) {
const httpError = error as ResponseError;
this.logger.error(
`Could not create stock. Failed with status ${httpError.status}, error: ${
httpError.response ? JSON.stringify(httpError.response.body) : ''
}`
);

throw error;
}
}

public async getAll(
facilityRef?: string,
tenantArticleIds?: string[],
locationRefs?: string[],
size?: number,
startAfterId?: string
): Promise<StockPaginatedResult> {
try {
const queryParams: QueryParams = {};

if (facilityRef) {
queryParams['facilityRef'] = facilityRef;
}

if (tenantArticleIds) {
tenantArticleIds = tenantArticleIds.slice(0, 499);
queryParams['tenantArticleId'] = tenantArticleIds;
}

if (locationRefs) {
locationRefs = locationRefs.slice(0, 499);
queryParams['locationRef'] = locationRefs;
}

if (size && size <= 100) {
queryParams['size'] = size.toString();
}

if (startAfterId) {
queryParams['startAfterId'] = startAfterId;
}

return await this.apiClient.get<StockPaginatedResult>(this.path, queryParams);
} catch (error) {
const httpError = error as ResponseError;
this.logger.error(
`Fetching all stock failed with status ${httpError.status}, error: ${
httpError.response ? JSON.stringify(httpError.response.body) : ''
}`
);

throw error;
}
}

public async upsertStocks(stocksForUpsert: StocksForUpsert): Promise<StockUpsertOperationResult> {
try {
return await this.apiClient.put<StockUpsertOperationResult>(this.path, { ...stocksForUpsert });
} catch (error) {
const httpError = error as ResponseError;
this.logger.error(
`Could not upsert stock. Failed with status ${httpError.status}, error: ${
httpError.response ? JSON.stringify(httpError.response.body) : ''
}`
);

throw error;
}
}

public async updateById(stockId: string, stockForUpdate: StockForUpdate): Promise<Stock> {
try {
return await this.apiClient.put<Stock>(`${this.path}/${stockId}`, { ...stockForUpdate });
} catch (error) {
const httpError = error as ResponseError;
this.logger.error(
`Could not update stock. Failed with status ${httpError.status}, error: ${
httpError.response ? JSON.stringify(httpError.response.body) : ''
}`
);

throw error;
}
}

public async deleteById(stockId: string): Promise<void> {
try {
return await this.apiClient.delete(`${this.path}/${stockId}`);
} catch (error) {
const httpError = error as ResponseError;
this.logger.error(
`Could not delete stock with id ${stockId}. Failed with status ${httpError.status}, error: ${
httpError.response ? JSON.stringify(httpError.response.body) : ''
}`
);

throw error;
}
}

public async getById(stockId: string): Promise<Stock> {
try {
return await this.apiClient.get<Stock>(`${this.path}/${stockId}`);
} catch (error) {
const httpError = error as ResponseError;
this.logger.error(
`Could not get stock with id '${stockId}'. Failed with status ${httpError.status}, error: ${
httpError.response ? JSON.stringify(httpError.response.body) : ''
}`
);

throw error;
}
}

public async getStockSummaries(
size?: number,
startAfterId?: string,
facilityServiceTypes?: FacilityServiceType[],
facilityStatuses?: FacilityStatus[],
facilityRefs?: string[],
allowStale?: boolean,
tenantArticleIds?: string[],
channelRefs?: string[]
): Promise<StockSummaries> {
try {
const queryParams: QueryParams = {};

if (size && size <= 100) {
queryParams['size'] = size.toString();
}

if (startAfterId) {
queryParams['startAfterId'] = startAfterId;
}

if (facilityServiceTypes && facilityServiceTypes.length > 0) {
queryParams['facilityServiceTypes'] = facilityServiceTypes;
}

if (facilityStatuses) {
queryParams['facilityStatus'] = facilityStatuses;
}

if (facilityRefs) {
facilityRefs = facilityRefs.slice(0, 499);
queryParams['facilityRefs'] = facilityRefs;
}

if (allowStale) {
queryParams['allowStale'] = String(allowStale);
}

if (tenantArticleIds) {
tenantArticleIds = tenantArticleIds.slice(0, 499);
queryParams['tenantArticleIds'] = tenantArticleIds;
}

if (channelRefs) {
channelRefs = channelRefs.slice(0, 49);
queryParams['channelRefs'] = channelRefs;
}
return await this.apiClient.get<StockSummaries>(`${this.path}/summaries`, queryParams);
} catch (error) {
const httpError = error as ResponseError;
this.logger.error(
`Fetching stock summaries failed with status ${httpError.status}, error: ${
httpError.response ? JSON.stringify(httpError.response.body) : ''
}`
);

throw error;
}
}

public async getStockDistribution(
tenantArticleId: string,
facilityServiceTypes?: FacilityServiceType[],
facilityStatuses?: FacilityStatus[],
facilityName?: string,
facilityIds?: string[],
channelRefs?: string[]
): Promise<StockDistribution> {
if (!tenantArticleId) {
throw new Error('tenantArticleId is missing.');
}

try {
const queryParams: QueryParams = {};

if (facilityServiceTypes) {
queryParams['facilityServiceTypes'] = facilityServiceTypes;
}

if (facilityStatuses) {
queryParams['facilityStatus'] = facilityStatuses;
}

if (facilityName) {
queryParams['facilityName'] = facilityName;
}

if (facilityIds) {
facilityIds = facilityIds.slice(0, 499);
queryParams['facilityIds'] = facilityIds;
}

if (channelRefs) {
channelRefs = channelRefs.slice(0, 49);
queryParams['channelRefs'] = channelRefs;
}

return await this.apiClient.get<StockDistribution>(`articles/${tenantArticleId}/stockdistribution`, queryParams);
} catch (error) {
const httpError = error as ResponseError;
this.logger.error(
`Fetching stock distribution for tenantArticleId ${tenantArticleId} failed with status ${
httpError.status
}, error: ${httpError.response ? JSON.stringify(httpError.response.body) : ''}`
);

throw error;
}
}
}
1 change: 1 addition & 0 deletions src/fft-api/stock/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './fftStockService';
38 changes: 38 additions & 0 deletions src/fft-api/types/api.swagger.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35809,6 +35809,12 @@ components:
$ref: '#/components/schemas/ServiceJobLink'
type: array
minItems: 1
fullIdentifier:
type: string
description: >-
Full identifier of the service job. Using the full name of the
customer when created from an order.
example: 240429_lorem-42
required:
- id
- serviceJobLinks
Expand All @@ -35834,6 +35840,12 @@ components:
$ref: '#/components/schemas/ServiceJobLinkForCreation'
type: array
minItems: 1
fullIdentifier:
type: string
description: >-
Full identifier of the service job. Using the full name of the
customer when created from an order.
example: 240429_lorem-42
required:
- serviceJobLinks
ServiceJobLinkForCreation:
Expand Down Expand Up @@ -36324,6 +36336,8 @@ components:
properties:
name:
type: string
facilityRef:
type: string
InboundProcessPurchaseOrder:
type: object
properties:
Expand Down Expand Up @@ -36591,6 +36605,12 @@ components:
type: number
available:
type: number
deprecated: true
description: >-
This field is deprecated and replaced by new availability concepts.
Please see
https://docs.fulfillmenttools.com/api-docs/use-cases/inventory-management/global-inventory/availability
for more information.
byTrait:
$ref: '#/components/schemas/InventoryFacilityStockShapeByTrait'
changeReason:
Expand All @@ -36599,8 +36619,20 @@ components:
- UNKNOWN
availableToPromise:
type: number
deprecated: true
description: >-
This field is deprecated and replaced by new availability concepts.
Please see
https://docs.fulfillmenttools.com/api-docs/use-cases/inventory-management/global-inventory/availability
for more information.
readyToPick:
type: number
deprecated: true
description: >-
This field is deprecated and replaced by new availability concepts.
Please see
https://docs.fulfillmenttools.com/api-docs/use-cases/inventory-management/global-inventory/availability
for more information.
availableForPicking:
type: number
delta:
Expand All @@ -36609,6 +36641,10 @@ components:
type: array
items:
$ref: '#/components/schemas/InventoryFacilityStockStaleReason'
stockOnHand:
type: number
availableOnStock:
type: number
required:
- facilityRef
- tenantArticleId
Expand All @@ -36623,6 +36659,8 @@ components:
- availableForPicking
- delta
- staleReasons
- stockOnHand
- availableOnStock
RequestedDate:
type: object
properties:
Expand Down
Loading

0 comments on commit 9f14338

Please sign in to comment.