585 lines
19 KiB
JavaScript
585 lines
19 KiB
JavaScript
/**
|
|
* Backend Integration für Skrift Konfigurator
|
|
* Erweitert handleOrderSubmit um Backend-API Calls
|
|
*/
|
|
|
|
import SkriftBackendAPI from './configurator-api.js';
|
|
import { preparePlaceholdersForIndex } from './configurator-utils.js';
|
|
|
|
/**
|
|
* Bereitet Letter-Daten für Backend vor
|
|
*/
|
|
function prepareLettersForBackend(state) {
|
|
const letters = [];
|
|
const quantity = parseInt(state.answers?.quantity) || 1;
|
|
|
|
// Haupttext
|
|
const mainText = state.answers?.letterText || state.answers?.text || state.answers?.briefText || '';
|
|
const font = state.answers?.font || 'tilda';
|
|
const format = state.answers?.format || 'A4';
|
|
|
|
// Für jede Kopie einen Letter-Eintrag erstellen mit individuellen Platzhaltern
|
|
for (let i = 0; i < quantity; i++) {
|
|
const placeholders = preparePlaceholdersForIndex(state, i);
|
|
|
|
letters.push({
|
|
index: i,
|
|
text: mainText,
|
|
font: mapFontToBackend(font),
|
|
format: mapFormatToBackend(format),
|
|
placeholders: placeholders,
|
|
type: 'letter',
|
|
});
|
|
}
|
|
|
|
return letters;
|
|
}
|
|
|
|
/**
|
|
* Bereitet Envelope-Daten für Backend vor
|
|
*/
|
|
function prepareEnvelopesForBackend(state) {
|
|
const envelopes = [];
|
|
// envelope ist ein boolean (true/false), nicht 'yes'/'no'
|
|
const hasEnvelope = state.answers?.envelope === true;
|
|
|
|
console.log('[Backend Integration] prepareEnvelopesForBackend:', {
|
|
envelope: state.answers?.envelope,
|
|
hasEnvelope,
|
|
envelopeMode: state.answers?.envelopeMode,
|
|
});
|
|
|
|
if (!hasEnvelope) {
|
|
return envelopes;
|
|
}
|
|
|
|
const quantity = parseInt(state.answers?.quantity) || 1;
|
|
const envelopeMode = state.answers?.envelopeMode || 'recipientData';
|
|
const format = state.answers?.format || 'A4';
|
|
const font = state.answers?.envelopeFont || state.answers?.font || 'tilda';
|
|
|
|
// Envelope Format bestimmen
|
|
const envelopeFormat = format === 'a4' ? 'DIN_LANG' : 'C6';
|
|
|
|
if (envelopeMode === 'recipientData') {
|
|
// Empfängeradresse-Modus: Ein Envelope pro Brief mit individuellen Empfängerdaten
|
|
for (let i = 0; i < quantity; i++) {
|
|
const placeholders = preparePlaceholdersForIndex(state, i);
|
|
const recipient = state.recipientRows?.[i] || {};
|
|
|
|
// Umschlagtext aus Empfängerdaten zusammenbauen
|
|
const lines = [];
|
|
const fullName = `${recipient.firstName || ''} ${recipient.lastName || ''}`.trim();
|
|
if (fullName) lines.push(fullName);
|
|
|
|
const streetLine = `${recipient.street || ''} ${recipient.houseNumber || ''}`.trim();
|
|
if (streetLine) lines.push(streetLine);
|
|
|
|
const location = `${recipient.zip || ''} ${recipient.city || ''}`.trim();
|
|
if (location) lines.push(location);
|
|
|
|
if (recipient.country && recipient.country !== 'Deutschland') {
|
|
lines.push(recipient.country);
|
|
}
|
|
|
|
envelopes.push({
|
|
index: i,
|
|
text: lines.join('\n'),
|
|
font: mapFontToBackend(font),
|
|
format: envelopeFormat,
|
|
placeholders: placeholders,
|
|
type: 'envelope',
|
|
envelopeType: 'recipient',
|
|
});
|
|
}
|
|
} else if (envelopeMode === 'customText') {
|
|
// Custom Text Modus mit Platzhaltern
|
|
const customText = state.answers?.envelopeCustomText || '';
|
|
|
|
for (let i = 0; i < quantity; i++) {
|
|
const placeholders = preparePlaceholdersForIndex(state, i);
|
|
|
|
envelopes.push({
|
|
index: i,
|
|
text: customText,
|
|
font: mapFontToBackend(font),
|
|
format: envelopeFormat,
|
|
placeholders: placeholders,
|
|
type: 'envelope',
|
|
envelopeType: 'custom',
|
|
});
|
|
}
|
|
}
|
|
|
|
return envelopes;
|
|
}
|
|
|
|
// Hinweis: preparePlaceholdersForIndex ist jetzt in configurator-utils.js
|
|
|
|
/**
|
|
* Mapped Frontend-Font zu Backend-Font
|
|
*/
|
|
function mapFontToBackend(frontendFont) {
|
|
const fontMap = {
|
|
'tilda': 'tilda',
|
|
'alva': 'alva',
|
|
'ellie': 'ellie',
|
|
// Füge weitere Mappings hinzu falls nötig
|
|
};
|
|
|
|
return fontMap[frontendFont] || 'tilda';
|
|
}
|
|
|
|
/**
|
|
* Mapped Frontend-Format zu Backend-Format
|
|
*/
|
|
function mapFormatToBackend(frontendFormat) {
|
|
const formatMap = {
|
|
'a4': 'A4',
|
|
'a6p': 'A6_PORTRAIT',
|
|
'a6l': 'A6_LANDSCAPE',
|
|
'A4': 'A4',
|
|
'A6_PORTRAIT': 'A6_PORTRAIT',
|
|
'A6_LANDSCAPE': 'A6_LANDSCAPE',
|
|
};
|
|
|
|
return formatMap[frontendFormat] || 'A4';
|
|
}
|
|
|
|
/**
|
|
* Ermittelt das Umschlag-Format basierend auf Brief-Format
|
|
*/
|
|
function getEnvelopeFormat(letterFormat) {
|
|
const format = String(letterFormat).toLowerCase();
|
|
if (format === 'a4') return 'DIN_LANG';
|
|
if (format === 'a6p' || format === 'a6l') return 'C6';
|
|
return 'DIN_LANG';
|
|
}
|
|
|
|
/**
|
|
* Formatiert Format für lesbare Ausgabe
|
|
*/
|
|
function formatFormatLabel(format) {
|
|
const labels = {
|
|
'a4': 'A4 Hochformat',
|
|
'a6p': 'A6 Hochformat',
|
|
'a6l': 'A6 Querformat',
|
|
};
|
|
return labels[format] || format;
|
|
}
|
|
|
|
/**
|
|
* Formatiert Font für lesbare Ausgabe
|
|
*/
|
|
function formatFontLabel(font) {
|
|
const labels = {
|
|
'tilda': 'Tilda',
|
|
'alva': 'Alva',
|
|
'ellie': 'Ellie',
|
|
};
|
|
return labels[font] || font;
|
|
}
|
|
|
|
/**
|
|
* Baut das komplette Webhook-Datenobjekt zusammen
|
|
* Enthält ALLE relevanten Felder für Bestellbestätigung und n8n Workflow
|
|
*/
|
|
function buildWebhookData(state, backendResult) {
|
|
const answers = state.answers || {};
|
|
const order = state.order || {};
|
|
const quote = state.quote || {};
|
|
const ctx = state.ctx || {};
|
|
|
|
// Gutschein-Informationen
|
|
const voucherCode = order.voucherStatus?.valid ? order.voucherCode : null;
|
|
const voucherDiscount = order.voucherStatus?.valid ? (order.voucherStatus.discount || 0) : 0;
|
|
|
|
// Umschlag-Format ermitteln
|
|
const envelopeFormat = answers.envelope ? getEnvelopeFormat(answers.format) : null;
|
|
|
|
// Inland/Ausland zählen
|
|
let domesticCount = 0;
|
|
let internationalCount = 0;
|
|
const addressMode = state.addressMode || 'classic';
|
|
const rows = addressMode === 'free' ? (state.freeAddressRows || []) : (state.recipientRows || []);
|
|
|
|
for (const row of rows) {
|
|
if (!row) continue;
|
|
const country = addressMode === 'free' ? (row.line5 || '') : (row.country || '');
|
|
const countryLower = country.toLowerCase().trim();
|
|
const isDomestic = !countryLower ||
|
|
countryLower === 'deutschland' ||
|
|
countryLower === 'germany' ||
|
|
countryLower === 'de';
|
|
|
|
if (isDomestic) {
|
|
domesticCount++;
|
|
} else {
|
|
internationalCount++;
|
|
}
|
|
}
|
|
|
|
return {
|
|
// === BESTELLNUMMER & ZEITSTEMPEL ===
|
|
orderNumber: backendResult?.orderNumber || null,
|
|
timestamp: new Date().toISOString(),
|
|
|
|
// === KUNDE ===
|
|
customerType: answers.customerType || 'private',
|
|
customerTypeLabel: answers.customerType === 'business' ? 'Geschäftskunde' : 'Privatkunde',
|
|
|
|
// === PRODUKT ===
|
|
product: ctx.product?.key || null,
|
|
productLabel: ctx.product?.label || null,
|
|
productCategory: ctx.product?.category || null,
|
|
|
|
// === MENGE ===
|
|
quantity: parseInt(answers.quantity) || 0,
|
|
domesticCount: domesticCount,
|
|
internationalCount: internationalCount,
|
|
|
|
// === FORMAT & SCHRIFT ===
|
|
format: answers.format || null,
|
|
formatLabel: formatFormatLabel(answers.format),
|
|
font: answers.font || 'tilda',
|
|
fontLabel: formatFontLabel(answers.font || 'tilda'),
|
|
|
|
// === VERSAND ===
|
|
shippingMode: answers.shippingMode || null,
|
|
shippingModeLabel: answers.shippingMode === 'direct' ? 'Einzelversand durch Skrift' : 'Sammellieferung',
|
|
|
|
// === UMSCHLAG ===
|
|
envelopeIncluded: answers.envelope === true,
|
|
envelopeFormat: envelopeFormat,
|
|
envelopeFormatLabel: envelopeFormat === 'DIN_LANG' ? 'DIN Lang' : (envelopeFormat === 'C6' ? 'C6' : null),
|
|
envelopeMode: answers.envelopeMode || null,
|
|
envelopeModeLabel: answers.envelopeMode === 'recipientData' ? 'Empfängeradresse' :
|
|
(answers.envelopeMode === 'customText' ? 'Individueller Text' : null),
|
|
envelopeFont: answers.envelopeFont || answers.font || 'tilda',
|
|
envelopeFontLabel: formatFontLabel(answers.envelopeFont || answers.font || 'tilda'),
|
|
envelopeCustomText: answers.envelopeCustomText || null,
|
|
|
|
// === INHALT ===
|
|
contentCreateMode: answers.contentCreateMode || null,
|
|
contentCreateModeLabel: answers.contentCreateMode === 'self' ? 'Selbst erstellt' :
|
|
(answers.contentCreateMode === 'textservice' ? 'Textservice' : null),
|
|
letterText: answers.letterText || null,
|
|
|
|
// === MOTIV ===
|
|
motifNeeded: answers.motifNeed === true,
|
|
motifSource: answers.motifSource || null,
|
|
motifSourceLabel: answers.motifSource === 'upload' ? 'Eigenes Motiv hochgeladen' :
|
|
(answers.motifSource === 'printed' ? 'Bedruckte Karten verwenden' :
|
|
(answers.motifSource === 'design' ? 'Designservice' : null)),
|
|
motifFileName: answers.motifFileName || null,
|
|
motifFileMeta: answers.motifFileMeta || null,
|
|
|
|
// === SERVICES ===
|
|
serviceText: answers.serviceText === true,
|
|
serviceDesign: answers.serviceDesign === true,
|
|
serviceApi: answers.serviceApi === true,
|
|
|
|
// === FOLLOW-UP DETAILS (nur bei Follow-ups) ===
|
|
followupYearlyVolume: ctx.product?.isFollowUp ? (answers.followupYearlyVolume || null) : null,
|
|
followupCreateMode: ctx.product?.isFollowUp ? (answers.followupCreateMode || null) : null,
|
|
followupCreateModeLabel: ctx.product?.isFollowUp ? (
|
|
answers.followupCreateMode === 'auto' ? 'Automatisch (API)' :
|
|
(answers.followupCreateMode === 'manual' ? 'Manuell' : null)
|
|
) : null,
|
|
followupSourceSystem: ctx.product?.isFollowUp ? (answers.followupSourceSystem || null) : null,
|
|
followupTriggerDescription: ctx.product?.isFollowUp ? (answers.followupTriggerDescription || null) : null,
|
|
followupCheckCycle: ctx.product?.isFollowUp ? (answers.followupCheckCycle || null) : null,
|
|
followupCheckCycleLabel: ctx.product?.isFollowUp ? (
|
|
answers.followupCheckCycle === 'weekly' ? 'Wöchentlich' :
|
|
(answers.followupCheckCycle === 'monthly' ? 'Monatlich' :
|
|
(answers.followupCheckCycle === 'quarterly' ? 'Quartalsweise' : null))
|
|
) : null,
|
|
|
|
// === GUTSCHEIN ===
|
|
voucherCode: voucherCode,
|
|
voucherDiscount: voucherDiscount,
|
|
|
|
// === PREISE ===
|
|
currency: quote.currency || 'EUR',
|
|
subtotalNet: quote.subtotalNet || 0,
|
|
vatRate: quote.vatRate || 0.19,
|
|
vatAmount: quote.vatAmount || 0,
|
|
totalGross: quote.totalGross || 0,
|
|
priceLines: quote.lines || [],
|
|
|
|
// === KUNDENDATEN (Rechnungsadresse) ===
|
|
billingFirstName: order.billing?.firstName || '',
|
|
billingLastName: order.billing?.lastName || '',
|
|
billingCompany: order.billing?.company || '',
|
|
billingEmail: order.billing?.email || '',
|
|
billingPhone: order.billing?.phone || '',
|
|
billingStreet: order.billing?.street || '',
|
|
billingHouseNumber: order.billing?.houseNumber || '',
|
|
billingZip: order.billing?.zip || '',
|
|
billingCity: order.billing?.city || '',
|
|
billingCountry: order.billing?.country || 'Deutschland',
|
|
|
|
// === LIEFERADRESSE (falls abweichend) ===
|
|
shippingDifferent: order.shippingDifferent || false,
|
|
shippingFirstName: order.shippingDifferent ? (order.shipping?.firstName || '') : null,
|
|
shippingLastName: order.shippingDifferent ? (order.shipping?.lastName || '') : null,
|
|
shippingCompany: order.shippingDifferent ? (order.shipping?.company || '') : null,
|
|
shippingStreet: order.shippingDifferent ? (order.shipping?.street || '') : null,
|
|
shippingHouseNumber: order.shippingDifferent ? (order.shipping?.houseNumber || '') : null,
|
|
shippingZip: order.shippingDifferent ? (order.shipping?.zip || '') : null,
|
|
shippingCity: order.shippingDifferent ? (order.shipping?.city || '') : null,
|
|
shippingCountry: order.shippingDifferent ? (order.shipping?.country || 'Deutschland') : null,
|
|
|
|
// === EMPFÄNGERLISTE ===
|
|
addressMode: addressMode,
|
|
addressModeLabel: addressMode === 'free' ? 'Freie Adresszeilen' : 'Klassische Adresse',
|
|
recipients: addressMode === 'classic' ? (state.recipientRows || []).map((r, i) => ({
|
|
index: i,
|
|
firstName: r?.firstName || '',
|
|
lastName: r?.lastName || '',
|
|
street: r?.street || '',
|
|
houseNumber: r?.houseNumber || '',
|
|
zip: r?.zip || '',
|
|
city: r?.city || '',
|
|
country: r?.country || 'Deutschland',
|
|
})) : null,
|
|
recipientsFree: addressMode === 'free' ? (state.freeAddressRows || []).map((r, i) => ({
|
|
index: i,
|
|
line1: r?.line1 || '',
|
|
line2: r?.line2 || '',
|
|
line3: r?.line3 || '',
|
|
line4: r?.line4 || '',
|
|
line5: r?.line5 || '',
|
|
})) : null,
|
|
|
|
// === PLATZHALTER ===
|
|
placeholdersEnvelope: state.placeholders?.envelope || [],
|
|
placeholdersLetter: state.placeholders?.letter || [],
|
|
placeholderValues: state.placeholderValues || {},
|
|
|
|
// === BACKEND RESULT (falls vorhanden) ===
|
|
backendPath: backendResult?.path || null,
|
|
backendFiles: backendResult?.files || [],
|
|
backendSummary: backendResult?.summary || null,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Bereitet Metadaten für Backend vor
|
|
*/
|
|
function prepareOrderMetadata(state) {
|
|
return {
|
|
customer: {
|
|
type: state.answers?.customerType || 'private',
|
|
firstName: state.order?.firstName || '',
|
|
lastName: state.order?.lastName || '',
|
|
company: state.order?.company || '',
|
|
email: state.order?.email || '',
|
|
phone: state.order?.phone || '',
|
|
street: state.order?.street || '',
|
|
zip: state.order?.zip || '',
|
|
city: state.order?.city || '',
|
|
},
|
|
orderDate: new Date().toISOString(),
|
|
product: state.ctx?.product?.key || '',
|
|
quantity: state.answers?.quantity || 1,
|
|
format: state.answers?.format || 'A4',
|
|
shippingMode: state.answers?.shippingMode || 'direct',
|
|
quote: state.quote || {},
|
|
voucherCode: state.order?.voucherCode || null,
|
|
};
|
|
}
|
|
|
|
/**
|
|
* Erweiterte Order-Submit Funktion mit Backend-Integration
|
|
*/
|
|
export async function handleOrderSubmitWithBackend(state) {
|
|
const isB2B = state.answers?.customerType === "business";
|
|
const backend = window.SkriftConfigurator?.settings?.backend_connection || {};
|
|
const webhookUrl = backend.order_webhook_url;
|
|
const redirectUrlBusiness = backend.redirect_url_business;
|
|
const redirectUrlPrivate = backend.redirect_url_private;
|
|
const api = window.SkriftBackendAPI;
|
|
|
|
// Prüfe ob Backend konfiguriert ist
|
|
if (!backend.api_url) {
|
|
console.warn('[Backend Integration] Backend API URL nicht konfiguriert');
|
|
// Fallback zur alten Logik
|
|
return handleOrderSubmitLegacy(state);
|
|
}
|
|
|
|
try {
|
|
// 1. Backend Health Check
|
|
const isHealthy = await api.healthCheck();
|
|
if (!isHealthy) {
|
|
throw new Error('Backend ist nicht erreichbar');
|
|
}
|
|
|
|
// 2. Bestellnummer generieren (fortlaufend vom WP-Backend)
|
|
const orderNumber = await api.generateOrderNumber();
|
|
|
|
// 3. Daten vorbereiten
|
|
const letters = prepareLettersForBackend(state);
|
|
const envelopes = prepareEnvelopesForBackend(state);
|
|
const metadata = prepareOrderMetadata(state);
|
|
|
|
console.log('[Backend Integration] Generating order:', {
|
|
orderNumber,
|
|
letters,
|
|
envelopes,
|
|
metadata,
|
|
});
|
|
|
|
// 4. Order im Backend generieren
|
|
const result = await api.generateOrder(
|
|
orderNumber,
|
|
letters,
|
|
envelopes,
|
|
metadata
|
|
);
|
|
|
|
if (!result.success) {
|
|
throw new Error(result.error || 'Order generation failed');
|
|
}
|
|
|
|
console.log('[Backend Integration] Order generated successfully:', result);
|
|
|
|
// 5. Gutschein als verwendet markieren (falls vorhanden)
|
|
const voucherCode = state.order?.voucherStatus?.valid
|
|
? state.order.voucherCode
|
|
: null;
|
|
if (voucherCode) {
|
|
try {
|
|
const restUrl = window.SkriftConfigurator?.restUrl || "/wp-json/";
|
|
await fetch(restUrl + "skrift/v1/voucher/use", {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({ code: voucherCode }),
|
|
});
|
|
} catch (error) {
|
|
console.warn('[Backend Integration] Fehler beim Markieren des Gutscheins:', error);
|
|
}
|
|
}
|
|
|
|
// 6. Webhook aufrufen (wenn konfiguriert)
|
|
if (webhookUrl) {
|
|
try {
|
|
const webhookData = buildWebhookData(state, result);
|
|
|
|
const response = await fetch(webhookUrl, {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify(webhookData),
|
|
});
|
|
|
|
if (!response.ok) {
|
|
console.warn('[Backend Integration] Webhook call failed:', response.statusText);
|
|
}
|
|
} catch (error) {
|
|
console.warn('[Backend Integration] Webhook error:', error);
|
|
}
|
|
}
|
|
|
|
// 7. Weiterleitung
|
|
if (isB2B) {
|
|
if (redirectUrlBusiness) {
|
|
// Bestellnummer als Query-Parameter anhängen
|
|
const redirectUrl = new URL(redirectUrlBusiness);
|
|
redirectUrl.searchParams.set('orderNumber', result.orderNumber);
|
|
window.location.href = redirectUrl.toString();
|
|
} else {
|
|
alert(
|
|
`Vielen Dank für Ihre Bestellung!\n\nBestellnummer: ${result.orderNumber}\n\nSie erhalten in Kürze eine Bestätigungs-E-Mail mit allen Details.`
|
|
);
|
|
}
|
|
} else {
|
|
// Privatkunde: Zu PayPal weiterleiten
|
|
if (redirectUrlPrivate) {
|
|
const redirectUrl = new URL(redirectUrlPrivate);
|
|
redirectUrl.searchParams.set('orderNumber', result.orderNumber);
|
|
window.location.href = redirectUrl.toString();
|
|
} else {
|
|
alert(
|
|
`Bestellung erfolgreich erstellt!\n\nBestellnummer: ${result.orderNumber}\n\nWeiterleitung zu PayPal folgt...`
|
|
);
|
|
}
|
|
}
|
|
|
|
} catch (error) {
|
|
console.error('[Backend Integration] Order submission failed:', error);
|
|
|
|
alert(
|
|
`Fehler bei der Bestellverarbeitung:\n\n${error.message}\n\nBitte versuchen Sie es erneut oder kontaktieren Sie uns.`
|
|
);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Legacy Order-Submit (Fallback ohne Backend)
|
|
*/
|
|
async function handleOrderSubmitLegacy(state) {
|
|
const isB2B = state.answers?.customerType === "business";
|
|
const backend = window.SkriftConfigurator?.settings?.backend_connection || {};
|
|
const webhookUrl = backend.order_webhook_url;
|
|
const redirectUrlBusiness = backend.redirect_url_business;
|
|
const redirectUrlPrivate = backend.redirect_url_private;
|
|
|
|
// Gutschein als verwendet markieren
|
|
const voucherCode = state.order?.voucherStatus?.valid
|
|
? state.order.voucherCode
|
|
: null;
|
|
if (voucherCode) {
|
|
try {
|
|
const restUrl = window.SkriftConfigurator?.restUrl || "/wp-json/";
|
|
await fetch(restUrl + "skrift/v1/voucher/use", {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({ code: voucherCode }),
|
|
});
|
|
} catch (error) {
|
|
console.warn('Fehler beim Markieren des Gutscheins:', error);
|
|
}
|
|
}
|
|
|
|
// Webhook aufrufen
|
|
if (isB2B && webhookUrl) {
|
|
try {
|
|
const webhookData = buildWebhookData(state, null);
|
|
|
|
await fetch(webhookUrl, {
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify(webhookData),
|
|
});
|
|
} catch (error) {
|
|
console.warn('Webhook error:', error);
|
|
}
|
|
}
|
|
|
|
// Weiterleitung
|
|
if (isB2B && redirectUrlBusiness) {
|
|
window.location.href = redirectUrlBusiness;
|
|
} else if (!isB2B && redirectUrlPrivate) {
|
|
window.location.href = redirectUrlPrivate;
|
|
} else {
|
|
alert("Vielen Dank für Ihre Bestellung!");
|
|
}
|
|
}
|
|
|
|
export default {
|
|
handleOrderSubmitWithBackend,
|
|
prepareLettersForBackend,
|
|
prepareEnvelopesForBackend,
|
|
mapFontToBackend,
|
|
mapFormatToBackend,
|
|
buildWebhookData,
|
|
};
|