(rawInput: string): string {
const digitsOnly = rawInput.replace(/\D/g, '');
// Handle 20-digit format with AI (00)
if (digitsOnly.length === 20 && digitsOnly.startsWith('00')) {
return digitsOnly.substring(2);
}
// Handle 17-digit body (missing check digit)
if (digitsOnly.length === 17) {
const checkDigit = computeGs1CheckDigit(digitsOnly);
return digitsOnly + checkDigit.toString();
}
if (digitsOnly.length !== 18) {
throw new Error(Invalid SSCC length: ${digitsOnly.length}. Expected 17 or 18 digits.);
}
return digitsOnly;
}
#### 2. GS1 Modulo-10 Check Digit Algorithm
The GS1 Modulo-10 algorithm applies weighted multipliers to the payload digits. Starting from the rightmost digit of the 17-digit body, digits are multiplied by 3 and 1 alternately.
```typescript
/**
* Computes the GS1 Modulo-10 check digit for a 17-digit payload.
*/
export function computeGs1CheckDigit(payload: string): number {
if (payload.length !== 17) {
throw new Error('Payload must be exactly 17 digits');
}
let sum = 0;
// Iterate from right to left
for (let i = payload.length - 1; i >= 0; i--) {
const digit = parseInt(payload[i], 10);
const positionFromRight = payload.length - i;
const multiplier = (positionFromRight % 2 === 1) ? 3 : 1;
sum += digit * multiplier;
}
return (10 - (sum % 10)) % 10;
}
/**
* Validates an 18-digit SSCC by verifying the check digit.
*/
export function validateSsccIntegrity(sscc: string): SsccValidationResult {
const normalized = normalizeSsccInput(sscc);
const body = normalized.substring(0, 17);
const providedCheckDigit = parseInt(normalized[17], 10);
const expectedCheckDigit = computeGs1CheckDigit(body);
return {
isValid: providedCheckDigit === expectedCheckDigit,
normalizedSscc: normalized,
providedCheckDigit,
expectedCheckDigit,
error: providedCheckDigit !== expectedCheckDigit
? `Check digit mismatch: expected ${expectedCheckDigit}, got ${providedCheckDigit}`
: undefined
};
}
3. EDIFACT Extraction with Custom Delimiters
EDIFACT messages use the UNA segment to define custom delimiters. Hardcoding separators like + and ' will fail on non-standard files. A robust parser must read the UNA header first.
export interface EdifactSsccResult {
sscc: string;
segmentId: string;
qualifier: string;
validation: SsccValidationResult;
}
/**
* Extracts SSCCs from EDIFACT messages, handling custom UNA delimiters.
*/
export function extractSsccsFromEdifact(rawMessage: string): EdifactSsccResult[] {
const results: EdifactSsccResult[] = [];
// Parse UNA delimiters
const unaMatch = rawMessage.match(/^UNA(.{6})/);
const delimiters = unaMatch ? {
compSep: unaMatch[1][0],
elemSep: unaMatch[1][1],
segTerm: unaMatch[1][5]
} : { compSep: ':', elemSep: '+', segTerm: "'" };
// Split into segments
const segments = rawMessage
.replace(/\r?\n/g, '')
.split(delimiters.segTerm)
.map(s => s.trim())
.filter(Boolean);
for (const segment of segments) {
const elements = segment.split(delimiters.elemSep);
const segmentId = elements[0];
// Check relevant segments: GIN (with BJ qualifier), RFF (SI, AAK)
if (segmentId === 'GIN' || segmentId === 'RFF') {
for (let i = 1; i < elements.length; i++) {
const parts = elements[i].split(delimiters.compSep);
const qualifier = parts[0];
const value = parts[1];
if (value && (segmentId === 'GIN' && qualifier === 'BJ' ||
segmentId === 'RFF' && (qualifier === 'SI' || qualifier === 'AAK'))) {
try {
const validation = validateSsccIntegrity(value);
results.push({
sscc: validation.normalizedSscc,
segmentId,
qualifier,
validation
});
} catch (err) {
// Log parsing error but continue processing
console.warn(`Failed to parse SSCC in segment ${segmentId}: ${err}`);
}
}
}
}
}
return results;
}
Architecture Decisions
- Pure Functions: All validation and extraction functions are pure, ensuring testability and idempotency.
- Type Safety: TypeScript interfaces enforce contract compliance and reduce runtime errors.
- Error Boundaries: Extraction functions catch and log errors without halting the entire pipeline, ensuring partial data availability.
- Delimiter Agnostic: The EDIFACT parser reads UNA headers dynamically, supporting custom separators used by various trading partners.
Pitfall Guide
1. Application Identifier Contamination
Explanation: Scanners often output the AI (00) followed by the 18-digit SSCC, resulting in a 20-character string. Treating this as the SSCC payload causes validation failure.
Fix: Always strip the leading 00 before validation. Use normalizeSsccInput to handle this automatically.
2. Fixed-Width ERP Blindness
Explanation: ERP exports may store SSCCs in fixed-width fields without delimiters. Regex patterns using word boundaries (\b) will fail to match SSCCs followed immediately by other data.
Fix: Use lookbehind assertions or fixed-length extraction. Example: /(?<!\d)(00\d{18})/g captures the AI-prefixed SSCC regardless of trailing content.
3. EDIFACT Delimiter Assumption
Explanation: Hardcoding + and ' as separators will break on files with custom UNA delimiters, leading to missed SSCCs or parsing errors.
Fix: Always parse the UNA segment first. If UNA is absent, fall back to defaults but log a warning for non-standard files.
4. Check Digit Math Errors
Explanation: Incorrect multiplier application (e.g., starting with 1 instead of 3, or left-to-right iteration) produces wrong check digits.
Fix: Implement the algorithm strictly from right to left, with the rightmost digit multiplied by 3. Use the provided computeGs1CheckDigit function.
5. Leading Zero Loss
Explanation: Parsing SSCCs as numbers strips leading zeros, corrupting the payload.
Fix: Always treat SSCCs as strings. Avoid parseInt or Number conversions unless extracting individual digits for checksum calculation.
6. Segment Qualifier Variance
Explanation: Only checking GIN+BJ misses SSCCs in RFF+SI or RFF+AAK segments, which are common in DESADV messages.
Fix: Extend extraction logic to check multiple segments and qualifiers. Use a whitelist of valid qualifiers per segment type.
7. Performance in Batch Processing
Explanation: Inefficient string operations or regex compilation in loops can degrade performance when processing large EDIFACT files.
Fix: Pre-compile regex patterns, use array methods like map and filter for batch processing, and avoid unnecessary string concatenations.
Production Bundle
Action Checklist
Decision Matrix
| Scenario | Recommended Approach | Why | Cost Impact |
|---|
| Real-time UI Validation | Client-side Modulo-10 Check | Immediate feedback, reduces server load | Low |
| Batch EDIFACT Processing | Server-side Structured Parser | Handles large files, custom delimiters, and multiple segments | Medium |
| ERP Fixed-Width Exports | Lookbehind Regex + Normalization | Captures SSCCs without delimiters | Low |
| High-Volume API Integration | Optimized Pure Functions | Ensures performance and idempotency | Medium |
Configuration Template
// sscc.config.ts
export interface SsccConfig {
enableLogging: boolean;
logLevel: 'debug' | 'info' | 'warn' | 'error';
edifactDefaults: {
compSep: string;
elemSep: string;
segTerm: string;
};
validationRules: {
rejectInvalidCheckDigit: boolean;
autoGenerateCheckDigit: boolean;
};
}
export const defaultConfig: SsccConfig = {
enableLogging: true,
logLevel: 'info',
edifactDefaults: {
compSep: ':',
elemSep: '+',
segTerm: "'"
},
validationRules: {
rejectInvalidCheckDigit: true,
autoGenerateCheckDigit: true
}
};
Quick Start Guide
- Install Dependencies: Ensure TypeScript is configured in your project.
npm install typescript --save-dev
npx tsc --init
- Copy Implementation: Add the
normalizeSsccInput, computeGs1CheckDigit, validateSsccIntegrity, and extractSsccsFromEdifact functions to your codebase.
- Run Validation Tests: Test with sample SSCCs and EDIFACT messages.
const result = validateSsccIntegrity('356012345600000016');
console.log(result.isValid); // true
- Integrate with Pipeline: Use
extractSsccsFromEdifact in your EDI processing workflow to validate and extract SSCCs automatically.
- Monitor Logs: Enable logging to track validation errors and data quality issues in production.