From f099baadb0faea390d14e461307e5953dd585bed Mon Sep 17 00:00:00 2001 From: sardar umer Date: Tue, 20 Jun 2023 12:44:08 +0500 Subject: [PATCH 1/7] dashboarddummy stats --- .circleci/config.yml | 0 .eslintrc.cjs | 0 .github/ISSUE_TEMPLATE/bug.md | 0 .github/ISSUE_TEMPLATE/config.yml | 0 .github/ISSUE_TEMPLATE/corework.md | 0 .github/ISSUE_TEMPLATE/questions.md | 0 .github/pull_request_template.md | 0 .gitignore | 0 .nvmrc | 0 LICENSE | 0 README.md | 0 babel.config.cjs | 0 index.js | 0 jest.config.cjs | 0 package-lock.json | 4 +- package.json | 8 +- src/i18n/ar.json | 97 ++ src/i18n/bg.json | 97 ++ src/i18n/cs.json | 97 ++ src/i18n/de.json | 203 +++ src/i18n/el.json | 97 ++ src/i18n/en.json | 203 +++ src/i18n/es.json | 203 +++ src/i18n/fr.json | 97 ++ src/i18n/he.json | 47 + src/i18n/hr.json | 97 ++ src/i18n/hu.json | 97 ++ src/i18n/index.js | 54 + src/i18n/it.json | 97 ++ src/i18n/my.json | 97 ++ src/i18n/nb.json | 36 + src/i18n/nl.json | 97 ++ src/i18n/pl.json | 97 ++ src/i18n/pt.json | 196 +++ src/i18n/ro.json | 97 ++ src/i18n/ru.json | 97 ++ src/i18n/sl.json | 97 ++ src/i18n/sv.json | 97 ++ src/i18n/tr.json | 97 ++ src/i18n/vi.json | 97 ++ src/i18n/zh.json | 97 ++ src/index.js | 25 +- src/preStartup.js | 11 + .../__snapshots__/refunds.test.js.snap | 7 + .../refundsByPaymentId.test.js.snap | 11 + src/queries/dashBoardStats.js | 13 + src/queries/index.js | 4 + .../Query/dashBoardShoppingActivity.js | 19 + src/resolvers/Query/earningDataGraph.js | 23 + src/resolvers/Query/index.js | 10 + src/resolvers/Query/productDataGraph.js | 26 + src/resolvers/Query/productsUploaded.js | 29 + src/resolvers/index.js | 5 + src/schemas/index.js | 5 + src/schemas/schema.graphql | 41 + src/simpleSchemas.js | 1138 +++++++++++++++++ src/startup.js | 30 + src/tests/factory.js | 51 + ...verifyPaymentsMatchOrderTotal.test.js.snap | 3 + src/util/addInvoiceToGroup.js | 49 + src/util/addShipmentMethodToGroup.js | 64 + src/util/addTaxesToGroup.js | 48 + src/util/anonymousToken.js | 21 + .../buildOrderFulfillmentGroupFromInput.js | 81 ++ src/util/buildOrderItem.js | 145 +++ src/util/compareExpectedAndActualTotals.js | 23 + src/util/createNotification.js | 48 + src/util/generateUID.js | 25 + src/util/getDataForOrderEmail.js | 212 +++ src/util/getDataForOrderEmail.test.js | 284 ++++ src/util/getOrderQuery.js | 42 + src/util/getOrderQuery.test.js | 68 + src/util/getProductbyId.js | 19 + src/util/getSurchargesForGroup.js | 55 + src/util/sendOrderEmail.js | 55 + src/util/updateGroupStatusFromItemStatus.js | 20 + src/util/updateGroupTotals.js | 102 ++ src/util/verifyPaymentsMatchOrderTotal.js | 27 + .../verifyPaymentsMatchOrderTotal.test.js | 29 + src/util/xformOrderGroupToCommonOrder.js | 127 ++ src/xforms/id.js | 50 + ...formOrderFulfillmentGroupSelectedOption.js | 26 + src/xforms/xformOrderItems.js | 24 + 83 files changed, 5776 insertions(+), 19 deletions(-) mode change 100644 => 100755 .circleci/config.yml mode change 100644 => 100755 .eslintrc.cjs mode change 100644 => 100755 .github/ISSUE_TEMPLATE/bug.md mode change 100644 => 100755 .github/ISSUE_TEMPLATE/config.yml mode change 100644 => 100755 .github/ISSUE_TEMPLATE/corework.md mode change 100644 => 100755 .github/ISSUE_TEMPLATE/questions.md mode change 100644 => 100755 .github/pull_request_template.md mode change 100644 => 100755 .gitignore mode change 100644 => 100755 .nvmrc mode change 100644 => 100755 LICENSE mode change 100644 => 100755 README.md mode change 100644 => 100755 babel.config.cjs mode change 100644 => 100755 index.js mode change 100644 => 100755 jest.config.cjs mode change 100644 => 100755 package-lock.json mode change 100644 => 100755 package.json create mode 100755 src/i18n/ar.json create mode 100755 src/i18n/bg.json create mode 100755 src/i18n/cs.json create mode 100755 src/i18n/de.json create mode 100755 src/i18n/el.json create mode 100755 src/i18n/en.json create mode 100755 src/i18n/es.json create mode 100755 src/i18n/fr.json create mode 100755 src/i18n/he.json create mode 100755 src/i18n/hr.json create mode 100755 src/i18n/hu.json create mode 100755 src/i18n/index.js create mode 100755 src/i18n/it.json create mode 100755 src/i18n/my.json create mode 100755 src/i18n/nb.json create mode 100755 src/i18n/nl.json create mode 100755 src/i18n/pl.json create mode 100755 src/i18n/pt.json create mode 100755 src/i18n/ro.json create mode 100755 src/i18n/ru.json create mode 100755 src/i18n/sl.json create mode 100755 src/i18n/sv.json create mode 100755 src/i18n/tr.json create mode 100755 src/i18n/vi.json create mode 100755 src/i18n/zh.json mode change 100644 => 100755 src/index.js create mode 100755 src/preStartup.js create mode 100755 src/queries/__snapshots__/refunds.test.js.snap create mode 100755 src/queries/__snapshots__/refundsByPaymentId.test.js.snap create mode 100755 src/queries/dashBoardStats.js create mode 100644 src/queries/index.js create mode 100755 src/resolvers/Query/dashBoardShoppingActivity.js create mode 100644 src/resolvers/Query/earningDataGraph.js create mode 100755 src/resolvers/Query/index.js create mode 100644 src/resolvers/Query/productDataGraph.js create mode 100644 src/resolvers/Query/productsUploaded.js create mode 100755 src/resolvers/index.js create mode 100755 src/schemas/index.js create mode 100755 src/schemas/schema.graphql create mode 100755 src/simpleSchemas.js create mode 100755 src/startup.js create mode 100755 src/tests/factory.js create mode 100755 src/util/__snapshots__/verifyPaymentsMatchOrderTotal.test.js.snap create mode 100755 src/util/addInvoiceToGroup.js create mode 100755 src/util/addShipmentMethodToGroup.js create mode 100755 src/util/addTaxesToGroup.js create mode 100755 src/util/anonymousToken.js create mode 100755 src/util/buildOrderFulfillmentGroupFromInput.js create mode 100755 src/util/buildOrderItem.js create mode 100755 src/util/compareExpectedAndActualTotals.js create mode 100755 src/util/createNotification.js create mode 100755 src/util/generateUID.js create mode 100755 src/util/getDataForOrderEmail.js create mode 100755 src/util/getDataForOrderEmail.test.js create mode 100755 src/util/getOrderQuery.js create mode 100755 src/util/getOrderQuery.test.js create mode 100755 src/util/getProductbyId.js create mode 100755 src/util/getSurchargesForGroup.js create mode 100755 src/util/sendOrderEmail.js create mode 100755 src/util/updateGroupStatusFromItemStatus.js create mode 100755 src/util/updateGroupTotals.js create mode 100755 src/util/verifyPaymentsMatchOrderTotal.js create mode 100755 src/util/verifyPaymentsMatchOrderTotal.test.js create mode 100755 src/util/xformOrderGroupToCommonOrder.js create mode 100755 src/xforms/id.js create mode 100755 src/xforms/xformOrderFulfillmentGroupSelectedOption.js create mode 100755 src/xforms/xformOrderItems.js diff --git a/.circleci/config.yml b/.circleci/config.yml old mode 100644 new mode 100755 diff --git a/.eslintrc.cjs b/.eslintrc.cjs old mode 100644 new mode 100755 diff --git a/.github/ISSUE_TEMPLATE/bug.md b/.github/ISSUE_TEMPLATE/bug.md old mode 100644 new mode 100755 diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml old mode 100644 new mode 100755 diff --git a/.github/ISSUE_TEMPLATE/corework.md b/.github/ISSUE_TEMPLATE/corework.md old mode 100644 new mode 100755 diff --git a/.github/ISSUE_TEMPLATE/questions.md b/.github/ISSUE_TEMPLATE/questions.md old mode 100644 new mode 100755 diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md old mode 100644 new mode 100755 diff --git a/.gitignore b/.gitignore old mode 100644 new mode 100755 diff --git a/.nvmrc b/.nvmrc old mode 100644 new mode 100755 diff --git a/LICENSE b/LICENSE old mode 100644 new mode 100755 diff --git a/README.md b/README.md old mode 100644 new mode 100755 diff --git a/babel.config.cjs b/babel.config.cjs old mode 100644 new mode 100755 diff --git a/index.js b/index.js old mode 100644 new mode 100755 diff --git a/jest.config.cjs b/jest.config.cjs old mode 100644 new mode 100755 diff --git a/package-lock.json b/package-lock.json old mode 100644 new mode 100755 index bbd8f6e..98fc2ae --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { - "name": "@reactioncommerce/api-plugin-example", - "version": "0.0.0-development", + "name": "@umerjaved1/api-plugin-dashboard-stats", + "version": "1.0.0-development", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json old mode 100644 new mode 100755 index f468272..fcf6dff --- a/package.json +++ b/package.json @@ -1,12 +1,12 @@ { - "name": "@reactioncommerce/api-plugin-example", + "name": "@codistan-isb/api-plugin-dashboard-stats", "description": "Boilerplate Example plugin for the Reaction API", "label": "Example Plugin", - "version": "0.0.0-development", + "version": "1.0.0-development", "main": "index.js", "type": "module", "engines": { - "node": ">=14.18.1" + "node": ">=12.14.1" }, "homepage": "https://github.com/reactioncommerce/api-plugin-example", "url": "https://github.com/reactioncommerce/api-plugin-example", @@ -81,4 +81,4 @@ "publishConfig": { "access": "public" } -} +} \ No newline at end of file diff --git a/src/i18n/ar.json b/src/i18n/ar.json new file mode 100755 index 0000000..d385cd1 --- /dev/null +++ b/src/i18n/ar.json @@ -0,0 +1,97 @@ +[{ + "i18n": "ar", + "ns": "reaction-orders", + "translation": { + "reaction-orders": { + "notFound": "لم يتم العثور على الطلب", + "order": { + "applyRefundToThisOrder": "هل تريد استرداد مبلغ {{currencySymbol}}{{refund}} لهذا الطلب؟", + "applyRefundDuringCancelOrder": "سيؤدي هذا إلى رد المبلغ الإجمالي للطلب البالغ {{currencySymbol}}{{invoiceTotal}}", + "cancelOrder": "هل تريد إلغاء هذا الطلب؟", + "cancelOrderLabel": "الغاء الطلب", + "cancelOrderThenRestock": "نعم، وإعادة تخزين", + "cancelOrderNoRestock": "نعم، ولكن لا إعادة تخزين" + }, + "admin": { + "shortcut": { + "ordersLabel": "أوامر" + }, + "orderRisk": { + "high": "مخاطرة عالية", + "riskCapture": "أمر القبض مع تهمة المخاطر؟", + "riskCaptureWarn": "كنت على وشك التقاط النظام مع خطر تهمة. تأكيد قبل المتابعة." + }, + "dashboard": { + "ordersLabel": "أوامر", + "ordersTitle": "أوامر", + "ordersDescription": "تلبية طلباتكم", + "clearSearch": "صافي" + }, + "orderWorkflow": { + "ordersList": { + "date": "التاريخ", + "orderId": "بطاقة تعريف الطلب ", + "total": "جميع", + "emailNotFound": "البريد الإلكتروني غير متوفر" + }, + "summary": { + "cardTitle": "ملخص", + "copyOrderLink": "نسخ رابط الطلب" + }, + "invoice": { + "cardTitle": "فاتورة", + "adjustedTotal": "إجمالي المعدل", + "capturedTotal": "الإجمالي المقبوض", + "refund": "إعادة مال", + "refundLabel": "لاسترداد الأموال", + "refundItem": "العناصر", + "refundItemAmount": "جميع", + "refundTotal": "رد المبلغ الإجمالي" + }, + "fulfillment": "تلبية الطلب", + "orderDetails": "تفاصيل الطلب", + "shipmentTracking": "شحنة" + }, + "table": { + "headers": { + "name": "اسم", + "email": "إعدادات البريد الإلكتروني", + "date": "التاريخ", + "id": "هوية شخصية", + "total": "جميع", + "shipping": "الشحن", + "status": "حالة" + }, + "filter": { + "status": "حالة", + "dateRange": "نطاق الموعد", + "shippingStatus": "حالة الشحن", + "new": "جديد", + "captured": "تم تحصيل الدفعة", + "shipped": "تم الشحن", + "completed": "مكتمل", + "canceled": "تم الإلغاء", + "refunded": "المستردة" + }, + "search": { + "clearSearch": "صافي", + "placeholder": "تقسيمات البحث" + }, + "data": { + "status": { + "new": "الجديد", + "coreOrderWorkflow/created": "خلقت", + "coreOrderWorkflow/processing": "معالجة", + "coreOrderWorkflow/completed": "منجز", + "coreOrderWorkflow/canceled": "ألغيت", + "coreOrderWorkflow/picked": "اختار", + "coreOrderWorkflow/packed": "معباه", + "coreOrderWorkflow/labeled": "المسمى", + "coreOrderWorkflow/shipped": "شحنها" + } + } + } + } + } + } +}] diff --git a/src/i18n/bg.json b/src/i18n/bg.json new file mode 100755 index 0000000..b60fa3b --- /dev/null +++ b/src/i18n/bg.json @@ -0,0 +1,97 @@ +[{ + "i18n": "bg", + "ns": "reaction-orders", + "translation": { + "reaction-orders": { + "notFound": "Поръчката не е намерена", + "order": { + "applyRefundToThisOrder": "Нанесете възстановяване на {{currencySymbol}}{{refund}} до тази цел?", + "applyRefundDuringCancelOrder": "Това ще възстанови общата сума на поръчката на {{currencySymbol}}{{invoiceTotal}}", + "cancelOrder": "Искате ли да анулирате тази поръчка?", + "cancelOrderLabel": "Отмяна на поръчката", + "cancelOrderThenRestock": "Да, и отново", + "cancelOrderNoRestock": "Да, но без зареждане" + }, + "admin": { + "shortcut": { + "ordersLabel": "Поръчки" + }, + "orderRisk": { + "high": "Висок риск", + "riskCapture": "Поръчка за улавяне с риск?", + "riskCaptureWarn": "На път сте да запишете поръчката с риск от зареждане. Потвърдете, преди да продължите." + }, + "dashboard": { + "ordersLabel": "Поръчки", + "ordersTitle": "Поръчки", + "ordersDescription": "Изпълним вашите поръчки", + "clearSearch": "ясно" + }, + "orderWorkflow": { + "ordersList": { + "date": "Дата", + "orderId": "Поръчка ID", + "total": "Обща сума", + "emailNotFound": "Няма наличен имейл" + }, + "summary": { + "cardTitle": "обобщение", + "copyOrderLink": "Копиране на връзката по поръчка" + }, + "invoice": { + "cardTitle": "фактура", + "adjustedTotal": "Корегирана сума", + "capturedTotal": "Одобрена сума", + "refund": "Възстановяване", + "refundLabel": "За възстановяване на средства", + "refundItem": "Предмети", + "refundItemAmount": "Обща сума", + "refundTotal": "Общо възнаграждение" + }, + "fulfillment": "изпълняване", + "orderDetails": "подробности за поръчката", + "shipmentTracking": "пратка" + }, + "table": { + "headers": { + "name": "Име", + "email": "Email настройки", + "date": "Дата", + "id": "документ за самоличност", + "total": "Обща сума", + "shipping": "Доставка", + "status": "Статус" + }, + "filter": { + "status": "Статус", + "dateRange": "Период от време", + "shippingStatus": "Статус на доставка", + "new": "Нов", + "captured": "Плащането Заснето", + "shipped": "Изпратено", + "completed": "завършен", + "canceled": "отменен", + "refunded": "Възстановява" + }, + "search": { + "clearSearch": "ясно", + "placeholder": "Поръчки за търсене" + }, + "data": { + "status": { + "new": "нов", + "coreOrderWorkflow/created": "Създаден", + "coreOrderWorkflow/processing": "обработване", + "coreOrderWorkflow/completed": "завършен", + "coreOrderWorkflow/canceled": "отменен", + "coreOrderWorkflow/picked": "избран", + "coreOrderWorkflow/packed": "натъпкан", + "coreOrderWorkflow/labeled": "Етикетирани", + "coreOrderWorkflow/shipped": "Доставят" + } + } + } + } + } + } +}] diff --git a/src/i18n/cs.json b/src/i18n/cs.json new file mode 100755 index 0000000..19193d9 --- /dev/null +++ b/src/i18n/cs.json @@ -0,0 +1,97 @@ +[{ + "i18n": "cs", + "ns": "reaction-orders", + "translation": { + "reaction-orders": { + "notFound": "Objednávka nebyla nalezena", + "order": { + "applyRefundToThisOrder": "Uplatňovat vrácení {{currencySymbol}}{{refund}} na tomto pořadí?", + "applyRefundDuringCancelOrder": "Tím bude vrácena celková objednávka {{currencySymbol}}{{invoiceTotal}}", + "cancelOrder": "Chcete tuto objednávku zrušit?", + "cancelOrderLabel": "Zrušit objednávku", + "cancelOrderThenRestock": "Ano, a znovu", + "cancelOrderNoRestock": "Ano, ale žádné doplňování" + }, + "admin": { + "shortcut": { + "ordersLabel": "objednávky" + }, + "orderRisk": { + "high": "Vysoké riziko", + "riskCapture": "Zachyťte příkaz s rizikovým poplatkem?", + "riskCaptureWarn": "Chystáte se zachytit objednávku s rizikem poplatku. Potvrdit před pokračováním." + }, + "dashboard": { + "ordersLabel": "objednávky", + "ordersTitle": "objednávky", + "ordersDescription": "Splnit Vaše objednávky", + "clearSearch": "Jasný" + }, + "orderWorkflow": { + "ordersList": { + "date": "Datum", + "orderId": "Číslo objednávky", + "total": "Celkem", + "emailNotFound": "E-mail není k dispozici" + }, + "summary": { + "cardTitle": "Souhrn", + "copyOrderLink": "Kopírovat odkaz na objednávku" + }, + "invoice": { + "cardTitle": "Faktura", + "adjustedTotal": "Upravený Total", + "capturedTotal": "zajatý Total", + "refund": "Vrácení platby", + "refundLabel": "Pro vrácení peněz", + "refundItem": "předměty", + "refundItemAmount": "Celkem", + "refundTotal": "Celkové vrácení peněz" + }, + "fulfillment": "Splnění", + "orderDetails": "Podrobnosti o objednávce", + "shipmentTracking": "Zásilka" + }, + "table": { + "headers": { + "name": "Název", + "email": "Email", + "date": "Datum", + "id": "ID", + "total": "Celkem", + "shipping": "lodní", + "status": "Postavení" + }, + "filter": { + "status": "Postavení", + "dateRange": "Časové období", + "shippingStatus": "Stav odeslání", + "new": "Nový", + "captured": "platba Zachycená", + "shipped": "Posíláme", + "completed": "Dokončeno", + "canceled": "Zrušeno", + "refunded": "vrácena" + }, + "search": { + "clearSearch": "Jasný", + "placeholder": "Vyhledávací příkazy" + }, + "data": { + "status": { + "new": "Nový", + "coreOrderWorkflow/created": "Vytvořeno", + "coreOrderWorkflow/processing": "zpracovává se", + "coreOrderWorkflow/completed": "Dokončeno", + "coreOrderWorkflow/canceled": "Zrušeno", + "coreOrderWorkflow/picked": "Vybrali jste", + "coreOrderWorkflow/packed": "Balené", + "coreOrderWorkflow/labeled": "Označeno", + "coreOrderWorkflow/shipped": "Odesláno" + } + } + } + } + } + } +}] diff --git a/src/i18n/de.json b/src/i18n/de.json new file mode 100755 index 0000000..b2abd4b --- /dev/null +++ b/src/i18n/de.json @@ -0,0 +1,203 @@ +[ + { + "i18n": "de", + "ns": "reaction-orders", + "translation": { + "reaction-orders": { + "calculateRefundByItem": "Rückerstattung pro Artikel berechnen", + "fulfillment": "Abwicklung", + "fulfillmentGroupHeader": "Abwicklungsgruppe {{currentGroupCount}} von {{totalGroupsCount}}", + "items": "Positionen", + "newStatus": "Neuer Status", + "notFound": "Auftrag nicht gefunden", + "refunds": "Rückerstattungen", + "orderActions": { + "markAsPackedDescription": "Alle Positionen der Abwicklungsgruppe als \"Verpackt\" markieren", + "markAsPackedLabel": "Als verpackt markieren", + "markAsShippedDescription": "Alle Positionen der Abwicklungsgruppe als \"Versendet\" markieren", + "markAsShippedLabel": "Als versendet markieren", + "updateGroupStatus": "Gruppenstatus ändern" + }, + "orderDetails": "Auftragsdetails", + "order": { + "allowShippingRefund": "Rückerstattung der Versandkosten erlauben", + "allPaymentsRefunded": "Alle Zahlungen wurden vollständig erstattet", + "amountToRefund": "Zu erstattender Betrag", + "applyRefund": "Rückerstattung ausführen", + "applyRefundDuringCancelOrder": "Damit wird die Auftragssumme von {{invoiceTotal}}{{currencySymbol}} zurückerstattet", + "applyRefundToThisOrder": "Rückerstattung von {{refund}}{{currencySymbol}} für diese Bestellung ausführen?", + "availableToRefund": "Für Rückerstattung verfügbar", + "cancelGroup": "Möchten Sie diese Abwicklungsgruppe stornieren?", + "cancelGroupLabel": "Abwicklungsgruppe stornieren", + "cancelOrder": "Möchten Sie diese Bestellung stornieren?", + "cancelOrderLabel": "Bestellung stornieren", + "cancelOrderNoRestock": "In den Bestand übernehmen", + "cancelOrderThenRestock": "Nicht in den Bestand übernehmen", + "noRefundReason": "Keine Begründung", + "order": "Bestellung", + "paymentRefunded": "Zahlung vollständig erstattet", + "placed": "Beauftragt am", + "previousRefunds": "Erfolgte Rückerstattungen", + "previouslyRefunded": "Bereits erstatteter Betrag", + "reason": "Grund", + "reasonForRefund": "Grund der Rückerstattung", + "reasonForRefundFormLabel": "Grund der Rückerstattung (optional)", + "refund": "Erstatten", + "refundButton": "{{currentRefundAmount}} erstatten", + "refundedTo": "Erstattet an", + "refunding": "Wird erstattet", + "refundItemsAlert": "Möchten Sie {{refundItemsTotal}} für {{refundItemsQuantity}} Artikel erstatten?", + "refundItemsApproveAlert": "Möchten Sie die Zahlung autorisieren und danach {{totalAmount}} erstatten?", + "refundItemsCaptureAlert": "Möchten Sie die Zahlung einziehen und danach {{totalAmount}} erstatten?", + "refundItemsSuccess": "Rückerstattung erfolgreich ausgeführt", + "refundItemsTitle": "Artikel erstatten", + "refundItemsWait": "Zahlung wurde noch nicht eingezogen", + "refundNotSupported": "Rückerstattungen werden bei dieser Zahlungsart nicht unterstützt", + "refundsNotSupported": "Rückerstattungen werden bei der/den Zahlungsart(en) dieser Bestellung nicht unterstützt", + "refundReason": { + "customerRequest": "Kundenanfrage", + "duplicatePayment": "Doppelte Zahlung", + "fraudulent": "Betrug" + }, + "refundTo": "Erstatten an", + "restockInventory": "In den Bestand zurückführen?", + "updateFulfillmentGroupStatusMessage": "Möchten Sie den Status für diese Abwicklungsgruppe in {{groupStatus}} ändern?", + "updateFulfillmentGroupStatusTitle": "Abwicklungsgruppenstatus ändern" + }, + "status": { + "new": "Neu", + "coreOrderWorkflow/created": "Erstellt", + "coreOrderWorkflow/processing": "wird bearbeitet", + "coreOrderWorkflow/completed": "Abgeschlossen", + "coreOrderWorkflow/canceled": "Storniert", + "coreOrderWorkflow/picked": "Kommissioniert", + "coreOrderWorkflow/packed": "Verpackt", + "coreOrderWorkflow/labeled": "Beschriftet", + "coreOrderWorkflow/shipped": "Versendet", + "multipleStatuses": "Mehrere Status" + }, + "orderCard": { + "orderSummary": { + "showOrderSummary": "Zusammenfassung anzeigen", + "title": "Zusammenfassung" + } + }, + "shippingAddress": "Lieferadresse", + "shippingMethod": "Versandart", + "trackingNumber": "Sendungsverfolgungsnummer", + "admin": { + "shortcut": { + "ordersLabel": "Bestellungen" + }, + "orderRisk": { + "high": "Hohes Risiko", + "riskCapture": "Zahlung mit Risiko einziehen?", + "riskCaptureWarn": "Sie sind dabei, eine Zahlung mit einem Zahlungsrisiko einzuziehen. Bestätigen Sie dies." + }, + "dashboard": { + "ordersLabel": "Bestellungen", + "ordersTitle": "Bestellungen", + "ordersDescription": "Bestellungen abwickeln", + "clearSearch": "Zurücksetzen" + }, + "orderWorkflow": { + "ordersList": { + "date": "Datum", + "orderId": "Bestellnummer", + "total": "Summe", + "emailNotFound": "E-Mail-Adresse nicht verfügbar" + }, + "summary": { + "cardTitle": "Zusammenfassung", + "copyOrderLink": "Link kopieren" + }, + "invoice": { + "cardTitle": "Rechnung", + "adjustedTotal": "Angepasste Summe", + "capturedTotal": "Eingezogen", + "printInvoice": "Rechnung drucken", + "refund": "Erstattet", + "refundLabel": "Erstattbar", + "refundItem": "Artikel", + "refundItemAmount": "Summe", + "refundTotal": "Rückerstattung Gesamt" + }, + "fulfillment": "Abwicklung", + "orderDetails": "Bestellungs-Details", + "shipmentTracking": "Versand", + "fulfillmentGroups": { + "cancelFulfillmentGroup": "Abwicklungsgruppe stornieren", + "markAsPacked": "Als verpackt markieren", + "printShippingLabel": "Adressetikett drucken" + } + }, + "table": { + "headers": { + "customer": "Kunde", + "name": "Name", + "payment": "Zahlung", + "fulfillment": "Abwicklung", + "email": "E-Mail", + "date": "Datum", + "id": "Bestellnummer", + "total": "Summe", + "shipping": "Versand", + "status": "Status" + }, + "error": "Es ist ein Fehler aufgetreten", + "filter": { + "canceled": "Storniert", + "captured": "Zahlung eingezogen", + "completed": "Abgeschlossen", + "dateRange": "Datumsbereich", + "globalFilter": "Bestellungen filtern", + "last30": "Letzte 30 Tage", + "last7": "Letzte 7 Tage", + "new": "Neu", + "refunded": "Erstattet", + "shipped": "Versendet", + "shippingStatus": "Versandstatus", + "status": "Status", + "today": "Heute" + }, + "search": { + "clearSearch": "Zurücksetzen", + "placeholder": "Bestellungen durchsuchen" + }, + "orderStatus": { + "coreOrderWorkflow/canceled": "Storniert", + "coreOrderWorkflow/completed": "Abgeschlossen", + "new": "Neu", + "coreOrderWorkflow/processing": "In Bearbeitung" + }, + "paymentStatus": { + "completed": "Abgeschlossen", + "created": "Angelegt" + }, + "fulfillmentStatus": { + "coreOrderWorkflow/completed": "Abgeschlossen", + "coreOrderWorkflow/created": "Anelegt", + "coreOrderWorkflow/canceled": "Storniert", + "new": "Neu", + "coreOrderWorkflow/processing": "In Bearbeitung" + }, + "data": { + "status": { + "new": "Neu", + "coreOrderWorkflow/created": "Erstellt", + "coreOrderWorkflow/processing": "In Bearbeitung", + "coreOrderWorkflow/completed": "Abgeschlossen", + "coreOrderWorkflow/canceled": "Storniert", + "coreOrderWorkflow/picked": "Kommissioniert", + "coreOrderWorkflow/packed": "Verpackt", + "coreOrderWorkflow/labeled": "Beschriftet", + "coreOrderWorkflow/shipped": "Versendet", + "multipleStatuses": "Mehrere Status" + } + } + } + } + } + } + } +] diff --git a/src/i18n/el.json b/src/i18n/el.json new file mode 100755 index 0000000..a2e1f1d --- /dev/null +++ b/src/i18n/el.json @@ -0,0 +1,97 @@ +[{ + "i18n": "el", + "ns": "reaction-orders", + "translation": { + "reaction-orders": { + "notFound": "Η παραγγελία δεν βρέθηκε", + "order": { + "applyRefundToThisOrder": "Εφαρμόστε επιστροφή του {{currencySymbol}}{{refund}} σε αυτή τη σειρά;", + "applyRefundDuringCancelOrder": "Αυτό θα επιστρέψει το σύνολο παραγγελίας του {{currencySymbol}}{{invoiceTotal}}", + "cancelOrder": "Θέλετε να ακυρώσετε αυτήν την παραγγελία;", + "cancelOrderLabel": "Ακύρωση παραγγελίας", + "cancelOrderThenRestock": "Ναι, και επαναλάβετε", + "cancelOrderNoRestock": "Ναι, αλλά χωρίς ανανέωση" + }, + "admin": { + "shortcut": { + "ordersLabel": "παραγγελίες" + }, + "orderRisk": { + "high": "Υψηλού κινδύνου", + "riskCapture": "Εντολή λήψης με χρέωση κινδύνου;", + "riskCaptureWarn": "Πρόκειται να συλλάβετε παραγγελία με κίνδυνο επιβάρυνσης. Επιβεβαιώστε πριν συνεχίσετε." + }, + "dashboard": { + "ordersLabel": "παραγγελίες", + "ordersTitle": "παραγγελίες", + "ordersDescription": "Εκπληρώσει τις παραγγελίες σας", + "clearSearch": "Σαφής" + }, + "orderWorkflow": { + "ordersList": { + "date": "Ημερομηνία", + "orderId": "Αριθμός Παραγγελίας", + "total": "Σύνολο", + "emailNotFound": "Το ηλεκτρονικό ταχυδρομείο δεν είναι διαθέσιμο" + }, + "summary": { + "cardTitle": "Περίληψη", + "copyOrderLink": "Αντιγραφή συνδέσμου παραγγελίας" + }, + "invoice": { + "cardTitle": "Τιμολόγιο", + "adjustedTotal": "Προσαρμοσμένο Σύνολο", + "capturedTotal": "συλλαμβάνονται Σύνολο", + "refund": "Επιστροφή χρημάτων", + "refundLabel": "Για επιστροφή χρημάτων", + "refundItem": "Είδη", + "refundItemAmount": "Σύνολο", + "refundTotal": "Επιστροφή Σύνολο" + }, + "fulfillment": "Εκπλήρωση", + "orderDetails": "Λεπτομέρειες Παραγγελίας", + "shipmentTracking": "Αποστολή" + }, + "table": { + "headers": { + "name": "Όνομα", + "email": "Ρυθμίσεις email", + "date": "Ημερομηνία", + "id": "ταυτότητα", + "total": "Σύνολο", + "shipping": "Αποστολή", + "status": "Κατάσταση" + }, + "filter": { + "status": "Κατάσταση", + "dateRange": "Εύρος ημερομηνιών", + "shippingStatus": "Κατάσταση αποστολής", + "new": "Νέος", + "captured": "πληρωμή Συλλαμβάνονται", + "shipped": "αποστέλλονται", + "completed": "Ολοκληρώθηκε το", + "canceled": "Ακυρώθηκε", + "refunded": "επιστρέφεται" + }, + "search": { + "clearSearch": "Σαφής", + "placeholder": "Εντολές αναζήτησης" + }, + "data": { + "status": { + "new": "Νέος", + "coreOrderWorkflow/created": "Δημιουργήθηκε", + "coreOrderWorkflow/processing": "Επεξεργασία", + "coreOrderWorkflow/completed": "Ολοκληρώθηκε το", + "coreOrderWorkflow/canceled": "Ακυρώθηκε", + "coreOrderWorkflow/picked": "Εκλεκτός", + "coreOrderWorkflow/packed": "Συσκευασμένα", + "coreOrderWorkflow/labeled": "Με την ένδειξη", + "coreOrderWorkflow/shipped": "Αποστέλλονται" + } + } + } + } + } + } +}] diff --git a/src/i18n/en.json b/src/i18n/en.json new file mode 100755 index 0000000..68902e5 --- /dev/null +++ b/src/i18n/en.json @@ -0,0 +1,203 @@ +[ + { + "i18n": "en", + "ns": "reaction-orders", + "translation": { + "reaction-orders": { + "calculateRefundByItem": "Calculate refund by item", + "fulfillment": "Fulfillment", + "fulfillmentGroupHeader": "Fulfillment group {{currentGroupCount}} of {{totalGroupsCount}}", + "items": "Items", + "newStatus": "New status", + "notFound": "Order Not Found", + "refunds": "Refunds", + "orderActions": { + "markAsPackedDescription": "Mark all items in this fulfillment group as \"Packed\"", + "markAsPackedLabel": "Mark as packed", + "markAsShippedDescription": "Mark all items in this fulfillment group as \"Shipped\"", + "markAsShippedLabel": "Mark as shipped", + "updateGroupStatus": "Update group status" + }, + "orderDetails": "Order Details", + "order": { + "allowShippingRefund": "Allow shipping to be refunded", + "allPaymentsRefunded": "All payments have been fully refunded", + "amountToRefund": "Amount to refund", + "applyRefund": "Apply refund", + "applyRefundDuringCancelOrder": "This will refund the order total of {{currencySymbol}}{{invoiceTotal}}", + "applyRefundToThisOrder": "Apply refund of {{refund}} to this order?", + "availableToRefund": "Available to refund", + "cancelGroup": "Do you want to cancel this group?", + "cancelGroupLabel": "Cancel group", + "cancelOrder": "Do you want to cancel this entire order?", + "cancelOrderLabel": "Cancel order", + "cancelOrderNoRestock": "Do not restock", + "cancelOrderThenRestock": "Restock", + "noRefundReason": "No reason provided", + "order": "Order", + "paymentRefunded": "Payment is fully refunded", + "placed": "Placed", + "previousRefunds": "Previous refunds", + "previouslyRefunded": "Previously refunded", + "reason": "Reason", + "reasonForRefund": "Reason for refund", + "reasonForRefundFormLabel": "Reason for refund (optional)", + "refund": "Refund", + "refundButton": "Refund {{currentRefundAmount}}", + "refundedTo": "Refunded to", + "refunding": "Refunding", + "refundItemsAlert": "Do you want to refund {{refundItemsQuantity}} item(s) totaling {{refundItemsTotal}}?", + "refundItemsApproveAlert": "Do you want to approve this payment and then refund {{totalAmount}}?", + "refundItemsCaptureAlert": "Do you want to capture this payment and then refund {{totalAmount}}?", + "refundItemsSuccess": "Refund was successfully applied", + "refundItemsTitle": "Refund Items", + "refundItemsWait": "Payment is not yet captured", + "refundNotSupported": "Payment method does not support refunds", + "refundsNotSupported": "Refunds are not supported with the payment method(s) used for this order", + "refundReason": { + "customerRequest": "Customer request", + "duplicatePayment": "Duplicate payment", + "fraudulent": "Fraudulent" + }, + "refundTo": "Refund to", + "restockInventory": "Restock inventory?", + "updateFulfillmentGroupStatusMessage": "Do you want to update this fulfillment group status to {{groupStatus}}", + "updateFulfillmentGroupStatusTitle": "Update group status" + }, + "status": { + "new": "New ", + "coreOrderWorkflow/created": "Created ", + "coreOrderWorkflow/processing": "Processing ", + "coreOrderWorkflow/completed": "Completed ", + "coreOrderWorkflow/canceled": "Canceled ", + "coreOrderWorkflow/picked": "Picked ", + "coreOrderWorkflow/packed": "Packed ", + "coreOrderWorkflow/labeled": "Labeled ", + "coreOrderWorkflow/shipped": "Shipped ", + "multipleStatuses": "Multiple statuses" + }, + "orderCard": { + "orderSummary": { + "showOrderSummary": "Show order summary", + "title": "Order summary" + } + }, + "shippingAddress": "Shipping address", + "shippingMethod": "Shipping method", + "trackingNumber": "Tracking number", + "admin": { + "shortcut": { + "ordersLabel": "Orders" + }, + "orderRisk": { + "high": "High Risk", + "riskCapture": "Capture Order with Risk Charge?", + "riskCaptureWarn": "You are about to capture order with a charge risk. Confirm before proceeding." + }, + "dashboard": { + "ordersLabel": "Orders", + "ordersTitle": "Orders", + "ordersDescription": "Fulfill your orders", + "clearSearch": "Clear" + }, + "orderWorkflow": { + "ordersList": { + "date": "Date", + "orderId": "Order ID", + "total": "Total", + "emailNotFound": "Email not available" + }, + "summary": { + "cardTitle": "Summary", + "copyOrderLink": "Copy Order Link" + }, + "invoice": { + "cardTitle": "Invoice", + "adjustedTotal": "Adjusted Total", + "capturedTotal": "Captured Total", + "printInvoice": "Print invoice", + "refund": "Refund", + "refundLabel": "For Refund", + "refundItem": "Items", + "refundItemAmount": "Total", + "refundTotal": "Refund Total" + }, + "fulfillment": "Fulfillment", + "orderDetails": "Order details", + "shipmentTracking": "Shipment", + "fulfillmentGroups": { + "cancelFulfillmentGroup": "Cancel group", + "markAsPacked": "Mark as packed", + "printShippingLabel": "Print shipping label" + } + }, + "table": { + "headers": { + "customer": "Customer", + "name": "Name", + "payment": "Payment", + "fulfillment": "Fulfillment", + "email": "Email", + "date": "Date", + "id": "Order ID", + "total": "Total", + "shipping": "Shipping", + "status": "Status" + }, + "error": "An error occurred", + "filter": { + "canceled": "Canceled", + "captured": "Captured", + "completed": "Completed", + "dateRange": "Date range", + "globalFilter": "Filter orders", + "last30": "Last 30 days", + "last7": "Last 7 days", + "new": "New", + "refunded": "Refunded", + "shipped": "Shipped", + "shippingStatus": "Shipping status", + "status": "Status", + "today": "Today" + }, + "search": { + "clearSearch": "Clear", + "placeholder": "Search orders" + }, + "orderStatus": { + "coreOrderWorkflow/canceled": "Canceled", + "coreOrderWorkflow/completed": "Completed", + "new": "New", + "coreOrderWorkflow/processing": "Processing" + }, + "paymentStatus": { + "completed": "Completed", + "created": "Created" + }, + "fulfillmentStatus": { + "coreOrderWorkflow/completed": "Completed", + "coreOrderWorkflow/created": "Created", + "coreOrderWorkflow/canceled": "Canceled", + "new": "New", + "coreOrderWorkflow/processing": "Processing" + }, + "data": { + "status": { + "new": "New ", + "coreOrderWorkflow/created": "Created ", + "coreOrderWorkflow/processing": "Processing ", + "coreOrderWorkflow/completed": "Completed ", + "coreOrderWorkflow/canceled": "Canceled ", + "coreOrderWorkflow/picked": "Picked ", + "coreOrderWorkflow/packed": "Packed ", + "coreOrderWorkflow/labeled": "Labeled ", + "coreOrderWorkflow/shipped": "Shipped ", + "multipleStatuses": "Multiple statuses" + } + } + } + } + } + } + } +] diff --git a/src/i18n/es.json b/src/i18n/es.json new file mode 100755 index 0000000..daac599 --- /dev/null +++ b/src/i18n/es.json @@ -0,0 +1,203 @@ +[ + { + "i18n": "es", + "ns": "reaction-orders", + "translation": { + "reaction-orders": { + "calculateRefundByItem": "Calcular reembolso por artículo", + "fulfillment": "Cumplimiento", + "fulfillmentGroupHeader": "Grupo de cumplimiento {{currentGroupCount}} de {{totalGroupsCount}}", + "items": "Artículos", + "newStatus": "Nuevo estado", + "notFound": "Orden no encontrada", + "refunds": "Reembolsos", + "orderActions": { + "markAsPackedDescription": "Marcar todos los artículos en este grupo como \"Empaquetado\"", + "markAsPackedLabel": "Marcar como empaquetado(s)", + "markAsShippedDescription": "Marcar todos los artículos en este grupo como \"Enviado\"", + "markAsShippedLabel": "Marcar como enviado(s)", + "updateGroupStatus": "Actualizar estado del grupo" + }, + "orderDetails": "Detalles de la Orden", + "order": { + "allowShippingRefund": "Permitir que se reembolse el envío", + "allPaymentsRefunded": "Todos los pagos han sido reembolsados en su totalidad", + "amountToRefund": "Monto a reembolsar", + "applyRefund": "Aplicar reembolso", + "applyRefundDuringCancelOrder": "Esto reembolsará el total de la orden de {{currencySymbol}}{{invoiceTotal}}", + "applyRefundToThisOrder": "¿Aplicar reembolso de {{refund}} a esta orden?", + "availableToRefund": "Disponible para reembolsar", + "cancelGroup": "¿Desea cancerar este grupo?", + "cancelGroupLabel": "¿Cancelar grupo?", + "cancelOrder": "¿Desea cancelar esta orden?", + "cancelOrderLabel": "Cancelar orden", + "cancelOrderNoRestock": "Sí, pero sin reabastecimiento", + "cancelOrderThenRestock": "Sí, y reabastecer", + "noRefundReason": "No se proporcionó ninguna razón", + "order": "Orden", + "paymentRefunded": "Pago totalmente reembolsado", + "placed": "Realizado", + "previousRefunds": "Reembolsos anteriores", + "previouslyRefunded": "Reembolsado anteriormente", + "reason": "Razón", + "reasonForRefund": "Razón para reembolsar", + "reasonForRefundFormLabel": "Razón para reembolsar (opcional)", + "refund": "Reembolsar", + "refundButton": "Reembolsar {{currentRefundAmount}}", + "refundedTo": "Reembolsado a", + "refunding": "Reembolsando", + "refundItemsAlert": "¿Desea reembolsar {{refundItemsQuantity}} artículo(s) con un total de {{refundItemsTotal}}?", + "refundItemsApproveAlert": "¿Quiere aprobar este pago y luego reembolsar {{totalAmount}}?", + "refundItemsCaptureAlert": "¿Quiere capturar este pago y luego reembolsar {{totalAmount}}?", + "refundItemsSuccess": "El reembolso se aplicó con éxito", + "refundItemsTitle": "Reembolsar Artículos", + "refundItemsWait": "El pago aún no se ha capturado", + "refundNotSupported": "El método de pago no admite reembolsos", + "refundsNotSupported": "Los reembolsos no son compatibles con el(los) método(s) de pago utilizado(s) para esta orden", + "refundReason": { + "customerRequest": "Solicitud del cliente", + "duplicatePayment": "Pago duplicado", + "fraudulent": "Fraudulento" + }, + "refundTo": "Reembolsar a", + "restockInventory": "¿Reabastecer inventario?", + "updateFulfillmentGroupStatusMessage": "¿Desea actualizar el estado de este grupo a {{groupStatus}}", + "updateFulfillmentGroupStatusTitle": "Actualizar estado de grupo" + }, + "status": { + "new": "Nuevo ", + "coreOrderWorkflow/created": "Creado ", + "coreOrderWorkflow/processing": "Procesando ", + "coreOrderWorkflow/completed": "Completado ", + "coreOrderWorkflow/canceled": "Cancelado ", + "coreOrderWorkflow/picked": "Recogido ", + "coreOrderWorkflow/packed": "Empaquetado ", + "coreOrderWorkflow/labeled": "Etiquetado ", + "coreOrderWorkflow/shipped": "Enviado ", + "multipleStatuses": "Múltiples estados" + }, + "orderCard": { + "orderSummary": { + "showOrderSummary": "Mostrar resumen de la orden", + "title": "Resumen de Orden" + } + }, + "shippingAddress": "Dirección de envío", + "shippingMethod": "Método de envío", + "trackingNumber": "Número de seguimiento", + "admin": { + "shortcut": { + "ordersLabel": "Órdenes" + }, + "orderRisk": { + "high": "Alto riesgo", + "riskCapture": "¿Capturar orden con riesgo de crédito?", + "riskCaptureWarn": "Usted está a punto de capturar la orden con un riesgo de crédito. Confirme antes de continuar." + }, + "dashboard": { + "ordersLabel": "Órdenes", + "ordersTitle": "Órdenes", + "ordersDescription": "Cumplir con sus órdenes", + "clearSearch": "Limpiar" + }, + "orderWorkflow": { + "ordersList": { + "date": "Fecha", + "orderId": "ID de orden", + "total": "Total", + "emailNotFound": "Correo electrónico no disponible" + }, + "summary": { + "cardTitle": "Resumen", + "copyOrderLink": "Copiar enlace de la orden" + }, + "invoice": { + "cardTitle": "Factura", + "adjustedTotal": "Total ajustado", + "capturedTotal": "Total capturado", + "printInvoice": "Imprimir factura", + "refund": "Reembolso", + "refundLabel": "Para reembolso", + "refundItem": "Artículos", + "refundItemAmount": "Total", + "refundTotal": "Total reembolso" + }, + "fulfillment": "Cumplimiento", + "orderDetails": "Detalles de la orden", + "shipmentTracking": "Envío", + "fulfillmentGroups": { + "cancelFulfillmentGroup": "Cancelar grupo", + "markAsPacked": "Marcar como empaquetado", + "printShippingLabel": "Imprimir etiqueta de envío" + } + }, + "table": { + "headers": { + "customer": "Cliente", + "name": "Nombre", + "payment": "Pago", + "fulfillment": "Cumplimiento", + "email": "Correo electrónico", + "date": "Fecha", + "id": "ID de orden", + "total": "Total", + "shipping": "Envío", + "status": "Estado" + }, + "error": "Ocurrió un error", + "filter": { + "canceled": "Cancelado", + "captured": "Pago capturado", + "completed": "Completado", + "dateRange": "Rango de fechas", + "globalFilter": "Filtrar órdenes", + "last30": "Últimos 30 días", + "last7": "Últimos 7 días", + "new": "Nuevo", + "refunded": "Reembolsado", + "shipped": "Enviado", + "shippingStatus": "Estado del envío", + "status": "Estado", + "today": "Hoy" + }, + "search": { + "clearSearch": "Limpiar", + "placeholder": "Buscar órdenes" + }, + "orderStatus": { + "coreOrderWorkflow/canceled": "Cancelado", + "coreOrderWorkflow/completed": "Completado", + "new": "Nuevo", + "coreOrderWorkflow/processing": "Procesando" + }, + "paymentStatus": { + "completed": "Completado", + "created": "Creado" + }, + "fulfillmentStatus": { + "coreOrderWorkflow/completed": "Completado", + "coreOrderWorkflow/created": "Creado", + "coreOrderWorkflow/canceled": "Cancelado", + "new": "Nuevo", + "coreOrderWorkflow/processing": "Procesando" + }, + "data": { + "status": { + "new": "Nuevo ", + "coreOrderWorkflow/created": "Creado ", + "coreOrderWorkflow/processing": "Procesando ", + "coreOrderWorkflow/completed": "Completado ", + "coreOrderWorkflow/canceled": "Cancelado ", + "coreOrderWorkflow/picked": "Recogido ", + "coreOrderWorkflow/packed": "Empaquetado ", + "coreOrderWorkflow/labeled": "Etiquetado ", + "coreOrderWorkflow/shipped": "Enviado ", + "multipleStatuses": "Múltiples estados" + } + } + } + } + } + } + } +] diff --git a/src/i18n/fr.json b/src/i18n/fr.json new file mode 100755 index 0000000..c2df088 --- /dev/null +++ b/src/i18n/fr.json @@ -0,0 +1,97 @@ +[{ + "i18n": "fr", + "ns": "reaction-orders", + "translation": { + "reaction-orders": { + "notFound": "Ordre non trouvé", + "order": { + "applyRefundToThisOrder": "Appliquer le remboursement de {{currencySymbol}}{{refund}} à cette commande?", + "applyRefundDuringCancelOrder": "Cela remboursera le total de la commande de {{currencySymbol}}{{invoiceTotal}}", + "cancelOrder": "Voulez-vous annuler cette commande?", + "cancelOrderLabel": "annuler la commande", + "cancelOrderThenRestock": "Oui, et réapprovisionnez", + "cancelOrderNoRestock": "Oui, mais pas de réapprovisionnement" + }, + "admin": { + "shortcut": { + "ordersLabel": "Commandes" + }, + "orderRisk": { + "high": "Risque élevé", + "riskCapture": "Ordre de capture avec charge de risque?", + "riskCaptureWarn": "Vous êtes sur le point de capturer l'ordre avec un risque de charge. Confirmer avant de continuer." + }, + "dashboard": { + "ordersLabel": "Commandes", + "ordersTitle": "Commandes", + "ordersDescription": "Accomplissez vos commandes", + "clearSearch": "Effacez" + }, + "orderWorkflow": { + "ordersList": { + "date": "Date", + "orderId": "ID commande", + "total": "Total", + "emailNotFound": "Email non disponible" + }, + "summary": { + "cardTitle": "Récapitulatif", + "copyOrderLink": "Lien de commande de copie" + }, + "invoice": { + "cardTitle": "Facture", + "adjustedTotal": "Total ajusté", + "capturedTotal": "Total saisi", + "refund": "Rembourser", + "refundLabel": "Pour le remboursement", + "refundItem": "Articles", + "refundItemAmount": "Total", + "refundTotal": "Remboursement total" + }, + "fulfillment": "Exécution", + "orderDetails": "Détails de la commande", + "shipmentTracking": "Expédition" + }, + "table": { + "headers": { + "name": "Nom", + "email": "Paramètres de messagerie", + "date": "Date", + "id": "ID", + "total": "Total", + "shipping": "Livraison", + "status": "Statut" + }, + "filter": { + "status": "Statut", + "dateRange": "Période", + "shippingStatus": "Statut d'envoi", + "new": "Nouveau", + "captured": "Paiement saisi", + "shipped": "Expédié", + "completed": "Complété", + "canceled": "Annulé", + "refunded": "Remboursé" + }, + "search": { + "clearSearch": "Effacer", + "placeholder": "Rechercher une commande" + }, + "data": { + "status": { + "new": "Nouveau", + "coreOrderWorkflow/created": "Créé", + "coreOrderWorkflow/processing": "En traitement", + "coreOrderWorkflow/completed": "Terminé", + "coreOrderWorkflow/canceled": "Annulé", + "coreOrderWorkflow/picked": "Choisi", + "coreOrderWorkflow/packed": "Emballé", + "coreOrderWorkflow/labeled": "Étiqueté", + "coreOrderWorkflow/shipped": "Expédié" + } + } + } + } + } + } +}] diff --git a/src/i18n/he.json b/src/i18n/he.json new file mode 100755 index 0000000..f24b767 --- /dev/null +++ b/src/i18n/he.json @@ -0,0 +1,47 @@ +[{ + "i18n": "he", + "ns": "reaction-orders", + "translation": { + "reaction-orders": { + "admin": { + "orderWorkflow": { + "ordersList": { + "date": "תאריך", + "orderId": "מזהה הזמנה", + "total": "סך הכל" + }, + "summary": { + "cardTitle": "סיכום" + }, + "invoice": { + "cardTitle": "חשבונית", + "adjustedTotal": "סך מתוקן", + "capturedTotal": "סך התשלום שעובד", + "refundItemAmount": "סך הכל" + }, + "fulfillment": "ביצוע", + "orderDetails": "פרטי הזמנה", + "shipmentTracking": "משלוח" + }, + "table": { + "headers": { + "email": "דוא\"ל", + "date": "תאריך", + "total": "סך הכל", + "shipping": "משלוח", + "status": "סטטוס" + }, + "filter": { + "status": "סטטוס", + "new": "חדש", + "captured": "התשלום עובד", + "shipped": "נשלח", + "completed": "הושלם", + "canceled": "בוטל", + "refunded": "החזר ניתן" + } + } + } + } + } +}] diff --git a/src/i18n/hr.json b/src/i18n/hr.json new file mode 100755 index 0000000..420333f --- /dev/null +++ b/src/i18n/hr.json @@ -0,0 +1,97 @@ +[{ + "i18n": "hr", + "ns": "reaction-orders", + "translation": { + "reaction-orders": { + "notFound": "Red nije pronađen", + "order": { + "applyRefundToThisOrder": "Nanesite povrat {{currencySymbol}}{{refund}} ovom cilju?", + "applyRefundDuringCancelOrder": "To će vratiti ukupni iznos narudžbe {{currencySymbol}}{{invoiceTotal}}", + "cancelOrder": "Želite li otkazati ovu narudžbu?", + "cancelOrderLabel": "Otkazati narudžbu", + "cancelOrderThenRestock": "Da, i popunite se", + "cancelOrderNoRestock": "Da, ali ne popunjavanje" + }, + "admin": { + "shortcut": { + "ordersLabel": "Narudžbe" + }, + "orderRisk": { + "high": "Visokog rizika", + "riskCapture": "Snimanje reda s naplatom rizika?", + "riskCaptureWarn": "Uskoro ćete uhvatiti red s rizikom naplate. Potvrdite prije nastavka." + }, + "dashboard": { + "ordersLabel": "Narudžbe", + "ordersTitle": "Narudžbe", + "ordersDescription": "Ispuniti Vaše narudžbe", + "clearSearch": "Čisto" + }, + "orderWorkflow": { + "ordersList": { + "date": "Rok", + "orderId": "ID narudžbe", + "total": "Ukupno", + "emailNotFound": "E-pošta nije dostupna" + }, + "summary": { + "cardTitle": "Sažetak", + "copyOrderLink": "Kopiraj narudžbu" + }, + "invoice": { + "cardTitle": "Dostavnica", + "adjustedTotal": "Prilagođen Ukupno", + "capturedTotal": "Uhvaćena Ukupno", + "refund": "Povrat", + "refundLabel": "Za povrat", + "refundItem": "stavke", + "refundItemAmount": "Ukupno", + "refundTotal": "Ukupni iznos povrata" + }, + "fulfillment": "Ispunjenje", + "orderDetails": "Detalji narudžbe", + "shipmentTracking": "Pošiljka" + }, + "table": { + "headers": { + "name": "Ime", + "email": "Postavke e-pošte", + "date": "Rok", + "id": "ID", + "total": "Ukupno", + "shipping": "dostava", + "status": "Položaj" + }, + "filter": { + "status": "Položaj", + "dateRange": "Raspon datuma", + "shippingStatus": "Status isporuke", + "new": "Novi", + "captured": "Uhvaćena plaćanja", + "shipped": "isporučen", + "completed": "dovršen", + "canceled": "otkazan", + "refunded": "Povrat za" + }, + "search": { + "clearSearch": "Čisto", + "placeholder": "Narudžbe za pretraživanje" + }, + "data": { + "status": { + "new": "Novi", + "coreOrderWorkflow/created": "stvoren", + "coreOrderWorkflow/processing": "Obrada", + "coreOrderWorkflow/completed": "dovršen", + "coreOrderWorkflow/canceled": "otkazan", + "coreOrderWorkflow/picked": "izabran", + "coreOrderWorkflow/packed": "upakiran", + "coreOrderWorkflow/labeled": "Označen", + "coreOrderWorkflow/shipped": "isporučen" + } + } + } + } + } + } +}] diff --git a/src/i18n/hu.json b/src/i18n/hu.json new file mode 100755 index 0000000..f9e87d5 --- /dev/null +++ b/src/i18n/hu.json @@ -0,0 +1,97 @@ +[{ + "i18n": "hu", + "ns": "reaction-orders", + "translation": { + "reaction-orders": { + "notFound": "A rendelés nem található", + "order": { + "applyRefundToThisOrder": "Alkalmazza visszatérítés {{currencySymbol}}{{refund}} hogy ebben a sorrendben?", + "applyRefundDuringCancelOrder": "Ez visszatéríti a rendelés teljes {{currencySymbol}}{{invoiceTotal}}", + "cancelOrder": "Szeretné, hogy megszünteti ezt a rendelést?", + "cancelOrderLabel": "Rendelés lemondása", + "cancelOrderThenRestock": "Igen, és újratelepítésére", + "cancelOrderNoRestock": "Igen, de nem az újratelepítés" + }, + "admin": { + "shortcut": { + "ordersLabel": "rendelés" + }, + "orderRisk": { + "high": "Nagy kockázat", + "riskCapture": "Rögzítse a rendelést kockázatkezeléssel?", + "riskCaptureWarn": "A megbízást díjkötelezettséggel fogja elfoglalni. A folytatás előtt hagyja jóvá." + }, + "dashboard": { + "ordersLabel": "rendelés", + "ordersTitle": "rendelés", + "ordersDescription": "Teljesíteni a megrendeléseket", + "clearSearch": "Egyértelmű" + }, + "orderWorkflow": { + "ordersList": { + "date": "Dátum", + "orderId": "Rendelés azonosító", + "total": "Összesen", + "emailNotFound": "E-mail nem elérhető" + }, + "summary": { + "cardTitle": "összefoglalás", + "copyOrderLink": "Copy Order link" + }, + "invoice": { + "cardTitle": "Számla", + "adjustedTotal": "korrigált összesen", + "capturedTotal": "elfogott Összesen", + "refund": "Visszatérítés", + "refundLabel": "Visszatérítésre", + "refundItem": "példány", + "refundItemAmount": "Összesen", + "refundTotal": "Visszatérítés összesen" + }, + "fulfillment": "Teljesítés", + "orderDetails": "Rendelési információk", + "shipmentTracking": "szállítmány" + }, + "table": { + "headers": { + "name": "Név", + "email": "E-mail", + "date": "Dátum", + "id": "ID", + "total": "Összesen", + "shipping": "Szállítás", + "status": "Állapot" + }, + "filter": { + "status": "Állapot", + "dateRange": "Időintervallum", + "shippingStatus": "Szállítási állapot", + "new": "Új", + "captured": "fizetési rögzített", + "shipped": "Kiszállított", + "completed": "befejezték", + "canceled": "Törölve", + "refunded": "Visszautalva" + }, + "search": { + "clearSearch": "Egyértelmű", + "placeholder": "Keresési megbízások" + }, + "data": { + "status": { + "new": "Új", + "coreOrderWorkflow/created": "Alkotó", + "coreOrderWorkflow/processing": "Feldolgozás", + "coreOrderWorkflow/completed": "befejezték", + "coreOrderWorkflow/canceled": "Törölve", + "coreOrderWorkflow/picked": "Válogatott", + "coreOrderWorkflow/packed": "Csomagolt", + "coreOrderWorkflow/labeled": "Jelzett", + "coreOrderWorkflow/shipped": "Kiszállított" + } + } + } + } + } + } +}] diff --git a/src/i18n/index.js b/src/i18n/index.js new file mode 100755 index 0000000..9deb0a6 --- /dev/null +++ b/src/i18n/index.js @@ -0,0 +1,54 @@ +import ar from "./ar.json"; +import bg from "./bg.json"; +import de from "./de.json"; +import el from "./el.json"; +import en from "./en.json"; +import es from "./es.json"; +import fr from "./fr.json"; +import he from "./he.json"; +import hr from "./hr.json"; +import it from "./it.json"; +import my from "./my.json"; +import nb from "./nb.json"; +import nl from "./nl.json"; +import pl from "./pl.json"; +import pt from "./pt.json"; +import ro from "./ro.json"; +import ru from "./ru.json"; +import sl from "./sl.json"; +import sv from "./sv.json"; +import tr from "./tr.json"; +import vi from "./vi.json"; +import zh from "./zh.json"; + +// +// we want all the files in individual +// imports for easier handling by +// automated translation software +// +export default { + translations: [ + ...ar, + ...bg, + ...de, + ...el, + ...en, + ...es, + ...fr, + ...he, + ...hr, + ...it, + ...my, + ...nb, + ...nl, + ...pl, + ...pt, + ...ro, + ...ru, + ...sl, + ...sv, + ...tr, + ...vi, + ...zh + ] +}; diff --git a/src/i18n/it.json b/src/i18n/it.json new file mode 100755 index 0000000..cc7701b --- /dev/null +++ b/src/i18n/it.json @@ -0,0 +1,97 @@ +[{ + "i18n": "it", + "ns": "reaction-orders", + "translation": { + "reaction-orders": { + "notFound": "Ordine non trovato", + "order": { + "applyRefundToThisOrder": "Applicare il rimborso di {{currencySymbol}}{{refund}} a questo ordine?", + "applyRefundDuringCancelOrder": "Ciò restituisce il totale dell'ordine di {{currencySymbol}}{{invoiceTotal}}", + "cancelOrder": "Vuoi annullare questo ordine?", + "cancelOrderLabel": "Annulla Ordine", + "cancelOrderThenRestock": "Sì, e riposare", + "cancelOrderNoRestock": "Sì, ma non ripieno" + }, + "admin": { + "shortcut": { + "ordersLabel": "Ordini" + }, + "orderRisk": { + "high": "Alto rischio", + "riskCapture": "Acquisizione dell'ordine con carica di rischio?", + "riskCaptureWarn": "Stai per catturare l'ordine con un rischio di addebito. Confermare prima di procedere." + }, + "dashboard": { + "ordersLabel": "Ordini", + "ordersTitle": "Ordini", + "ordersDescription": "Soddisfare i vostri ordini", + "clearSearch": "Chiaro" + }, + "orderWorkflow": { + "ordersList": { + "date": "Data", + "orderId": "ID ordine", + "total": "Totale", + "emailNotFound": "L'email non è disponibile" + }, + "summary": { + "cardTitle": "Sommario", + "copyOrderLink": "Copia ordini di copia" + }, + "invoice": { + "cardTitle": "Fattura", + "adjustedTotal": "totale rettificato", + "capturedTotal": "catturato totale", + "refund": "Rimborso", + "refundLabel": "Per il rimborso", + "refundItem": "Elementi", + "refundItemAmount": "Totale", + "refundTotal": "Rimborso Totale" + }, + "fulfillment": "Compimento", + "orderDetails": "Dettagli ordine", + "shipmentTracking": "Spedizione" + }, + "table": { + "headers": { + "name": "Nome", + "email": "Impostazioni e-mail", + "date": "Data", + "id": "ID", + "total": "Totale", + "shipping": "spedizione", + "status": "Stato" + }, + "filter": { + "status": "Stato", + "dateRange": "Intervallo di date", + "shippingStatus": "Stato della spedizione", + "new": "Nuovo", + "captured": "pagamento Captured", + "shipped": "Spedito", + "completed": "Completato", + "canceled": "Annullato", + "refunded": "rimborsato" + }, + "search": { + "clearSearch": "Chiaro", + "placeholder": "Ordini di ricerca" + }, + "data": { + "status": { + "new": "Nuovo", + "coreOrderWorkflow/created": "Creato", + "coreOrderWorkflow/processing": "lavorazione", + "coreOrderWorkflow/completed": "Completato", + "coreOrderWorkflow/canceled": "Annullato", + "coreOrderWorkflow/picked": "raccolto", + "coreOrderWorkflow/packed": "Confezionato", + "coreOrderWorkflow/labeled": "Labeled", + "coreOrderWorkflow/shipped": "Spedito" + } + } + } + } + } + } +}] diff --git a/src/i18n/my.json b/src/i18n/my.json new file mode 100755 index 0000000..9a6fa84 --- /dev/null +++ b/src/i18n/my.json @@ -0,0 +1,97 @@ +[{ + "i18n": "my", + "ns": "reaction-orders", + "translation": { + "reaction-orders": { + "notFound": "အမိန့်တွေ့မ", + "order": { + "applyRefundToThisOrder": "ဒီအမိန့်မှ {{currencySymbol}}{{refund}} ၏ပြန်အမ်းငွေ Apply?", + "applyRefundDuringCancelOrder": "ဤသည် {{currencySymbol}}{{invoiceTotal}} ၏အမိန့်စုစုပေါင်းပြန်အမ်းလိမ့်မယ်", + "cancelOrder": "သင်သည်ဤအမိန့်ကိုပယ်ဖျက်ချင်ပါသလား?", + "cancelOrderLabel": "အော်ဒါဖျက်သိမ်းသည်", + "cancelOrderThenRestock": "ဟုတ်ကဲ့, restock", + "cancelOrderNoRestock": "ဟုတ်ကဲ့, ဒါပေမယ့်ဘယ်သူမျှမ restocking" + }, + "admin": { + "shortcut": { + "ordersLabel": "အမိန့်" + }, + "orderRisk": { + "high": "မြင့်မားသောအန္တရာယ်", + "riskCapture": "အန္တရာယ်တာဝန်ခံနှင့်အတူအမိန့် Capture?", + "riskCaptureWarn": "သင်တစ်ဦးတာဝန်ခံအန္တရာယ်နှင့်အတူအမိန့်ကိုဖမ်းယူဖို့အကြောင်းရှိပါသည်။ ဆက်လက်မလုပ်ဆောင်မီအတည်ပြုပါ။" + }, + "dashboard": { + "ordersLabel": "အမိန့်", + "ordersTitle": "အမိန့်", + "ordersDescription": "သင့်ရဲ့အမိန့်ဖြည့်ဆည်း", + "clearSearch": "ရှင်းလင်းသော" + }, + "orderWorkflow": { + "ordersList": { + "date": "နေ့စှဲ", + "orderId": "အမိန့် ID ကို", + "total": "စုစုပေါင်း ကျသင့်ငွေ", + "emailNotFound": "အီးမေးလ်ပို့ရန်ကိုမရရှိနိုင်" + }, + "summary": { + "cardTitle": "အကျဉ်းချုပ်", + "copyOrderLink": "Copy ကူးအမိန့် Link ကို" + }, + "invoice": { + "cardTitle": "ဝယ်ကုန်စာရင်း", + "adjustedTotal": "Adjustments စုစုပေါင်း", + "capturedTotal": "ဖမ်းမိစုစုပေါင်း", + "refund": "ပြန်ပေးခြင်း", + "refundLabel": "ပြန်အမ်းငွေအဘို့", + "refundItem": "items", + "refundItemAmount": "စုစုပေါင်း ကျသင့်ငွေ", + "refundTotal": "ပြန်အမ်းငွေစုစုပေါင်း" + }, + "fulfillment": "ပွညျ့စုံ", + "orderDetails": "အမိန့်အသေးစိတ်", + "shipmentTracking": "တင်ပို့" + }, + "table": { + "headers": { + "name": "နာမကိုအမှီ", + "email": "အီးမေးလ်က", + "date": "နေ့စှဲ", + "id": "ID ကို", + "total": "စုစုပေါင်း ကျသင့်ငွေ", + "shipping": "သင်္ဘောဖြင့်ကုန်ပစ္စည်းပို့ခြင်း", + "status": "status" + }, + "filter": { + "status": "status", + "dateRange": "နေ့စွဲအကွာအဝေး", + "shippingStatus": "shipping status ကို", + "new": "နယူး", + "captured": "ငွေပေးချေမှုရမည့်ဖမ်းဆီးခံရသော", + "shipped": "တင်ပို့", + "completed": "completed", + "canceled": "ဖျက်သိမ်း", + "refunded": "ပြန်အမ်း" + }, + "search": { + "clearSearch": "ရှင်းလင်းသော", + "placeholder": "ရှာရန်အမိန့်" + }, + "data": { + "status": { + "new": "နယူး", + "coreOrderWorkflow/created": "Created", + "coreOrderWorkflow/processing": "ထုတ်ယူခြင်း", + "coreOrderWorkflow/completed": "ပြီးစီး", + "coreOrderWorkflow/canceled": "ဖျက်သိမ်း", + "coreOrderWorkflow/picked": "ကောက်ယူ", + "coreOrderWorkflow/packed": "Pack", + "coreOrderWorkflow/labeled": "တံဆိပ်ကပ်", + "coreOrderWorkflow/shipped": "တင်ပို့" + } + } + } + } + } + } +}] diff --git a/src/i18n/nb.json b/src/i18n/nb.json new file mode 100755 index 0000000..e46e4d6 --- /dev/null +++ b/src/i18n/nb.json @@ -0,0 +1,36 @@ +[{ + "i18n": "nb", + "ns": "reaction-orders", + "translation": { + "reaction-orders": { + "admin": { + "orderWorkflow": { + "ordersList": { + "total": "Totalt" + }, + "invoice": { + "cardTitle": "Faktura", + "adjustedTotal": "Endret Total", + "capturedTotal": "Totalt innbetalt", + "refundItemAmount": "Totalt" + } + }, + "table": { + "headers": { + "email": "Epost", + "total": "Totalt", + "shipping": "Frakt" + }, + "filter": { + "new": "Ny", + "captured": "Betalt", + "shipped": "Sendt", + "completed": "Fullført", + "canceled": "Avbrutt", + "refunded": "Refundert" + } + } + } + } + } +}] diff --git a/src/i18n/nl.json b/src/i18n/nl.json new file mode 100755 index 0000000..2649f87 --- /dev/null +++ b/src/i18n/nl.json @@ -0,0 +1,97 @@ +[{ + "i18n": "nl", + "ns": "reaction-orders", + "translation": { + "reaction-orders": { + "notFound": "Bestelling niet gevonden", + "order": { + "applyRefundToThisOrder": "Solliciteer terugbetaling van {{currencySymbol}}{{refund}} deze bestelling?", + "applyRefundDuringCancelOrder": "Dit zal het bestelbedrag van {{currencySymbol}}{{invoiceTotal}} terugbetalen", + "cancelOrder": "Wilt u deze bestelling annuleren?", + "cancelOrderLabel": "Annuleer bestelling", + "cancelOrderThenRestock": "Ja, en opnieuw", + "cancelOrderNoRestock": "Ja, maar geen restocking" + }, + "admin": { + "shortcut": { + "ordersLabel": "orders" + }, + "orderRisk": { + "high": "Hoog risico", + "riskCapture": "Capture Order Met Risicostarief?", + "riskCaptureWarn": "Je staat op het punt om de bestelling vast te leggen met een ladingrisico. Bevestig voordat u verder gaat." + }, + "dashboard": { + "ordersLabel": "orders", + "ordersTitle": "orders", + "ordersDescription": "Vervul uw bestellingen", + "clearSearch": "Duidelijk" + }, + "orderWorkflow": { + "ordersList": { + "date": "Datum", + "orderId": "Order ID", + "total": "Totaal", + "emailNotFound": "E-mail niet beschikbaar" + }, + "summary": { + "cardTitle": "Overzicht", + "copyOrderLink": "Kopieer Order Link" + }, + "invoice": { + "cardTitle": "Factuur", + "adjustedTotal": "aangepaste Total", + "capturedTotal": "gevangen Totaal", + "refund": "Terugbetaling", + "refundLabel": "Voor terugbetaling", + "refundItem": "Items", + "refundItemAmount": "Totaal", + "refundTotal": "Restitutie Totaal" + }, + "fulfillment": "Vervulling", + "orderDetails": "Bestel Details", + "shipmentTracking": "Verzending" + }, + "table": { + "headers": { + "name": "Naam", + "email": "E-mailinstellingen", + "date": "Datum", + "id": "ID", + "total": "Totaal", + "shipping": "Verzenden", + "status": "Toestand" + }, + "filter": { + "status": "Toestand", + "dateRange": "Datumbereik", + "shippingStatus": "Verzendstatus", + "new": "Nieuw", + "captured": "betaling Gevangen", + "shipped": "verzonden", + "completed": "Voltooid", + "canceled": "Geannuleerd", + "refunded": "terugbetaald" + }, + "search": { + "clearSearch": "Duidelijk", + "placeholder": "Zoekopdrachten" + }, + "data": { + "status": { + "new": "nieuwe", + "coreOrderWorkflow/created": "gemaakt", + "coreOrderWorkflow/processing": "Verwerken", + "coreOrderWorkflow/completed": "Voltooid", + "coreOrderWorkflow/canceled": "Geannuleerd", + "coreOrderWorkflow/picked": "uitgezocht", + "coreOrderWorkflow/packed": "overvol", + "coreOrderWorkflow/labeled": "geëtiketteerd", + "coreOrderWorkflow/shipped": "verzonden" + } + } + } + } + } + } +}] diff --git a/src/i18n/pl.json b/src/i18n/pl.json new file mode 100755 index 0000000..2f7510f --- /dev/null +++ b/src/i18n/pl.json @@ -0,0 +1,97 @@ +[{ + "i18n": "pl", + "ns": "reaction-orders", + "translation": { + "reaction-orders": { + "notFound": "Zamówienie nie zostało znalezione", + "order": { + "applyRefundToThisOrder": "Zastosuj zwrot {{currencySymbol}}{{refund}} do tego celu?", + "applyRefundDuringCancelOrder": "Spowoduje to zwrot całkowitej kwoty zamówienia {{currencySymbol}}{{invoiceTotal}}", + "cancelOrder": "Czy chcesz anulować zamówienie?", + "cancelOrderLabel": "Anuluj zamówienie", + "cancelOrderThenRestock": "Tak, i restockuj", + "cancelOrderNoRestock": "Tak, ale nie restockuj" + }, + "admin": { + "shortcut": { + "ordersLabel": "Zamówienia" + }, + "orderRisk": { + "high": "Wysokie ryzyko", + "riskCapture": "Złożyć zamówienie z opłatą za ryzyko?", + "riskCaptureWarn": "Zamierzasz uchwycić zlecenie z ładunkiem. Potwierdź przed kontynuowaniem." + }, + "dashboard": { + "ordersLabel": "Zamówienia", + "ordersTitle": "Zamówienia", + "ordersDescription": "Spełnij swoje zamówienia", + "clearSearch": "Wyczyść" + }, + "orderWorkflow": { + "ordersList": { + "date": "Data", + "orderId": "Identyfikator zamówienia", + "total": "Kwota", + "emailNotFound": "E-mail nie jest dostępny" + }, + "summary": { + "cardTitle": "Podsumowanie", + "copyOrderLink": "Kopiuj link zamówienia" + }, + "invoice": { + "cardTitle": "Faktura", + "adjustedTotal": "Skorygowana kwota", + "capturedTotal": "Zapłacona kwota", + "refund": "Zwrot", + "refundLabel": "Zwrot", + "refundItem": "Przedmiot", + "refundItemAmount": "Kwota", + "refundTotal": "Zwrot kosztów" + }, + "fulfillment": "Spełnienie", + "orderDetails": "Szczegóły zamówienia", + "shipmentTracking": "Wysyłka" + }, + "table": { + "headers": { + "name": "Nazwa", + "email": "Ustawienia poczty e-mail", + "date": "Data", + "id": "ID", + "total": "Kwota", + "shipping": "Wysyłka", + "status": "Status" + }, + "filter": { + "status": "Status", + "dateRange": "Zakres dat", + "shippingStatus": "Stan wysyłki", + "new": "Nowy", + "captured": "Płatność Przechwycona", + "shipped": "Wysłano", + "completed": "Zakończono", + "canceled": "Anulowane", + "refunded": "Zwrócone" + }, + "search": { + "clearSearch": "Wyczyść", + "placeholder": "Szukaj zamówień" + }, + "data": { + "status": { + "new": "Nowy", + "coreOrderWorkflow/created": "Nowe", + "coreOrderWorkflow/processing": "Przetwarzane", + "coreOrderWorkflow/completed": "Zakończone", + "coreOrderWorkflow/canceled": "Anulowane", + "coreOrderWorkflow/picked": "Odebrane", + "coreOrderWorkflow/packed": "Spakowane", + "coreOrderWorkflow/labeled": "Oznaczone", + "coreOrderWorkflow/shipped": "Wysłane" + } + } + } + } + } + } +}] diff --git a/src/i18n/pt.json b/src/i18n/pt.json new file mode 100755 index 0000000..04fea3f --- /dev/null +++ b/src/i18n/pt.json @@ -0,0 +1,196 @@ +[{ + "i18n": "pt", + "ns": "reaction-orders", + "translation": { + "reaction-orders": { + "admin": { + "dashboard": { + "clearSearch": "Limpar", + "ordersDescription": "Atenda seus pedidos", + "ordersLabel": "Pedidos", + "ordersTitle": "Pedidos" + }, + "orderRisk": { + "high": "Alto risco", + "riskCapture": "Capturar pedido com cobrança de risco?", + "riskCaptureWarn": "Você está prestes a capturar o pedido com cobrança de risco. Confirme antes de prosseguir." + }, + "orderWorkflow": { + "fulfillment": "Conclusão", + "fulfillmentGroups": { + "cancelFulfillmentGroup": "Cancelar grupo", + "markAsPacked": "Marcar como embalado", + "printShippingLabel": "Imprimir etiqueta de envio" + }, + "invoice": { + "adjustedTotal": "Total ajustado", + "capturedTotal": "Total capturado", + "cardTitle": "Fatura", + "printInvoice": "Imprimir fatura", + "refund": "Reembolso", + "refundItem": "Itens", + "refundItemAmount": "Total", + "refundLabel": "Para reembolso", + "refundTotal": "Reembolso Total" + }, + "orderDetails": "Detalhes do pedido", + "ordersList": { + "date": "Data", + "emailNotFound": "Email não disponível", + "orderId": "ID do pedido", + "total": "Total" + }, + "shipmentTracking": "Envio", + "summary": { + "cardTitle": "Resumo", + "copyOrderLink": "Copiar link do pedido" + } + }, + "shortcut": { + "ordersLabel": "Pedidos" + }, + "table": { + "data": { + "status": { + "coreOrderWorkflow/canceled": "Cancelado", + "coreOrderWorkflow/completed": "Concluído", + "coreOrderWorkflow/created": "Criado", + "coreOrderWorkflow/labeled": "Etiquetado", + "coreOrderWorkflow/packed": "Embalado", + "coreOrderWorkflow/picked": "Separado", + "coreOrderWorkflow/processing": "Processando", + "coreOrderWorkflow/shipped": "Enviado", + "multipleStatuses": "Vários status", + "new": "Novo" + } + }, + "filter": { + "canceled": "Cancelado", + "captured": "Capturado", + "completed": "Concluído", + "dateRange": "Intervalo de datas", + "new": "Novo", + "refunded": "Reembolsado", + "shipped": "Enviado", + "shippingStatus": "Status de envio", + "status": "Status" + }, + "fulfillmentStatus": { + "coreOrderWorkflow/canceled": "Cancelado", + "coreOrderWorkflow/completed": "Concluído", + "coreOrderWorkflow/created": "Criado", + "coreOrderWorkflow/processing": "Processando", + "new": "Novo" + }, + "headers": { + "customer": "Cliente", + "date": "Data", + "email": "Email", + "fulfillment": "Fulfillment", + "id": "ID do pedido", + "name": "Nome", + "payment": "Pagamento", + "shipping": "Envio", + "status": "Status", + "total": "Total" + }, + "orderStatus": { + "coreOrderWorkflow/canceled": "Cancelado", + "coreOrderWorkflow/completed": "Concluído", + "coreOrderWorkflow/processing": "Processando", + "new": "Novo" + }, + "paymentStatus": { + "completed": "Concluído", + "created": "Criado" + }, + "search": { + "clearSearch": "Limpar", + "placeholder": "Pesquisar pedidos" + } + } + }, + "calculateRefundByItem": "Calcular reembolso por item", + "fulfillment": "Fulfillment", + "fulfillmentGroupHeader": "Grupo de fulfillment {{currentGroupCount}} de {{totalGroupsCount}}", + "items": "Itens", + "newStatus": "Novo status", + "notFound": "Pedido não encontrado", + "order": { + "allPaymentsRefunded": "Todos os pagamentos foram completamente reembolsados", + "allowShippingRefund": "Permitir que o frete seja reembolsado", + "amountToRefund": "Montante a reembolsar", + "applyRefund": "Aplicar reembolso", + "applyRefundDuringCancelOrder": "Isto irá reembolsar o total do pedido de {{currencySymbol}}{{invoiceTotal}}", + "applyRefundToThisOrder": "Aplicar reembolso de {{currencySymbol}}{{refund}} para este pedido?", + "availableToRefund": "Disponível para reembolso", + "cancelGroup": "Deseja cancelar este grupo?", + "cancelGroupLabel": "Cancelar grupo", + "cancelOrder": "Deseja cancelar este pedido?", + "cancelOrderLabel": "Cancelar pedido", + "cancelOrderNoRestock": "Sem reabastecimento", + "cancelOrderThenRestock": "Reabastecer", + "noRefundReason": "Motivo não fornecido", + "order": "Pedido", + "paymentRefunded": "O pagamento está totalmente reembolsado", + "placed": "Colocado", + "previousRefunds": "Reembolsos anteriores", + "previouslyRefunded": "Anteriormente reembolsado", + "reason": "Motivo", + "reasonForRefund": "Motivo para reembolso", + "reasonForRefundFormLabel": "Motivo para reembolso (opcional)", + "refund": "Reembolso", + "refundButton": "Reembolso de {{currentRefundAmount}}", + "refundItemsAlert": "Deseja reembolsar {{refundItemsQuantity}} item(s) totalizando {{refundItemsTotal}}?", + "refundItemsApproveAlert": "Deseja aprovar este pagamento e depois reembolsar {{totalAmount}}?", + "refundItemsCaptureAlert": "Deseja capturar este pagamento e depois reembolsar {{totalAmount}}?", + "refundItemsSuccess": "Reembolso aplicado com sucesso", + "refundItemsTitle": "Itens de reembolso", + "refundItemsWait": "Pagamento ainda não foi capturado", + "refundNotSupported": "O modo de pagamento não suporta reembolsos", + "refundReason": { + "customerRequest": "Solicitação do cliente", + "duplicatePayment": "Pagamento duplicado", + "fraudulent": "Fraudulento" + }, + "refundTo": "Reembolso para", + "refundedTo": "Reembolsado para", + "refunding": "Reembolsando", + "refundsNotSupported": "Reembolsos não são suportados com o(s) modo(s) de pagamento usado(s) para este pedido", + "restockInventory": "Repor estoque?", + "updateFulfillmentGroupStatusMessage": "Deseja atualizar o status deste grupo de fulfillment para {{groupStatus}}", + "updateFulfillmentGroupStatusTitle": "Atualizar status do grupo" + }, + "orderActions": { + "markAsPackedDescription": "Marcar todos os itens neste grupo de fulfillment como \"Embalados\"", + "markAsPackedLabel": "Marcar como embalado", + "markAsShippedDescription": "Marcar todos os itens neste grupo de fulfillment como \"Enviados\"", + "markAsShippedLabel": "Marcar como enviado", + "updateGroupStatus": "Atualizar status do grupo" + }, + "orderCard": { + "orderSummary": { + "showOrderSummary": "Mostrar resumo do pedido", + "title": "Resumo do pedido" + } + }, + "orderDetails": "Detalhes do pedido", + "refunds": "Reembolsos", + "shippingAddress": "Endereço de entrega", + "shippingMethod": "Modo de envio", + "status": { + "coreOrderWorkflow/canceled": "Cancelado", + "coreOrderWorkflow/completed": "Completo", + "coreOrderWorkflow/created": "Criado", + "coreOrderWorkflow/labeled": "Etiquetado", + "coreOrderWorkflow/packed": "Embalado", + "coreOrderWorkflow/picked": "Separado", + "coreOrderWorkflow/processing": "Processando", + "coreOrderWorkflow/shipped": "Enviado", + "multipleStatuses": "Vários status", + "new": "Novo" + }, + "trackingNumber": "Código de rastreamento" + } + } +}] diff --git a/src/i18n/ro.json b/src/i18n/ro.json new file mode 100755 index 0000000..c9ec0a5 --- /dev/null +++ b/src/i18n/ro.json @@ -0,0 +1,97 @@ +[{ + "i18n": "ro", + "ns": "reaction-orders", + "translation": { + "reaction-orders": { + "notFound": "Comanda nu a fost găsită", + "order": { + "applyRefundToThisOrder": "Aplicați rambursarea {{currencySymbol}}{{refund}} la această comandă?", + "applyRefundDuringCancelOrder": "Aceasta va restitui suma totală a comenzii {{currencySymbol}}{{invoiceTotal}}", + "cancelOrder": "Doriți să anulați această comandă?", + "cancelOrderLabel": "Anulați comanda", + "cancelOrderThenRestock": "Da, și restock", + "cancelOrderNoRestock": "Da, dar fără repopulare" + }, + "admin": { + "shortcut": { + "ordersLabel": "Istoric comenzi" + }, + "orderRisk": { + "high": "Risc ridicat", + "riskCapture": "Capturați comanda cu taxă de risc?", + "riskCaptureWarn": "Sunteți pe punctul de a capta comanda cu un risc de taxă. Confirmați înainte de a continua." + }, + "dashboard": { + "ordersLabel": "Istoric comenzi", + "ordersTitle": "Istoric comenzi", + "ordersDescription": "Onora comenzile", + "clearSearch": "clar" + }, + "orderWorkflow": { + "ordersList": { + "date": "Data", + "orderId": "ID comandă", + "total": "Total", + "emailNotFound": "E-mailul nu este disponibil" + }, + "summary": { + "cardTitle": "Rezumat", + "copyOrderLink": "Copiați linkul de comandă" + }, + "invoice": { + "cardTitle": "Factură", + "adjustedTotal": "Totalul ajustat", + "capturedTotal": "Totalul capturat", + "refund": "Restituire", + "refundLabel": "Pentru rambursare", + "refundItem": "articole", + "refundItemAmount": "Total", + "refundTotal": "Suma totală de rambursare" + }, + "fulfillment": "Onorarea comenzilor", + "orderDetails": "Detalii Comandă", + "shipmentTracking": "Expediere" + }, + "table": { + "headers": { + "name": "Nume", + "email": "Email", + "date": "Data", + "id": "ID", + "total": "Total", + "shipping": "livrare", + "status": "Stare" + }, + "filter": { + "status": "Stare", + "dateRange": "Interval de date", + "shippingStatus": "Starea de expediere", + "new": "Nou", + "captured": "Plată Capturată", + "shipped": "Expediat", + "completed": "Completă", + "canceled": "Anulată", + "refunded": "Rambursată" + }, + "search": { + "clearSearch": "clar", + "placeholder": "Comenzi de căutare" + }, + "data": { + "status": { + "new": "Nou", + "coreOrderWorkflow/created": "Creată", + "coreOrderWorkflow/processing": "Prelucrare", + "coreOrderWorkflow/completed": "terminat", + "coreOrderWorkflow/canceled": "Anulat", + "coreOrderWorkflow/picked": "ales", + "coreOrderWorkflow/packed": "bătătorit", + "coreOrderWorkflow/labeled": "marcat", + "coreOrderWorkflow/shipped": "Transportat" + } + } + } + } + } + } +}] diff --git a/src/i18n/ru.json b/src/i18n/ru.json new file mode 100755 index 0000000..b6169af --- /dev/null +++ b/src/i18n/ru.json @@ -0,0 +1,97 @@ +[{ + "i18n": "ru", + "ns": "reaction-orders", + "translation": { + "reaction-orders": { + "notFound": "Заказ не найден", + "order": { + "applyRefundToThisOrder": "Применить возврат {{currencySymbol}}{{refund}} в этот заказ?", + "applyRefundDuringCancelOrder": "Это вернет сумму заказа {{currencySymbol}}{{invoiceTotal}}", + "cancelOrder": "Вы хотите отменить этот заказ?", + "cancelOrderLabel": "Отменить заказ", + "cancelOrderThenRestock": "Да, и пополнение запасов", + "cancelOrderNoRestock": "Да, но без пополнения запасов" + }, + "admin": { + "shortcut": { + "ordersLabel": "заказы" + }, + "orderRisk": { + "high": "Высокий риск", + "riskCapture": "Захват заказа с риском?", + "riskCaptureWarn": "Вы собираетесь захватить заказ с риском для оплаты. Подтвердите, прежде чем продолжить." + }, + "dashboard": { + "ordersLabel": "заказы", + "ordersTitle": "заказы", + "ordersDescription": "Исполни ваши заказы", + "clearSearch": "Очистить" + }, + "orderWorkflow": { + "ordersList": { + "date": "Дата", + "orderId": "Идентификационный номер заказа", + "total": "Итого", + "emailNotFound": "Электронная почта недоступна" + }, + "summary": { + "cardTitle": "Сводка", + "copyOrderLink": "Ссылка на копию заказа" + }, + "invoice": { + "cardTitle": "Счёт", + "adjustedTotal": "Итого после возврата", + "capturedTotal": "Получено всего", + "refund": "Возврат", + "refundLabel": "Для возврата", + "refundItem": "Предметы", + "refundItemAmount": "Итого", + "refundTotal": "Сумма возврата" + }, + "fulfillment": "Движение заказа", + "orderDetails": "Детали заказа", + "shipmentTracking": "Перевозка" + }, + "table": { + "headers": { + "name": "Имя", + "email": "Настройки электронной почты", + "date": "Дата", + "id": "ID", + "total": "Итого", + "shipping": "Перевозка", + "status": "Статус" + }, + "filter": { + "status": "Статус", + "dateRange": "Диапазон дат", + "shippingStatus": "Статус отправки", + "new": "Новых", + "captured": "Оплаченных", + "shipped": "Доставлено", + "completed": "Завершенных", + "canceled": "Отмененных", + "refunded": "Возвращенных" + }, + "search": { + "clearSearch": "Очистить", + "placeholder": "Поиск заказов" + }, + "data": { + "status": { + "new": "новый", + "coreOrderWorkflow/created": "созданный", + "coreOrderWorkflow/processing": "обработка", + "coreOrderWorkflow/completed": "Завершенный", + "coreOrderWorkflow/canceled": "отменен", + "coreOrderWorkflow/picked": "поднято", + "coreOrderWorkflow/packed": "уплотненный", + "coreOrderWorkflow/labeled": "маркированный", + "coreOrderWorkflow/shipped": "Высылаем" + } + } + } + } + } + } +}] diff --git a/src/i18n/sl.json b/src/i18n/sl.json new file mode 100755 index 0000000..17fe026 --- /dev/null +++ b/src/i18n/sl.json @@ -0,0 +1,97 @@ +[{ + "i18n": "sl", + "ns": "reaction-orders", + "translation": { + "reaction-orders": { + "notFound": "Naročilo ni bilo mogoče najti", + "order": { + "applyRefundToThisOrder": "Uporabi vračilo {{currencySymbol}}{{refund}} k temu, da bi?", + "applyRefundDuringCancelOrder": "To bo povrnil red skupaj {{currencySymbol}}{{invoiceTotal}}", + "cancelOrder": "Ali želite preklicati naročilo?", + "cancelOrderLabel": "Preklic naročila", + "cancelOrderThenRestock": "Ja, in obnovo", + "cancelOrderNoRestock": "Da, vendar se ne obnavlja stalež" + }, + "admin": { + "shortcut": { + "ordersLabel": "naročila" + }, + "orderRisk": { + "high": "Visoko tveganje", + "riskCapture": "Ukaz za prestrezanje s tveganjem?", + "riskCaptureWarn": "Prihajali boste po naročilu s tveganjem polnjenja. Potrdite, preden nadaljujete." + }, + "dashboard": { + "ordersLabel": "naročila", + "ordersTitle": "naročila", + "ordersDescription": "Izpolnitev vašega naročila", + "clearSearch": "Počisti" + }, + "orderWorkflow": { + "ordersList": { + "date": "Datum", + "orderId": "ID naročila", + "total": "Skupaj", + "emailNotFound": "E-pošta ni na voljo" + }, + "summary": { + "cardTitle": "Povzetek", + "copyOrderLink": "Kopiraj Naročilo Link" + }, + "invoice": { + "cardTitle": "Račun", + "adjustedTotal": "prilagojena Skupaj", + "capturedTotal": "Zajeto Skupaj", + "refund": "vračilo", + "refundLabel": "Za vračilo", + "refundItem": "Elementi", + "refundItemAmount": "Skupaj", + "refundTotal": "Povračilo skupaj" + }, + "fulfillment": "izpolnitev", + "orderDetails": "podrobnosti naročila", + "shipmentTracking": "pošiljka" + }, + "table": { + "headers": { + "name": "Name", + "email": "E-naslov", + "date": "Datum", + "id": "ID", + "total": "Skupaj", + "shipping": "Dostava", + "status": "Status" + }, + "filter": { + "status": "Status", + "dateRange": "Časovno obdobje", + "shippingStatus": "Status pošiljanja", + "new": "New", + "captured": "plačilo Zajeto", + "shipped": "pošiljajo", + "completed": "končan", + "canceled": "preklicana", + "refunded": "Vrnjeno" + }, + "search": { + "clearSearch": "Počisti", + "placeholder": "Iskalna naročila" + }, + "data": { + "status": { + "new": "Novo", + "coreOrderWorkflow/created": "Ustvarjeno", + "coreOrderWorkflow/processing": "Obravnavati", + "coreOrderWorkflow/completed": "Dokončano", + "coreOrderWorkflow/canceled": "Preklicano", + "coreOrderWorkflow/picked": "Izbrano", + "coreOrderWorkflow/packed": "Pakirano", + "coreOrderWorkflow/labeled": "Označeno", + "coreOrderWorkflow/shipped": "Shipped" + } + } + } + } + } + } +}] diff --git a/src/i18n/sv.json b/src/i18n/sv.json new file mode 100755 index 0000000..ba488d9 --- /dev/null +++ b/src/i18n/sv.json @@ -0,0 +1,97 @@ +[{ + "i18n": "sv", + "ns": "reaction-orders", + "translation": { + "reaction-orders": { + "notFound": "Beställningen hittades inte", + "order": { + "applyRefundToThisOrder": "Applicera återbetalning av {{currencySymbol}}{{refund}} till denna order?", + "applyRefundDuringCancelOrder": "Detta kommer att återbetala orderingången för {{currencySymbol}}{{invoiceTotal}}", + "cancelOrder": "Vill du avbryta denna order?", + "cancelOrderLabel": "Avbryt beställning", + "cancelOrderThenRestock": "Ja, och återuppta", + "cancelOrderNoRestock": "Ja, men ingen återhämtning" + }, + "admin": { + "shortcut": { + "ordersLabel": "Order" + }, + "orderRisk": { + "high": "Hög risk", + "riskCapture": "Capture Order med riskavgift?", + "riskCaptureWarn": "Du håller på att fånga order med en avgiftsrisk. Bekräfta innan du fortsätter." + }, + "dashboard": { + "ordersLabel": "Order", + "ordersTitle": "Order", + "ordersDescription": "Uppfylla dina beställningar", + "clearSearch": "Klar" + }, + "orderWorkflow": { + "ordersList": { + "date": "Datum", + "orderId": "För ID", + "total": "Totalt", + "emailNotFound": "E-post är inte tillgänglig" + }, + "summary": { + "cardTitle": "Sammanfattning", + "copyOrderLink": "Kopiera orderlänk" + }, + "invoice": { + "cardTitle": "Faktura", + "adjustedTotal": "justerat Total", + "capturedTotal": "fångade Totalt", + "refund": "Återbetalning", + "refundLabel": "För återbetalning", + "refundItem": "objekt", + "refundItemAmount": "Totalt", + "refundTotal": "Återbetalning totalt" + }, + "fulfillment": "Uppfyllelse", + "orderDetails": "Orderdetaljer", + "shipmentTracking": "Sändning" + }, + "table": { + "headers": { + "name": "Namn", + "email": "e-postinställningar", + "date": "Datum", + "id": "ID", + "total": "Totalt", + "shipping": "Frakt", + "status": "Status" + }, + "filter": { + "status": "Status", + "dateRange": "Datumintervall", + "shippingStatus": "Fraktstatus", + "new": "Ny", + "captured": "betalning Tagna", + "shipped": "skeppas", + "completed": "Avslutad", + "canceled": "Avbrutet", + "refunded": "återbetalas" + }, + "search": { + "clearSearch": "Klar", + "placeholder": "Sökord" + }, + "data": { + "status": { + "new": "Ny", + "coreOrderWorkflow/created": "Skapad", + "coreOrderWorkflow/processing": "bearbetning", + "coreOrderWorkflow/completed": "Avslutad", + "coreOrderWorkflow/canceled": "Avbryts", + "coreOrderWorkflow/picked": "plockade", + "coreOrderWorkflow/packed": "Packade", + "coreOrderWorkflow/labeled": "märkt", + "coreOrderWorkflow/shipped": "levereras" + } + } + } + } + } + } +}] diff --git a/src/i18n/tr.json b/src/i18n/tr.json new file mode 100755 index 0000000..91473d8 --- /dev/null +++ b/src/i18n/tr.json @@ -0,0 +1,97 @@ +[{ + "i18n": "tr", + "ns": "reaction-orders", + "translation": { + "reaction-orders": { + "notFound": "Sipariş Bulunamadı", + "order": { + "applyRefundToThisOrder": "Bu düzene {{currencySymbol}}{{refund}} iadesi uygulayın?", + "applyRefundDuringCancelOrder": "Bu, sipariş toplamını {{currencySymbol}}{{invoiceTotal}} tutarında iade edecektir", + "cancelOrder": "Bu emri iptal etmek istiyor musunuz?", + "cancelOrderLabel": "Siparişi iptal et", + "cancelOrderThenRestock": "Evet ve yeniden yerleştirme", + "cancelOrderNoRestock": "Evet, ama restocking yok" + }, + "admin": { + "shortcut": { + "ordersLabel": "Emirler" + }, + "orderRisk": { + "high": "Yüksek risk", + "riskCapture": "Risk Yüklemeli Sipariş Alınır mı?", + "riskCaptureWarn": "Yükleme riski taşıyan bir emir yakalamak üzeresin. Devam etmeden önce onaylayın." + }, + "dashboard": { + "ordersLabel": "Emirler", + "ordersTitle": "Emirler", + "ordersDescription": "siparişlerinizi yerine getirmek", + "clearSearch": "Açık" + }, + "orderWorkflow": { + "ordersList": { + "date": "Tarih", + "orderId": "Sipariş Kimliği", + "total": "Toplam", + "emailNotFound": "E-posta mevcut değil" + }, + "summary": { + "cardTitle": "özet", + "copyOrderLink": "Kopya Siparişi Bağlantısı" + }, + "invoice": { + "cardTitle": "Fatura", + "adjustedTotal": "Düzeltilmiş Toplam", + "capturedTotal": "yakalanan Toplam", + "refund": "Geri ödeme", + "refundLabel": "Geri Ödeme İçin", + "refundItem": "Öğeler", + "refundItemAmount": "Toplam", + "refundTotal": "Geri Ödeme Toplamı" + }, + "fulfillment": "yerine getirme", + "orderDetails": "sipariş detayları", + "shipmentTracking": "yükleme" + }, + "table": { + "headers": { + "name": "Isim", + "email": "E-posta", + "date": "Tarih", + "id": "Kimlik", + "total": "Toplam", + "shipping": "Nakliye", + "status": "Durum" + }, + "filter": { + "status": "Durum", + "dateRange": "Tarih aralığı", + "shippingStatus": "Nakliye durumu", + "new": "Yeni", + "captured": "Ödeme Yakalanan", + "shipped": "gönderilen", + "completed": "Tamamlanan", + "canceled": "Iptal edildi", + "refunded": "iade" + }, + "search": { + "clearSearch": "Açık", + "placeholder": "Siparişleri ara" + }, + "data": { + "status": { + "new": "Yeni", + "coreOrderWorkflow/created": "düzenlendi", + "coreOrderWorkflow/processing": "İşleme", + "coreOrderWorkflow/completed": "Tamamlanan", + "coreOrderWorkflow/canceled": "iptal edildi", + "coreOrderWorkflow/picked": "Seçilmiş", + "coreOrderWorkflow/packed": "paketlenmiş", + "coreOrderWorkflow/labeled": "Etiketli", + "coreOrderWorkflow/shipped": "gönderilen" + } + } + } + } + } + } +}] diff --git a/src/i18n/vi.json b/src/i18n/vi.json new file mode 100755 index 0000000..171b837 --- /dev/null +++ b/src/i18n/vi.json @@ -0,0 +1,97 @@ +[{ + "i18n": "vi", + "ns": "reaction-orders", + "translation": { + "reaction-orders": { + "notFound": "Không tìm thấy đơn đặt hàng", + "order": { + "applyRefundToThisOrder": "Áp dụng hoàn {{currencySymbol}}{{refund}} để đặt hàng này?", + "applyRefundDuringCancelOrder": "Điều này sẽ hoàn lại tổng số đơn đặt hàng của {{currencySymbol}}{{invoiceTotal}}", + "cancelOrder": "Bạn có muốn hủy đơn đặt hàng này không?", + "cancelOrderLabel": "Hủy đơn hàng", + "cancelOrderThenRestock": "Vâng, và nạp lại", + "cancelOrderNoRestock": "Vâng, nhưng không có hàng gia dụng" + }, + "admin": { + "shortcut": { + "ordersLabel": "Đơn đặt hàng" + }, + "orderRisk": { + "high": "Rủi ro cao", + "riskCapture": "Án lệnh bắt giữ với phí rủi ro?", + "riskCaptureWarn": "Bạn sắp bắt giữ lệnh với một rủi ro phí. Xác nhận trước khi tiếp tục." + }, + "dashboard": { + "ordersLabel": "Đơn đặt hàng", + "ordersTitle": "Đơn đặt hàng", + "ordersDescription": "Thực hiện đơn đặt hàng của bạn", + "clearSearch": "Trong sáng" + }, + "orderWorkflow": { + "ordersList": { + "date": "Ngày", + "orderId": "Order ID", + "total": "Tổng số", + "emailNotFound": "Email không có sẵn" + }, + "summary": { + "cardTitle": "Tóm lược", + "copyOrderLink": "Sao chép Liên kết Đặt hàng" + }, + "invoice": { + "cardTitle": "Hóa đơn", + "adjustedTotal": "Tổng số điều chỉnh", + "capturedTotal": "Bị bắt Tổng số", + "refund": "Hoàn tiền", + "refundLabel": "Đối với hoàn phí", + "refundItem": "Mặt hàng", + "refundItemAmount": "Tổng số", + "refundTotal": "Tổng số tiền hoàn lại" + }, + "fulfillment": "hoàn thành", + "orderDetails": "Đặt hàng Chi tiết", + "shipmentTracking": "lô hàng" + }, + "table": { + "headers": { + "name": "Tên", + "email": "Cài đặt email", + "date": "Ngày", + "id": "ID", + "total": "Tổng số", + "shipping": "Đang chuyển hàng", + "status": "Trạng thái" + }, + "filter": { + "status": "Trạng thái", + "dateRange": "Phạm vi ngày", + "shippingStatus": "Tình trạng giao hàng", + "new": "Mới", + "captured": "Thanh toán bắt", + "shipped": "vận chuyển", + "completed": "Đã hoàn thành", + "canceled": "bị hủy bỏ", + "refunded": "Hoàn" + }, + "search": { + "clearSearch": "Trong sáng", + "placeholder": "Tìm kiếm đơn đặt hàng" + }, + "data": { + "status": { + "new": "Mới", + "coreOrderWorkflow/created": "Tạo", + "coreOrderWorkflow/processing": "Chế biến", + "coreOrderWorkflow/completed": "Đã hoàn thành", + "coreOrderWorkflow/canceled": "Đã hủy", + "coreOrderWorkflow/picked": "Chọn", + "coreOrderWorkflow/packed": "Đóng gói", + "coreOrderWorkflow/labeled": "Được gắn nhãn", + "coreOrderWorkflow/shipped": "Đã được giao" + } + } + } + } + } + } +}] diff --git a/src/i18n/zh.json b/src/i18n/zh.json new file mode 100755 index 0000000..b6d77ec --- /dev/null +++ b/src/i18n/zh.json @@ -0,0 +1,97 @@ +[{ + "i18n": "zh", + "ns": "reaction-orders", + "translation": { + "reaction-orders": { + "notFound": "未找到订单", + "order": { + "applyRefundToThisOrder": "申请退还{{currencySymbol}}{{refund}}到这个订单?", + "applyRefundDuringCancelOrder": "这将退还{{currencySymbol}}{{invoiceTotal}}的订单总额", + "cancelOrder": "你想取消这个订单吗?", + "cancelOrderLabel": "取消订单", + "cancelOrderThenRestock": "是的,补货", + "cancelOrderNoRestock": "是的,但没有补货" + }, + "admin": { + "shortcut": { + "ordersLabel": "订单" + }, + "orderRisk": { + "high": "高风险", + "riskCapture": "捕获订单与风险费用?", + "riskCaptureWarn": "您即将捕获带有收费风险的订单。继续之前确认。" + }, + "dashboard": { + "ordersLabel": "订单", + "ordersTitle": "订单", + "ordersDescription": "完成订单", + "clearSearch": "明确" + }, + "orderWorkflow": { + "ordersList": { + "date": "日期", + "orderId": "订单ID", + "total": "合计", + "emailNotFound": "电子邮件不可用" + }, + "summary": { + "cardTitle": "概要", + "copyOrderLink": "复制订单链接" + }, + "invoice": { + "cardTitle": "账单", + "adjustedTotal": "调整后的总金额", + "capturedTotal": "已收款总额", + "refund": "退", + "refundLabel": "退款", + "refundItem": "项目", + "refundItemAmount": "合计", + "refundTotal": "退款总额" + }, + "fulfillment": "履行", + "orderDetails": "订单详细信息", + "shipmentTracking": "发货" + }, + "table": { + "headers": { + "name": "名字", + "email": "电子邮件设置", + "date": "日期", + "id": "ID", + "total": "合计", + "shipping": "运输", + "status": "状态" + }, + "filter": { + "status": "状态", + "dateRange": "日期范围", + "shippingStatus": "运送状态", + "new": "新订单", + "captured": "已收款", + "shipped": "已出货", + "completed": "已完成", + "canceled": "已取消", + "refunded": "已退款" + }, + "search": { + "clearSearch": "明确", + "placeholder": "搜索订单" + }, + "data": { + "status": { + "new": "新", + "coreOrderWorkflow/created": "创建", + "coreOrderWorkflow/processing": "处理", + "coreOrderWorkflow/completed": "已完成", + "coreOrderWorkflow/canceled": "取消", + "coreOrderWorkflow/picked": "采摘的", + "coreOrderWorkflow/packed": "袋装", + "coreOrderWorkflow/labeled": "标记", + "coreOrderWorkflow/shipped": "运" + } + } + } + } + } + } +}] diff --git a/src/index.js b/src/index.js old mode 100644 new mode 100755 index 620cd07..78352e9 --- a/src/index.js +++ b/src/index.js @@ -1,17 +1,16 @@ -import { createRequire } from "module"; - -const require = createRequire(import.meta.url); -const pkg = require("../package.json"); - -/** - * @summary Import and call this function to add this plugin to your API. - * @param {Object} app The ReactionAPI instance - * @returns {undefined} - */ +import pkg from "../package.json"; +import i18n from "./i18n/index.js"; +import resolvers from "./resolvers/index.js"; +import schemas from "./schemas/index.js"; export default async function register(app) { await app.registerPlugin({ - label: pkg.label, - name: pkg.name, - version: pkg.version + label: "Dashboard-stats", + name: "Dashboard-stats", + version: pkg.version, + i18n, + graphQL: { + resolvers, + schemas, + }, }); } diff --git a/src/preStartup.js b/src/preStartup.js new file mode 100755 index 0000000..fd5af11 --- /dev/null +++ b/src/preStartup.js @@ -0,0 +1,11 @@ +import { extendOrdersSchemas } from "./simpleSchemas.js"; + +/** + * @summary Called before startup + * @param {Object} context Startup context + * @param {Object} context.simpleSchemas Map of SimpleSchemas + * @returns {undefined} + */ +export default function ordersPreStartup(context) { + extendOrdersSchemas(context.simpleSchemas); +} diff --git a/src/queries/__snapshots__/refunds.test.js.snap b/src/queries/__snapshots__/refunds.test.js.snap new file mode 100755 index 0000000..8b26724 --- /dev/null +++ b/src/queries/__snapshots__/refunds.test.js.snap @@ -0,0 +1,7 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`throws if orderId isn't supplied 1`] = `"You must provide orderId and shopId arguments"`; + +exports[`throws if shopId isn't supplied 1`] = `"You must provide orderId and shopId arguments"`; + +exports[`throws if the order doesn't exist 1`] = `"Order not found"`; diff --git a/src/queries/__snapshots__/refundsByPaymentId.test.js.snap b/src/queries/__snapshots__/refundsByPaymentId.test.js.snap new file mode 100755 index 0000000..6d06c1e --- /dev/null +++ b/src/queries/__snapshots__/refundsByPaymentId.test.js.snap @@ -0,0 +1,11 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`throws if orderId isn't supplied 1`] = `"You must provide orderId, paymentId, and shopId arguments"`; + +exports[`throws if paymentId isn't supplied 1`] = `"You must provide orderId, paymentId, and shopId arguments"`; + +exports[`throws if shopId isn't supplied 1`] = `"You must provide orderId, paymentId, and shopId arguments"`; + +exports[`throws if the order doesn't exist 1`] = `"Order not found"`; + +exports[`throws if the payment doesn't exist 1`] = `"Payment not found"`; diff --git a/src/queries/dashBoardStats.js b/src/queries/dashBoardStats.js new file mode 100755 index 0000000..e2aa699 --- /dev/null +++ b/src/queries/dashBoardStats.js @@ -0,0 +1,13 @@ +import _ from "lodash"; +// import ReactionError from "@reactioncommerce/reaction-error"; + +export default async function dashBoardStats() { +return { + + productsSold: 10, + productsActive: 5, + productInCart: 3, + productInCheckout: 2, + productPaymentDone: 1, +}; +} diff --git a/src/queries/index.js b/src/queries/index.js new file mode 100644 index 0000000..3762c68 --- /dev/null +++ b/src/queries/index.js @@ -0,0 +1,4 @@ +import dashBoardStats from "../resolvers/Query/dashBoardStats"; +export default { + dashBoardStats, +}; diff --git a/src/resolvers/Query/dashBoardShoppingActivity.js b/src/resolvers/Query/dashBoardShoppingActivity.js new file mode 100755 index 0000000..10a0068 --- /dev/null +++ b/src/resolvers/Query/dashBoardShoppingActivity.js @@ -0,0 +1,19 @@ +import { decodeOrderOpaqueId, decodeShopOpaqueId } from "../../xforms/id.js"; +import ReactionError from "@reactioncommerce/reaction-error"; +export default async function dashBoardShoppingActivity(parent, args, context) { + if (!context.user) { + throw new ReactionError("access-denied", "Access Denied"); + } + const { shopId } = args; + console.log(decodeShopOpaqueId(shopId), "new"); + return { + shopId: shopId, + _id: "123", + productsSold: 10, + productsActive: 5, + productInCart: 3, + productInCheckout: 2, + productPaymentDone: 1, + }; +} + diff --git a/src/resolvers/Query/earningDataGraph.js b/src/resolvers/Query/earningDataGraph.js new file mode 100644 index 0000000..81b697c --- /dev/null +++ b/src/resolvers/Query/earningDataGraph.js @@ -0,0 +1,23 @@ +import { decodeOrderOpaqueId, decodeShopOpaqueId } from "../../xforms/id.js"; +import ReactionError from "@reactioncommerce/reaction-error"; +export default async function earningDataGraph(parent, args, context) { + if (!context.user) { + throw new ReactionError("access-denied", "Access Denied"); + } + const { shopId } = args; + return [ + { + date: "2023-01-01", + earning: 10, + }, + { + date: "2023-01-02", + earning: 15, + }, + { date: "2023-01-01", earning: 8 }, + { + date: "2023-01-02", + earning: 12, + }, + ]; +} diff --git a/src/resolvers/Query/index.js b/src/resolvers/Query/index.js new file mode 100755 index 0000000..b318b9d --- /dev/null +++ b/src/resolvers/Query/index.js @@ -0,0 +1,10 @@ +import dashBoardShoppingActivity from "./dashBoardShoppingActivity.js"; +import productDataGraph from "./productDataGraph.js"; +import earningDataGraph from "./earningDataGraph.js"; +import productsUploaded from "./productsUploaded.js"; +export default { + dashBoardShoppingActivity, + productDataGraph, + productsUploaded, + earningDataGraph, +}; diff --git a/src/resolvers/Query/productDataGraph.js b/src/resolvers/Query/productDataGraph.js new file mode 100644 index 0000000..d3e1d80 --- /dev/null +++ b/src/resolvers/Query/productDataGraph.js @@ -0,0 +1,26 @@ +import { decodeOrderOpaqueId, decodeShopOpaqueId } from "../../xforms/id.js"; +import ReactionError from "@reactioncommerce/reaction-error"; +export default async function productDataGraph(parent, args, context) { + if (!context.user) { + throw new ReactionError("access-denied", "Access Denied"); + } + const { shopId } = args; + return [ + + { + date: "2023-01-01", + + count: 10, + }, + { + date: "2023-01-02", + + count: 15, + }, + { date: "2023-01-01", count: 8 }, + { + date: "2023-01-02", + count: 12, + }, + ]; +} \ No newline at end of file diff --git a/src/resolvers/Query/productsUploaded.js b/src/resolvers/Query/productsUploaded.js new file mode 100644 index 0000000..264f71a --- /dev/null +++ b/src/resolvers/Query/productsUploaded.js @@ -0,0 +1,29 @@ +import ReactionError from "@reactioncommerce/reaction-error"; +export default async function productsUploaded(parent, args, context) { + const { shopId } = args; + if (!context.user) { + throw new ReactionError("access-denied", "Access Denied"); + } + const currentMonth = new Date().getMonth() + 1; + const previousMonth = currentMonth - 1; + + // Dummy data for the current month and previous month + const productUploadData = [ + { date: "2023-06-01", count: 10 }, + { date: "2023-06-05", count: 15 }, + { date: "2023-06-10", count: 8 }, + { date: "2023-05-01", count: 12 }, + { date: "2023-05-05", count: 5 }, + { date: "2023-12-10", count: 18 }, + ]; + + const currentMonthData = productUploadData.filter( + (data) => new Date(data.date).getMonth() + 1 === currentMonth + ); + + const previousMonthData = productUploadData.filter( + (data) => new Date(data.date).getMonth() + 1 === previousMonth + ); + + return { currentMonthData, previousMonthData }; +} diff --git a/src/resolvers/index.js b/src/resolvers/index.js new file mode 100755 index 0000000..9b3c650 --- /dev/null +++ b/src/resolvers/index.js @@ -0,0 +1,5 @@ + +import Query from "./Query/index.js"; +export default { + Query, +}; diff --git a/src/schemas/index.js b/src/schemas/index.js new file mode 100755 index 0000000..30096f9 --- /dev/null +++ b/src/schemas/index.js @@ -0,0 +1,5 @@ +import importAsString from "@reactioncommerce/api-utils/importAsString.js"; + +const schema = importAsString("./schema.graphql"); + +export default [schema]; diff --git a/src/schemas/schema.graphql b/src/schemas/schema.graphql new file mode 100755 index 0000000..d3a4ef3 --- /dev/null +++ b/src/schemas/schema.graphql @@ -0,0 +1,41 @@ +type DashBoardStats implements Node { + "The Order ID" + _id: ID! + productsSold: String + productsActive: String + productInCart: String + productInCheckout: String + productPaymentDone: String +} +type Product { + id: ID! + name: String! +} + +type ProductData { + date: String! + # product: Product! + count: Int! +} +type EarningData { + date: String! + # product: Product! + earning: Int! +} +type ProductUpload { + date: String! + count: Int! +} +type ProductUploads { + currentMonthData: [ProductUpload!]! + previousMonthData: [ProductUpload!]! +} + + +extend type Query { + "Get an order by its ID" + dashBoardShoppingActivity(shopId: ID!): DashBoardStats + productDataGraph: [ProductData!]! + earningDataGraph: [EarningData!]! + productsUploaded: ProductUploads! +} diff --git a/src/simpleSchemas.js b/src/simpleSchemas.js new file mode 100755 index 0000000..e7e842d --- /dev/null +++ b/src/simpleSchemas.js @@ -0,0 +1,1138 @@ +import SimpleSchema from "simpl-schema"; + +const withoutCodeCountries = ["AO", "AG", "AW", "BS", "BZ", "BJ", "BW", + "BF", "BI", "CM", "CF", "KM", "CG", "CD", "CK", "CI", "DJ", + "DM", "GQ", "ER", "FJ", "TF", "GM", "GH", "GD", "GN", "GY", + "HK", "IE", "JM", "KE", "KI", "MO", "MW", "ML", "MR", "MU", + "MS", "NR", "AN", "NU", "KP", "PA", "QA", "RW", "KN", "LC", + "ST", "SA", "SC", "SL", "SB", "SO", "SR", "SY", "TZ", "TL", + "TK", "TO", "TT", "TV", "UG", "AE", "VU", "YE", "ZW"]; + +/** + * @name Metafield + * @memberof Schemas + * @type {SimpleSchema} + * @property {String} key optional + * @property {String} namespace optional + * @property {String} scope optional + * @property {String} value optional + * @property {String} valueType optional + * @property {String} description optional + */ +const Metafield = new SimpleSchema({ + key: { + type: String, + max: 30, + optional: true + }, + namespace: { + type: String, + max: 20, + optional: true + }, + scope: { + type: String, + optional: true + }, + value: { + type: String, + optional: true + }, + valueType: { + type: String, + optional: true + }, + description: { + type: String, + optional: true + } +}); + +/** + * @name OrderAddress + * @memberof Schemas + * @type {SimpleSchema} + * @property {String} _id + * @property {String} fullName required + * @property {String} firstName + * @property {String} lastName + * @property {String} address1 required + * @property {String} address2 + * @property {String} city required + * @property {String} company + * @property {String} phone required + * @property {String} region required, State/Province/Region + * @property {String} postal required + * @property {String} country required + * @property {Boolean} isCommercial required + * @property {Boolean} isBillingDefault required + * @property {Boolean} isShippingDefault required + * @property {Boolean} failedValidation + * @property {Metafield[]} metafields + */ +export const OrderAddress = new SimpleSchema({ + "_id": { + type: String, + optional: true + }, + "fullName": { + type: String, + label: "Full name" + }, + "firstName": { + type: String, + label: "First name", + optional: true + }, + "lastName": { + type: String, + label: "Last name", + optional: true + }, + "address1": { + label: "Address 1", + type: String + }, + "address2": { + label: "Address 2", + type: String, + optional: true + }, + "city": { + type: String, + label: "City" + }, + "company": { + type: String, + label: "Company", + optional: true + }, + "phone": { + type: String, + label: "Phone" + }, + "region": { + label: "State/Province/Region", + type: String + }, + "postal": { + label: "ZIP/Postal Code", + type: String, + optional: true, + custom() { + const country = this.field("country"); + if (country && country.value) { + if (!withoutCodeCountries.includes(country.value) && !this.value) { + return "required"; + } + } + return true; + } + }, + "country": { + type: String, + label: "Country" + }, + "isCommercial": { + label: "This is a commercial address.", + type: Boolean, + defaultValue: false + }, + "isBillingDefault": { + label: "Make this your default billing address?", + type: Boolean, + defaultValue: false, + optional: true + }, + "isShippingDefault": { + label: "Make this your default shipping address?", + type: Boolean, + defaultValue: false, + optional: true + }, + "failedValidation": { + label: "Failed validation", + type: Boolean, + defaultValue: false, + optional: true + }, + "metafields": { + type: Array, + optional: true + }, + "metafields.$": { + type: Metafield + } +}); + +/** + * @name ShippingParcel + * @memberof Schemas + * @type {SimpleSchema} + * @property {String} containers optional + * @property {Number} length optional + * @property {Number} width optional + * @property {Number} height optional + * @property {Number} weight optional + */ +const ShippingParcel = new SimpleSchema({ + containers: { + type: String, + optional: true + }, + length: { + type: Number, + optional: true + }, + width: { + type: Number, + optional: true + }, + height: { + type: Number, + optional: true + }, + weight: { + type: Number, + optional: true + } +}); + +const Money = new SimpleSchema({ + currencyCode: String, + amount: { + type: Number, + min: 0 + } +}); + +/** + * @name CommonOrderItemAttribute + * @memberof Schemas + * @type {SimpleSchema} + * @property {String} label required + * @property {String} value optional + */ +export const CommonOrderItemAttribute = new SimpleSchema({ + label: String, + value: { + type: String, + optional: true + } +}); + +export const CommonOrderItem = new SimpleSchema({ + "_id": String, + "attributes": { + type: Array, + optional: true + }, + "attributes.$": CommonOrderItemAttribute, + "isTaxable": { + type: Boolean, + optional: true + }, + "parcel": { + type: ShippingParcel, + optional: true + }, + "price": Money, + "productId": String, + "productVendor": { + type: String, + optional: true + }, + "quantity": { + type: SimpleSchema.Integer, + min: 0 + }, + "shopId": String, + "subtotal": Money, + "taxCode": { + type: String, + optional: true + }, + "title": String, + "variantId": String, + "variantTitle": { + type: String, + optional: true + }, + "sellerId": { + type: String + } +}); + +const CommonOrderFulfillmentPrices = new SimpleSchema({ + handling: { + type: Money, + optional: true + }, + shipping: { + type: Money, + optional: true + }, + total: { + type: Money, + optional: true + } +}); + +const CommonOrderTotals = new SimpleSchema({ + groupDiscountTotal: { + type: Money, + optional: true + }, + groupItemTotal: { + type: Money, + optional: true + }, + groupTotal: { + type: Money, + optional: true + }, + orderDiscountTotal: { + type: Money, + optional: true + }, + orderItemTotal: { + type: Money, + optional: true + }, + orderTotal: { + type: Money, + optional: true + } +}); + +/** + * @type {SimpleSchema} + * @summary The CommonOrder schema describes an order for a single shop, containing only + * properties that can be provided by a Cart as well. Each fulfillment group in a Cart + * or Order can be transformed into a single CommonOrder. This allows plugins that + * operate on both cart and order to provide only a single function, accepting a CommonOrder, + * where the caller can transform and store the result as necessary for either Cart or Order. + * For example, tax services accept a CommonOrder and calculate taxes without knowing or + * caring whether it is for a Cart or an Order. + */ +export const CommonOrder = new SimpleSchema({ + accountId: { + type: String, + optional: true + }, + billingAddress: { + type: OrderAddress, + optional: true + }, + cartId: { + type: String, + optional: true + }, + currencyCode: String, + fulfillmentMethodId: { + type: String, + optional: true + }, + fulfillmentPrices: CommonOrderFulfillmentPrices, + fulfillmentType: { + type: String, + allowedValues: ["shipping"] + }, + items: [CommonOrderItem], + orderId: { + type: String, + optional: true + }, + originAddress: { + type: OrderAddress, + optional: true + }, + shippingAddress: { + type: OrderAddress, + optional: true + }, + shopId: String, + sourceType: { + type: String, + allowedValues: ["cart", "order"] + }, + totals: { + type: CommonOrderTotals, + optional: true + } +}); + +export const orderItemInputSchema = new SimpleSchema({ + "addedAt": { + type: Date, + optional: true + }, + + "price": Number, + "productConfiguration": Object, + "productConfiguration.productId": String, + "productConfiguration.productVariantId": String, + "quantity": { + type: SimpleSchema.Integer, + min: 1 + } +}); + +export const orderFulfillmentGroupInputSchema = new SimpleSchema({ + "data": { + type: Object, + blackbox: true, + optional: true + }, + "items": { + type: Array, + minCount: 1 + }, + "items.$": orderItemInputSchema, + "selectedFulfillmentMethodId": String, + "shopId": String, + "totalPrice": { + type: Number, + optional: true + }, + "type": { + type: String, + allowedValues: ["shipping"] + } +}); + +// Exported for unit tests +export const orderInputSchema = new SimpleSchema({ + // Although billing address is typically needed only by the payment plugin, + // some tax services require it to calculate taxes for digital items. Thus + // it should be provided here in order to be added to the CommonOrder if possible. + "billingAddress": { + type: OrderAddress, + optional: true + }, + "cartId": { + type: String, + optional: true + }, + "currencyCode": String, + /** + * If you need to store customFields, be sure to add them to your + * GraphQL input schema and your Order SimpleSchema with proper typing. + * This schema need not care what `customFields` is because the input + * and Order schemas will validate. Thus, we use blackbox here. + */ + "customFields": { + type: Object, + blackbox: true, + optional: true + }, + "email": String, + "fulfillmentGroups": { + type: Array, + minCount: 1 + }, + "fulfillmentGroups.$": orderFulfillmentGroupInputSchema, + "ordererPreferredLanguage": { + type: String, + optional: true + }, + "shopId": String +}); + +export const paymentInputSchema = new SimpleSchema({ + amount: Number, + // Optionally override the order.billingAddress for each payment + billingAddress: { + type: OrderAddress, + optional: true + }, + data: { + type: Object, + optional: true, + blackbox: true + }, + method: String +}); + +/** + * @name AnonymousAccessToken + * @memberof Schemas + * @type {SimpleSchema} + * @property {Date} createdAt when token was created + * @property {String} hashedToken token hash = base64(sha256(token-random-string)) + */ +const AnonymousAccessToken = new SimpleSchema({ + createdAt: Date, + hashedToken: String +}); + +/** + * @name Document + * @memberof Schemas + * @type {SimpleSchema} + * @property {String} docId required + * @property {String} docType optional + */ +const Document = new SimpleSchema({ + docId: { + type: String + }, + docType: { + type: String, + optional: true + } +}); + +/** + * @name ExportHistory + * @memberof Schemas + * @type {SimpleSchema} + * @property {String} status (required) Whether the export attempt succeeded or failed + * @property {Date} dateAttempted (required) Date the export was attempted + * @property {String} exportMethod (required) Name of the export method (e.g. CSV, Shopify) + * @property {String} destinationIdentifier The identifier for this order on the remote system + * @property {String} shopId (required) The shop ID + */ +const ExportHistory = new SimpleSchema({ + status: { + type: String, + allowedValues: ["success", "failure"] + }, + dateAttempted: { + type: Date + }, + exportMethod: { + type: String + }, + destinationIdentifier: { + type: String, + optional: true + }, + shopId: { + type: String + } +}); + +/** + * @name History + * @memberof Schemas + * @type {SimpleSchema} + * @property {String} event required + * @property {String} value required + * @property {String} userId required + * @property {String} updatedAt required + */ +const History = new SimpleSchema({ + event: { + type: String + }, + value: { + type: String + }, + userId: { + type: String + }, + updatedAt: { + type: Date + } +}); + +/** + * @name OrderInvoice + * @type {SimpleSchema} + * @memberof Schemas + * @property {Number} discounts Total of all discounts (a positive number, but subtracted from the grand total) + * @property {Number} effectiveTaxRate The effective tax rate, for display + * @property {Number} shipping Price of the selected fulfillment method + * @property {Number} subtotal Item total + * @property {Number} surcharges Total of all surcharges + * @property {Number} taxableAmount Total amount that was deemed taxable by the tax service + * @property {Number} taxes Total tax + * @property {Number} total Grand total + */ +export const OrderInvoice = new SimpleSchema({ + currencyCode: String, + discounts: { + type: Number, + min: 0 + }, + effectiveTaxRate: { + type: Number, + min: 0 + }, + shipping: { + type: Number, + min: 0 + }, + subtotal: { + type: Number, + min: 0 + }, + surcharges: { + type: Number, + min: 0 + }, + taxes: { + type: Number, + min: 0 + }, + taxableAmount: { + type: Number, + min: 0 + }, + total: { + type: Number, + min: 0 + } +}); + +/** + * @name Notes + * @memberof Schemas + * @type {SimpleSchema} + * @property {String} content required + * @property {String} userId required + * @property {Date} updatedAt required + */ +const Notes = new SimpleSchema({ + content: { + type: String + }, + userId: { + type: String + }, + updatedAt: { + type: Date + } +}); + +/** + * @name Workflow + * @summary Control view flow by attaching layout to a collection. + * Shop defaultWorkflow is defined in Shop. + * @memberof Schemas + * @type {SimpleSchema} + * @property {String} status, default value: `new` + * @property {String[]} workflow optional + */ +const Workflow = new SimpleSchema({ + "status": { + type: String, + defaultValue: "new" + }, + "workflow": { + type: Array, + optional: true + }, + "workflow.$": String +}); + +/** + * @name OrderDiscount + * @memberof Schemas + * @type {SimpleSchema} + * @property {Number} amount Amount of discount applied to the order + * @property {String} discountId Discount ID + */ +const OrderDiscount = new SimpleSchema({ + amount: Number, + discountId: String +}); + +/** + * @name OrderItemAttribute + * @memberof Schemas + * @type {SimpleSchema} + * @property {String} label required + * @property {String} value optional + */ +const OrderItemAttribute = new SimpleSchema({ + label: String, + value: { + type: String, + optional: true + } +}); + +/** + * @name OrderItem + * @memberof Schemas + * @summary Defines one item in an order + * @type {SimpleSchema} + * @property {String} _id Unique ID for the item + * @property {String} addedAt Date/time when this was first added to the cart/order + * @property {OrderItemAttribute[]} attributes Attributes of this item + * @property {String} cancelReason Free text reason for cancel, if this item is canceled + * @property {String} createdAt Date/time when this order item was created + * @property {Document[]} documents optional + * @property {History[]} history optional + * @property {String} optionTitle optionTitle from the selected variant + * @property {ShippingParcel} parcel Currently, parcel is in simple product schema. Need to include it here as well. + * @property {Money} price The price+currency of variantId at the moment the related order was placed + * @property {String} productId required + * @property {String} productSlug Product slug + * @property {String} productType Product type + * @property {String} productVendor Product vendor + * @property {Number} quantity required + * @property {String} shopId The owner shop + * @property {Number} subtotal The item subtotal, quantity x price + * @property {String} title Title from the selected product + * @property {String} updatedAt required + * @property {String} variantId required + * @property {String} variantTitle Title from the selected variant + * @property {Workflow} workflow optional + * + */ +export const OrderItem = new SimpleSchema({ + "_id": String, + "addedAt": Date, + "attributes": { + type: Array, + optional: true + }, + "attributes.$": OrderItemAttribute, + "sellerId":{type:String,optional:true}, + "cancelReason": { + type: String, + optional: true + }, + "createdAt": Date, + "documents": { + type: Array, + optional: true + }, + "documents.$": { + type: Document + }, + "history": { + type: Array, + optional: true + }, + "history.$": { + type: History + }, + "optionTitle": { + type: String, + optional: true + }, + "parcel": { + type: ShippingParcel, + optional: true + }, + "price": Money, + "productId": String, + "productSlug": { + type: String, + optional: true + }, + "productType": { + label: "Product Type", + type: String, + optional: true + }, + "productTagIds": { + label: "Product Tags", + type: Array, + optional: true + }, + "productTagIds.$": String, + "productVendor": { + label: "Product Vendor", + type: String, + optional: true + }, + "quantity": { + label: "Quantity", + type: SimpleSchema.Integer, + min: 0 + }, + "shopId": String, + "subtotal": Number, + "title": String, + "updatedAt": Date, + "variantId": { + type: String, + optional: true + }, + "variantTitle": { + type: String, + optional: true + }, + "workflow": { + type: Workflow, + optional: true, + defaultValue: {} + } +}); + +/** + * @name SelectedFulfillmentOption + * @memberof Schemas + * @type {SimpleSchema} + * @property {String} _id Shipment method Id + * @property {String} carrier optional + * @property {String} currencyCode Currency code for interpreting rate and handling + * @property {String} group Group, allowed values: `Ground`, `Priority`, `One Day`, `Free` + * @property {Number} handling optional, default value: `0` + * @property {String} label Public label + * @property {String} name Method name + * @property {Number} rate Rate + */ +const SelectedFulfillmentOption = new SimpleSchema({ + _id: String, + carrier: { + type: String, + optional: true + }, + currencyCode: String, + group: { + type: String, + optional: true + }, + handling: { + type: Number, + min: 0 + }, + label: String, + name: String, + rate: { + type: Number, + min: 0 + } +}); + +/** + * @name OrderFulfillmentGroup Schema + * @memberof Schemas + * @summary One fulfillment group of an order + * @type {SimpleSchema} + * @property {String} _id Group ID + * @property {Object} address Shipping address + * @property {String} customsLabelUrl URL for customs label + * @property {Object} invoice Invoice (same as the one on Payment) + * @property {Object[]} items The order items in this group + * @property {String[]} itemIds For convenience, the _id of all the items + * @property {Object} payment The payment info for this group + * @property {Object} shipmentMethod The fulfillment method that was chosen by the customer + * @property {String} shippingLabelUrl URL for shipping label + * @property {String} shopId The shop that fulfills this group + * @property {Number} totalItemQuantity The total item quantity, sum of all quantities + * @property {String} tracking Tracking reference ID + * @property {String} trackingUrl Tracking URL + * @property {String} type Fulfillment type + * @property {Object} workflow Current status and past statuses for this fulfillment + */ +export const OrderFulfillmentGroup = new SimpleSchema({ + "_id": String, + "address": { + type: OrderAddress, + optional: true + }, + "customsLabelUrl": { + type: String, + optional: true + }, + "invoice": OrderInvoice, + "items": { + type: Array, + minCount: 1 + }, + "items.$": OrderItem, + "itemIds": [String], + "shipmentMethod": SelectedFulfillmentOption, + "shippingLabelUrl": { + type: String, + optional: true + }, + "shopId": String, + "totalItemQuantity": { + type: SimpleSchema.Integer, + min: 1 + }, + "tracking": { + type: String, + optional: true + }, + "trackingUrl": { + type: String, + optional: true + }, + "type": { + type: String, + allowedValues: ["shipping"] + }, + "updatedAt": { + type: Date, + optional: true + }, + "workflow": Workflow +}); + +/** + * @name OrderTransaction Schema + * @memberof Schemas + * @summary Order transactions tie Shipping, Payment, and Inventory transactions + * @type {SimpleSchema} + * @property {String} itemId optional + * @property {String} paymentId optional + * @property {String} shipmentId optional + * @property {String} inventoryId optional + * @property {Date} createdAt required + */ +const OrderTransaction = new SimpleSchema({ + itemId: { + type: String, + optional: true + }, + paymentId: { + type: String, + optional: true + }, + shipmentId: { + type: String, + optional: true + }, + inventoryId: { + type: String, + optional: true + }, + createdAt: { + type: Date + } +}); + + +/** + * @name CurrencyExchangeRate + * @type {SimpleSchema} + * @memberof Schemas + * @property {String} userCurrency, default value: `"USD"` + * @property {Number} exchangeRate optional + */ +const CurrencyExchangeRate = new SimpleSchema({ + userCurrency: { + type: String, + optional: true, + defaultValue: "USD" + }, + exchangeRate: { + type: Number, + optional: true + } +}); + +/** + * @name Payment + * @type {SimpleSchema} + * @memberof Schemas + * @property {String} _id Payment ID + * @property {Address} [address] Billing address + * @property {Number} amount The amount paid or authorized + * @property {String} [cardBrand] The brand of card, if the payment method was a credit card + * @property {CurrencyExchangeRate} [currency] The exchange rate, if the user's currency is different from shop's + * @property {Object} [data] Arbitrary data that the payment method needs + * @property {String} mode "authorize" if still needs to be captured, or "capture" if captured. "cancel" if auth was canceled. + * @property {Invoice} invoice A summary of the totals that make up the full charge amount. Created when the payment is added to an order. + * @property {String} shopId The ID of the shop that is being paid. + */ +export const Payment = new SimpleSchema({ + "_id": { + type: String, + label: "Payment Id" + }, + "address": { + type: OrderAddress, + optional: true + }, + "amount": Number, + "captureErrorCode": { + type: String, + optional: true + }, + "captureErrorMessage": { + type: String, + optional: true + }, + "cardBrand": { + type: String, + optional: true + }, + "createdAt": Date, + "currency": { + type: CurrencyExchangeRate, + optional: true + }, + "currencyCode": String, + "data": { + type: Object, + optional: true, + blackbox: true + }, + "displayName": String, + "method": String, + "mode": String, + "name": String, + "paymentPluginName": String, + "processor": String, + "riskLevel": { + type: String, + optional: true + }, + "shopId": String, + "status": String, + "transactionId": String, + "transactions": { + type: Array + }, + "transactions.$": { + type: Object, + blackbox: true + } +}); + +/** + * @name Order Schema + * @memberof Schemas + * @type {SimpleSchema} + * @summary Order has an array of History, Documents, Notes, Items and OrderTransactions. + * @property {String} _id required + * @property {String} accountId Account ID for account orders, or null for anonymous + * @property {Object[]} anonymousAccessTokens Tokens for accessing anonymous orders, null for account orders + * @property {String} anonymousAccessTokens.hashedToken The hashed value for DB queries + * @property {Date} anonymousAccessTokens.createdAt When the token was created. Expiry is not currently implemented, but this Date is here to support that. + * @property {Address} [billingAddress] Optional billing address + * @property {String} cartId optional For tracking which cart created this order + * @property {Date} createdAt required + * @property {String} currencyCode required + * @property {Object} customFields optional + * @property {Document[]} documents optional + * @property {String} email optional + * @property {Object[]} exportHistory optional + * @property {History[]} history optional + * @property {Notes[]} notes optional + * @property {Payment[]} payments Array of payments + * @property {Shipment[]} shipping Array of fulfillment groups + * @property {String} shopId required The owner shop + * @property {Surcharges[]} surcharges Surcharges applied to this order + * @property {OrderTransaction[]} transactions optional + * @property {Date} updatedAt optional + * @property {Workflow} workflow optional + */ +export const Order = new SimpleSchema({ + "_id": { + type: String, + optional: true + }, + "accountId": { + type: String, + optional: true + }, + "anonymousAccessTokens": { + type: Array, + optional: true + }, + "anonymousAccessTokens.$": AnonymousAccessToken, + // Although billing address is typically needed only by the payment plugin, + // some tax services require it to calculate taxes for digital items. Thus + // it should be provided here in order to be added to the CommonOrder if possible. + "billingAddress": { + type: OrderAddress, + optional: true + }, + "cartId": { + type: String, + optional: true + }, + "createdAt": Date, + "currencyCode": String, + "customFields": { + type: Object, + blackbox: true, + optional: true + }, + "discounts": { + type: Array, + optional: true + }, + "discounts.$": OrderDiscount, + "documents": { + type: Array, + optional: true + }, + "documents.$": Document, + "email": { + type: String, + optional: true, + regEx: SimpleSchema.RegEx.Email + }, + "exportHistory": { + type: Array, + optional: true + }, + "exportHistory.$": ExportHistory, + "history": { + type: Array, + optional: true + }, + "history.$": History, + "notes": { + type: Array, + optional: true + }, + "notes.$": Notes, + "ordererPreferredLanguage": { + type: String, + optional: true + }, + "payments": { + type: Array, + optional: true + }, + "payments.$": Payment, + "referenceId": { + type: String + }, + "shipping": [OrderFulfillmentGroup], + "shopId": String, + "surcharges": { + type: Array, + optional: true + }, + "surcharges.$": { + type: Object, + blackbox: true + }, + "totalItemQuantity": { + type: SimpleSchema.Integer, + min: 1 + }, + "transactions": { + type: Array, + optional: true + }, + "transactions.$": OrderTransaction, + "updatedAt": { + type: Date, + optional: true + }, + "workflow": { + type: Workflow, + optional: true, + defaultValue: {} + } +}); + +/** + * @summary Extend schemas from other plugins + * @param {Object} schemas Schema map from context + * @return {undefined} + */ +export function extendOrdersSchemas(schemas) { + schemas.Shop.extend({ + orderStatusLabels: { + type: Object, + blackbox: true, + optional: true + } + }); +} diff --git a/src/startup.js b/src/startup.js new file mode 100755 index 0000000..66dd482 --- /dev/null +++ b/src/startup.js @@ -0,0 +1,30 @@ +import sendOrderEmail from "./util/sendOrderEmail.js"; +import createNotification from "./util/createNotification.js"; +import getProductbyId from "./util/getProductbyId.js"; + +/** + * @summary Called on startup + * @param {Object} context Startup context + * @param {Object} context.collections Map of MongoDB collections + * @returns {undefined} + */ +export default function ordersStartup(context) { + const { appEvents } = context; + + appEvents.on("afterOrderCreate", ({ order }) => sendOrderEmail(context, order)); + appEvents.on("afterOrderCreate", async ({ order ,createdBy}) =>{ + + let productPurchased= await getProductbyId(context,{productId:order?.shipping[0]?.items[0]?.variantId}) + + createNotification(context,{ + details: null, + from: createdBy, + hasDetails: false, + message: `You have a new order of ${productPurchased.title}`, + status: "unread", + to: productPurchased?.uploadedBy?.userId, + type: "newOrder", + url: `/en/profile/address?activeProfile=seller`}) + + }); +} diff --git a/src/tests/factory.js b/src/tests/factory.js new file mode 100755 index 0000000..36032b9 --- /dev/null +++ b/src/tests/factory.js @@ -0,0 +1,51 @@ +import { createFactoryForSchema, Factory } from "@reactioncommerce/data-factory"; +import { + Catalog, + CatalogProduct, + CatalogProductVariant +} from "@reactioncommerce/api-plugin-catalogs/src/simpleSchemas.js"; + +import { + Shop +} from "@reactioncommerce/api-plugin-shops/src/simpleSchemas.js"; + +import { + CommonOrder, + CommonOrderItem, + Order, + OrderAddress, + OrderFulfillmentGroup, + orderFulfillmentGroupInputSchema, + orderInputSchema, + OrderInvoice, + OrderItem, + orderItemInputSchema, + Payment +} from "../simpleSchemas.js"; + +const schemasToAddToFactory = { + Catalog, + CatalogProduct, + CatalogProductVariant, + CommonOrder, + CommonOrderItem, + Order, + OrderAddress, + OrderFulfillmentGroup, + orderFulfillmentGroupInputSchema, + orderInputSchema, + OrderInvoice, + OrderItem, + orderItemInputSchema, + Payment, + Shop +}; + +// Adds each to `Factory` object. For example, `Factory.Cart` +// will be the factory that builds an object that matches the +// `Cart` schema. +Object.keys(schemasToAddToFactory).forEach((key) => { + createFactoryForSchema(key, schemasToAddToFactory[key]); +}); + +export default Factory; diff --git a/src/util/__snapshots__/verifyPaymentsMatchOrderTotal.test.js.snap b/src/util/__snapshots__/verifyPaymentsMatchOrderTotal.test.js.snap new file mode 100755 index 0000000..30067bc --- /dev/null +++ b/src/util/__snapshots__/verifyPaymentsMatchOrderTotal.test.js.snap @@ -0,0 +1,3 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`throws if does not match 1`] = `"Total of all payments must equal order total"`; diff --git a/src/util/addInvoiceToGroup.js b/src/util/addInvoiceToGroup.js new file mode 100755 index 0000000..8bccce9 --- /dev/null +++ b/src/util/addInvoiceToGroup.js @@ -0,0 +1,49 @@ +import accounting from "accounting-js"; + +/** + * @summary Calculate final shipping, discounts, surcharges, and taxes; builds an invoice object + * with the totals on it; and sets group.invoice. + * @param {String} currencyCode Currency code of totals + * @param {Object} group The fulfillment group to be mutated + * @param {Number} groupDiscountTotal Total discount amount for group + * @param {Number} groupSurchargeTotal Total surcharge amount for group + * @param {Number} taxableAmount Total taxable amount for group + * @param {Number} taxTotal Total tax for group + * @returns {undefined} + */ +export default function addInvoiceToGroup({ + currencyCode, + group, + groupDiscountTotal, + groupSurchargeTotal, + taxableAmount, + taxTotal +}) { + // Items + const itemTotal = +accounting.toFixed(group.items.reduce((sum, item) => (sum + item.subtotal), 0), 3); + + // Taxes + const effectiveTaxRate = taxableAmount > 0 ? taxTotal / taxableAmount : 0; + + // Fulfillment + const shippingTotal = group.shipmentMethod.rate || 0; + const handlingTotal = group.shipmentMethod.handling || 0; + const fulfillmentTotal = shippingTotal + handlingTotal; + + // Totals + // To avoid rounding errors, be sure to keep this calculation the same between here and + // `buildOrderInputFromCart.js` in the client code. + const total = +accounting.toFixed(Math.max(0, itemTotal + fulfillmentTotal + taxTotal + groupSurchargeTotal - groupDiscountTotal), 3); + + group.invoice = { + currencyCode, + discounts: groupDiscountTotal, + effectiveTaxRate, + shipping: fulfillmentTotal, + subtotal: itemTotal, + surcharges: groupSurchargeTotal, + taxableAmount, + taxes: taxTotal, + total + }; +} diff --git a/src/util/addShipmentMethodToGroup.js b/src/util/addShipmentMethodToGroup.js new file mode 100755 index 0000000..3fb2562 --- /dev/null +++ b/src/util/addShipmentMethodToGroup.js @@ -0,0 +1,64 @@ +import ReactionError from "@reactioncommerce/reaction-error"; +import xformOrderGroupToCommonOrder from "./xformOrderGroupToCommonOrder.js"; + +/** + * @summary Sets `shipmentMethod` object for a fulfillment group + * @param {Object} context An object containing the per-request state + * @param {String} [accountId] ID of account that is placing or already did place the order + * @param {Object} [billingAddress] The primary billing address for the order, if known + * @param {String|null} [cartId] ID of the cart from which the order is being placed, if applicable + * @param {String} currencyCode Currency code for all money values + * @param {Number} discountTotal Calculated discount total + * @param {Object} group The fulfillment group to be mutated + * @param {String} orderId ID of existing or new order to which this group will belong + * @param {String} selectedFulfillmentMethodId ID of the fulfillment method option chosen by the user + * @returns {undefined} + */ +export default async function addShipmentMethodToGroup(context, { + accountId, + billingAddress, + cartId, + currencyCode, + discountTotal, + group, + orderId, + selectedFulfillmentMethodId +}) { + const { collections, queries } = context; + + const commonOrder = await xformOrderGroupToCommonOrder({ + accountId, + billingAddress, + cartId, + collections, + currencyCode, + group, + orderId, + discountTotal + }); + + // We are passing commonOrder in here, but we need the finalGroup.shipmentMethod data inside of final order, which doesn't get set until after this + // but we need the data from this in order to set it + const rates = await queries.getFulfillmentMethodsWithQuotes(commonOrder, context); + const errorResult = rates.find((option) => option.requestStatus === "error"); + if (errorResult) { + throw new ReactionError("invalid", errorResult.message); + } + + const selectedFulfillmentMethod = rates.find((rate) => selectedFulfillmentMethodId === rate.method._id); + if (!selectedFulfillmentMethod) { + throw new ReactionError("invalid", "The selected fulfillment method is no longer available." + + " Fetch updated fulfillment options and try creating the order again with a valid method."); + } + + group.shipmentMethod = { + _id: selectedFulfillmentMethod.method._id, + carrier: selectedFulfillmentMethod.method.carrier, + currencyCode, + label: selectedFulfillmentMethod.method.label, + group: selectedFulfillmentMethod.method.group, + name: selectedFulfillmentMethod.method.name, + handling: selectedFulfillmentMethod.handlingPrice, + rate: selectedFulfillmentMethod.rate + }; +} diff --git a/src/util/addTaxesToGroup.js b/src/util/addTaxesToGroup.js new file mode 100755 index 0000000..271bf2b --- /dev/null +++ b/src/util/addTaxesToGroup.js @@ -0,0 +1,48 @@ +import xformOrderGroupToCommonOrder from "./xformOrderGroupToCommonOrder.js"; + +/** + * @summary Adds taxes to a fulfillment group + * @param {Object} context An object containing the per-request state + * @param {String} [accountId] ID of account that is placing or already did place the order + * @param {Object} [billingAddress] The primary billing address for the order, if known + * @param {String|null} [cartId] ID of the cart from which the order is being placed, if applicable + * @param {String} currencyCode Currency code for all money values + * @param {Number} discountTotal Calculated discount total + * @param {Object} group The fulfillment group to be mutated + * @param {String} orderId ID of existing or new order to which this group will belong + * @param {Object} surcharges Surcharges object so it can be passed along + * @returns {Object} An object with `taxTotal` and `taxableAmount` numeric properties + */ +export default async function addTaxesToGroup(context, { + accountId, + billingAddress, + cartId, + currencyCode, + discountTotal, + group, + orderId, + surcharges +}) { + const { collections, mutations } = context; + + const commonOrder = await xformOrderGroupToCommonOrder({ + accountId, + billingAddress, + cartId, + collections, + currencyCode, + group, + orderId, + discountTotal, + surcharges + }); + + // A taxes plugin is expected to add a mutation named `setTaxesOnOrderFulfillmentGroup`. + // If this isn't done, assume 0 tax. + if (typeof mutations.setTaxesOnOrderFulfillmentGroup !== "function") { + return { taxTotal: 0, taxableAmount: 0 }; + } + + // This will mutate `group` to add whatever tax fields the `taxes` plugin has added to the schemas. + return mutations.setTaxesOnOrderFulfillmentGroup(context, { group, commonOrder }); +} diff --git a/src/util/anonymousToken.js b/src/util/anonymousToken.js new file mode 100755 index 0000000..0b73e1b --- /dev/null +++ b/src/util/anonymousToken.js @@ -0,0 +1,21 @@ +import getAnonymousAccessToken from "@reactioncommerce/api-utils/getAnonymousAccessToken.js"; + +/** + * Create a new anonymous access token and add it to an order + * + * @param {Object} context app context + * @param {String} orderId order id + * + * @returns {String} raw token secret + */ +export async function addAnonymousOrderToken(context, orderId) { + const token = getAnonymousAccessToken(); + // We must never store the raw secret in the DB + // So that even if the DB data is compromised, + // the raw secrets are not there + const toStore = { ...token }; + delete toStore.token; + const update = { $push: { anonymousAccessTokens: toStore } }; + await context.collections.Orders.updateOne({ _id: orderId }, update); + return token.token; +} diff --git a/src/util/buildOrderFulfillmentGroupFromInput.js b/src/util/buildOrderFulfillmentGroupFromInput.js new file mode 100755 index 0000000..a7ff513 --- /dev/null +++ b/src/util/buildOrderFulfillmentGroupFromInput.js @@ -0,0 +1,81 @@ +import Random from "@reactioncommerce/random"; +import buildOrderItem from "./buildOrderItem.js"; +import updateGroupTotals from "./updateGroupTotals.js"; + +/** + * @summary Builds an order fulfillment group from fulfillment group input. + * @param {Object} context an object containing the per-request state + * @param {String} [accountId] ID of account placing the order + * @param {Object[]} [additionalItems] Additional already-created order items to push into the group + * items array before calculating shipping, tax, surcharges, and totals. + * @param {Object} [billingAddress] The primary billing address for the order, if known + * @param {String|null} [cartId] ID of the cart from which the order is being placed, if applicable + * @param {String} currencyCode Currency code for all money values + * @param {Number} discountTotal Calculated discount total + * @param {Object} inputGroup Order fulfillment group input. See schema. + * @param {String} orderId ID of existing or new order to which this group will belong + * @param {Object} cart - the cart this order is being created from + * @returns {Promise} The fulfillment group + */ +export default async function buildOrderFulfillmentGroupFromInput(context, { + accountId, + additionalItems, + billingAddress, + cartId, + currencyCode, + discountTotal, + inputGroup, + orderId, + cart +}) { + const { data, items, selectedFulfillmentMethodId, shopId, totalPrice: expectedGroupTotal, type } = inputGroup; + + const group = { + _id: Random.id(), + address: data ? data.shippingAddress : null, + shopId, + type, + workflow: { status: "new", workflow: ["new"] } + }; + + // Build the final order item objects. As part of this, we look up the variant in the system and make sure that + // the price is what the caller expects it to be. + if (items) { + group.items = await Promise.all(items.map((inputItem) => buildOrderItem(context, { currencyCode, inputItem, cart }))); + } else { + group.items = []; + } + + if (Array.isArray(additionalItems) && additionalItems.length) { + group.items.push(...additionalItems); + } + + // Add some more properties for convenience + group.itemIds = group.items.map((item) => item._id); + group.totalItemQuantity = group.items.reduce((sum, item) => sum + item.quantity, 0); + + const { + groupSurcharges, + groupSurchargeTotal, + taxableAmount, + taxTotal + } = await updateGroupTotals(context, { + accountId, + billingAddress, + cartId, + currencyCode, + discountTotal, + expectedGroupTotal, + group, + orderId, + selectedFulfillmentMethodId + }); + + return { + group, + groupSurcharges, + groupSurchargeTotal, + taxableAmount, + taxTotal + }; +} diff --git a/src/util/buildOrderItem.js b/src/util/buildOrderItem.js new file mode 100755 index 0000000..c94da08 --- /dev/null +++ b/src/util/buildOrderItem.js @@ -0,0 +1,145 @@ +import accounting from "accounting-js"; +import Random from "@reactioncommerce/random"; +import ReactionError from "@reactioncommerce/reaction-error"; +import decodeOpaqueId from "@reactioncommerce/api-utils/decodeOpaqueId.js"; + +/** + * @summary Builds an order item + * @param {Object} context an object containing the per-request state + * @param {String} currencyCode The order currency code + * @param {Object} inputItem Order item input. See schema. + * @param {Object} cart - The cart this order is being built from + * @returns {Promise} An order item, matching the schema needed for insertion in the Orders collection + */ +export default async function buildOrderItem(context, { currencyCode, inputItem, cart }) { + const { queries,collections } = context; + const { Bids } = collections; + let { + addedAt, + price, + productConfiguration, + quantity + } = inputItem; + const { productId, productVariantId } = productConfiguration; + + const { + catalogProduct: chosenProduct, + parentVariant, + variant: chosenVariant + } = await queries.findProductAndVariant(context, productId, productVariantId); + + const variantPriceInfo = await queries.getVariantPrice(context, chosenVariant, currencyCode); + let finalPrice = (variantPriceInfo || {}).price; + + // Handle null or undefined price returned. Don't allow sale. + if (!finalPrice && finalPrice !== 0) { + throw new ReactionError("invalid", `Unable to get current price for "${chosenVariant.title || chosenVariant._id}"`); + } + + if (finalPrice !== price) { + let accountId =context.userId; + let is_valid=false; + let decodeProductId = decodeOpaqueId(productId).id; + let decodeVariantId = decodeOpaqueId(productVariantId).id; + if(context.userId){ + console.log('user Id',context.userId); + let activeBids = await Bids.findOne({ + createdBy: accountId, + productId: decodeProductId, + variantId: decodeVariantId + }); + if(activeBids&&activeBids.acceptedOffer){ + console.log("offer accepted"); + var d1 = new Date(); + var d2 = new Date(activeBids.acceptedOffer.validTill); + console.log(d1.getTime() <= d2.getTime()); + if (d1.getTime() <= d2.getTime()) { + is_valid = true; + } + if (is_valid) { + console.log("offer valid"); + if(activeBids.acceptedOffer.amount.amount!==price){ + throw new ReactionError("invalid", `Provided price for the "${chosenVariant.title}" item does not match current published price`); + }else{ + price=activeBids.acceptedOffer.amount.amount; + finalPrice=activeBids.acceptedOffer.amount.amount; + + } + + + } else { + throw new ReactionError("invalid", `Provided price for the "${chosenVariant.title}" item is expired`); + + } + } + } + else{ + throw new ReactionError("invalid", `Provided price for the "${chosenVariant.title}" item does not match current published price`); + + } + } + + const inventoryInfo = await context.queries.inventoryForProductConfiguration(context, { + fields: ["canBackorder", "inventoryAvailableToSell"], + productConfiguration: { + ...productConfiguration, + isSellable: true + }, + shopId: chosenProduct.shopId + }); + + if (!inventoryInfo.canBackorder && (quantity > inventoryInfo.inventoryAvailableToSell)) { + throw new ReactionError("invalid-order-quantity", `Sorry, "${chosenVariant.title}" is out of stock now.`); + } + + // Until we do a more complete attributes revamp, we'll do our best to fudge attributes here. + const attributes = []; + if (parentVariant) { + attributes.push({ + label: parentVariant.attributeLabel, + value: parentVariant.optionTitle + }); + } + attributes.push({ + label: chosenVariant.attributeLabel, + value: chosenVariant.optionTitle + }); + + const now = new Date(); + const newItem = { + _id: Random.id(), + addedAt: addedAt || now, + attributes, + createdAt: now, + optionTitle: chosenVariant && chosenVariant.optionTitle, + parcel: chosenVariant.parcel, + price: { + amount: finalPrice, + currencyCode + }, + productId: chosenProduct.productId, + productSlug: chosenProduct.slug, + sellerId: chosenVariant?.uploadedBy?.userId?chosenVariant?.uploadedBy?.userId:null, + productType: chosenProduct.type, + productTagIds: chosenProduct.tagIds, + productVendor: chosenProduct.vendor, + quantity, + shopId: chosenProduct.shopId, + subtotal: +accounting.toFixed(quantity * finalPrice, 3), + title: chosenProduct.title, + updatedAt: now, + variantId: chosenVariant.variantId, + variantTitle: chosenVariant.title, + workflow: { status: "new", workflow: ["coreOrderWorkflow/created", "coreItemWorkflow/removedFromInventoryAvailableToSell"] } + }; + + let cartItem; + if (cart && cart.items.length) { + cartItem = cart.items.find((cItem) => cItem.productId === newItem.productId); + } + for (const func of context.getFunctionsOfType("mutateNewOrderItemBeforeCreate")) { + await func(context, { chosenProduct, chosenVariant, item: newItem, cartItem }); // eslint-disable-line no-await-in-loop + } + + return newItem; +} diff --git a/src/util/compareExpectedAndActualTotals.js b/src/util/compareExpectedAndActualTotals.js new file mode 100755 index 0000000..1b03b0f --- /dev/null +++ b/src/util/compareExpectedAndActualTotals.js @@ -0,0 +1,23 @@ +import accounting from "accounting-js"; +import ReactionError from "@reactioncommerce/reaction-error"; + +/** + * @summary Compares expected total with actual total to make sure data is correct + * @param {Number} actualTotal Actual total + * @param {Number} expectedTotal Expected total + * @returns {undefined} + */ +export default function compareExpectedAndActualTotals(actualTotal, expectedTotal) { + // In order to prevent mismatch due to rounding, we convert these to strings before comparing. What we really + // care about is, do these match to the specificity that the shopper will see (i.e. to the scale of the currency)? + // No currencies have greater than 3 decimal places, so we'll use 3. + const expectedTotalString = accounting.toFixed(expectedTotal, 3); + const actualTotalString = accounting.toFixed(actualTotal, 3); + + if (expectedTotalString !== actualTotalString) { + throw new ReactionError( + "invalid", + `Client provided total price ${expectedTotalString} for order group, but actual total price is ${actualTotalString}` + ); + } +} diff --git a/src/util/createNotification.js b/src/util/createNotification.js new file mode 100755 index 0000000..92d82c8 --- /dev/null +++ b/src/util/createNotification.js @@ -0,0 +1,48 @@ +import generateUID from "./generateUID.js"; +/** + * + * @method createNotification + * @summary Get all of a Unit's Variants or only a Unit's top level Variants. + * @param {Object} context - an object containing the per-request state + * @param {String} unitOrVariantId - A Unit or top level Unit Variant ID. + * @param {Boolean} topOnly - True to return only a units top level variants. + * @param {Object} args - an object of all arguments that were sent by the client + * @param {Boolean} args.shouldIncludeHidden - Include hidden units in results + * @param {Boolean} args.shouldIncludeArchived - Include archived units in results + * @returns {Promise} Array of Unit Variant objects. + */ +export default async function createNotification(context, args) { + const { collections, pubSub } = context; + const { Notifications } = collections; + const { details, hasDetails, message, status, to, type,url } = args; + let accountId = context.userId; + let new_id = await generateUID(); + + + let insert_obj = { + details, + hasDetails, + message, + status:"unread", + to, + type, + from :accountId, + timeSent:new Date(), + _id: new_id, + url: url?url:"/this/is/url", + }; + let NotificationsAdded = await Notifications.insertOne(insert_obj); + console.log("NotificationsAdded.insertedId",NotificationsAdded.insertedId); + + if (NotificationsAdded.insertedId) { + + let nptif_res= await Notifications.findOne({ _id: NotificationsAdded.insertedId }); + pubSub.publish(`notifications-${to}`, { notifications: nptif_res}); + + console.log(nptif_res) + return await nptif_res + } else { + throw new Error("Something went wrong"); + } + +} diff --git a/src/util/generateUID.js b/src/util/generateUID.js new file mode 100755 index 0000000..aa0a055 --- /dev/null +++ b/src/util/generateUID.js @@ -0,0 +1,25 @@ +/** + * + * @method createOrUpdateProductReview + * @summary Add or update product reviews by users + * @param {Object} context - an object containing the per-request state + * @param {String} unitOrVariantId - A Unit or top level Unit Variant ID. + * @param {Boolean} topOnly - True to return only a units top level variants. + * @param {Object} args - an object of all arguments that were sent by the client + * @returns {Promise} Array of product reviews objects. + */ + export default async function generateUID( + + + ) { var dt = new Date().getTime(); + var uuid = "xxxyxxxxxxxxxxxxxxx".replace( + /[xy]/g, + function (c) { + var r = (dt + Math.random() * 16) % 16 | 0; + dt = Math.floor(dt / 16); + return (c == "x" ? r : (r & 0x3) | 0x8).toString(16); + } + ); + return uuid; + } + \ No newline at end of file diff --git a/src/util/getDataForOrderEmail.js b/src/util/getDataForOrderEmail.js new file mode 100755 index 0000000..f31cc70 --- /dev/null +++ b/src/util/getDataForOrderEmail.js @@ -0,0 +1,212 @@ +import _ from "lodash"; +import formatMoney from "@reactioncommerce/api-utils/formatMoney.js"; +import xformOrderItems from "../xforms/xformOrderItems.js"; +import { addAnonymousOrderToken } from "./anonymousToken.js"; + +/** + * @name formatDateForEmail + * @method + * @private + * @summary helper to generate the order date as a string for emails + * @param {Date} date The date to format + * @returns {String} return date formatted as a MM/DD/YYYY string + */ +function formatDateForEmail(date) { + const emailDate = new Date(date); // Clone date + const year = emailDate.getFullYear(); // get year + const month = emailDate.getMonth() + 1; // get month number + 1 (js has 0 indexed months) + const day = emailDate.getDate(); // get day number (js has 1 indexed days) + + const paddedMonth = month > 9 ? `${month}` : `0${month}`; // generate padded month if necessary + const paddedDay = day > 9 ? `${day}` : `0${day}`; // generate padded days if necessary + + return `${paddedMonth}/${paddedDay}/${year}`; // return MM/DD/YYYY formatted string +} + +/** + * @summary Builds data for rendering order emails + * @param {Object} context - App context + * @param {Object} input - Necessary input + * @param {Object} input.order - The order document + * @returns {Object} Data object to use when rendering email templates + */ +export default async function getDataForOrderEmailDefault(context, { order }) { + const { collections, getAbsoluteUrl } = context; + const { Accounts, Shops } = collections; + + // Get Shop information + const shop = await Shops.findOne({ _id: order.shopId }); + + // Get Account information + let account = null; + if (order.accountId) { + account = await Accounts.findOne({ _id: order.accountId }); + } + + // TODO need to make this fully support multiple fulfillment groups. Now it's just collapsing into one + const amount = order.shipping.reduce((sum, group) => sum + group.invoice.total, 0); + const discounts = order.shipping.reduce((sum, group) => sum + group.invoice.discounts, 0); + const subtotal = order.shipping.reduce((sum, group) => sum + group.invoice.subtotal, 0); + const taxes = order.shipping.reduce((sum, group) => sum + group.invoice.taxes, 0); + const shippingCost = order.shipping.reduce((sum, group) => sum + group.invoice.shipping, 0); + + const { address: shippingAddress, shipmentMethod, tracking } = order.shipping[0]; + const { carrier } = shipmentMethod; + const [firstPayment] = (order.payments || []); + const { address: paymentBillingAddress, currency } = firstPayment || {}; + + const shippingAddressForEmail = shippingAddress ? { + address: `${shippingAddress.address1}${shippingAddress.address2 ? ` ${shippingAddress.address2}` : ""}`, + city: shippingAddress.city, + fullName: shippingAddress.fullName, + postal: shippingAddress.postal, + region: shippingAddress.region + } : null; + + let billingAddressForEmail = null; + if (order.billingAddress) { + billingAddressForEmail = { + address: `${order.billingAddress.address1}${order.billingAddress.address2 ? ` ${order.billingAddress.address2}` : ""}`, + city: order.billingAddress.city, + fullName: order.billingAddress.fullName, + postal: order.billingAddress.postal, + region: order.billingAddress.region + }; + } else if (paymentBillingAddress) { + billingAddressForEmail = { + address: `${paymentBillingAddress.address1}${paymentBillingAddress.address2 ? ` ${paymentBillingAddress.address2}` : ""}`, + city: paymentBillingAddress.city, + fullName: paymentBillingAddress.fullName, + postal: paymentBillingAddress.postal, + region: paymentBillingAddress.region + }; + } + + const refunds = []; + + if (Array.isArray(order.payments)) { + const promises = order.payments.map(async (payment) => { + const shopRefunds = await context.queries.getPaymentMethodConfigByName(payment.name).functions.listRefunds(context, payment); + const shopRefundsWithPaymentId = shopRefunds.map((shopRefund) => ({ ...shopRefund, paymentId: payment._id })); + refunds.push(...shopRefundsWithPaymentId); + }); + await Promise.all(promises); + } + + const refundTotal = refunds.reduce((acc, refund) => acc + refund.amount, 0); + + const userCurrency = (currency && currency.userCurrency) || shop.currency; + + // Get user currency exchange rate at time of transaction + const userCurrencyExchangeRate = (currency && currency.exchangeRate) || 1; + + // Combine same products into single "product" for display purposes + const combinedItems = []; + + // Transform all order items to add images, etc. + const adjustedOrderGroups = await Promise.all(order.shipping.map(async (group) => { + let items = await xformOrderItems(context, group.items); + + items = items.map((item) => ({ + ...item, + placeholderImage: getAbsoluteUrl("/resources/placeholder.gif"), + price: { + ...item.price, + // Add displayAmount to match user currency settings + displayAmount: item.price.amount + }, + subtotal: { + ...item.subtotal, + // Add displayAmount to match user currency settings + displayAmount: item.subtotal.amount, + }, + // These next two are for backward compatibility with existing email templates. + // New templates should use `imageURLs` instead. + productImage: item.imageURLs && item.imageURLs.large, + variantImage: item.imageURLs && item.imageURLs.large + })); + + return { ...group, items }; + })); + + // Loop through all items in the order. The items are split into individual items + const orderItems = adjustedOrderGroups.reduce((list, group) => [...list, ...group.items], []); + for (const orderItem of orderItems) { + // Find an existing item in the combinedItems array + const foundItem = combinedItems.find((combinedItem) => combinedItem.variantId === orderItem.variantId); + + // Increment the quantity count for the duplicate product variants + if (foundItem) { + foundItem.quantity += orderItem.quantity; + } else { + // Otherwise push the unique item into the combinedItems array + combinedItems.push(orderItem); + } + } + + const copyrightDate = new Date().getFullYear(); + + // storefront URLs are technically optional, and headless is OK. + // In that case we'll assume the email template does not use nor need + // the orderUrl property, so it will be null in the order email data object. + let orderUrl = _.get(shop, "storefrontUrls.storefrontOrderUrl", null); + if (orderUrl) { + let token = ""; + orderUrl = orderUrl.replace(":orderId", encodeURIComponent(order.referenceId)); + const isAnonymous = !order.accountId; + const wantsToken = orderUrl.includes(":token"); + if (isAnonymous && wantsToken) { + token = await addAnonymousOrderToken(context, order._id); + } + // Replace :token either with empty string or a toke + orderUrl = orderUrl.replace(":token", encodeURIComponent(token)); + } + + const physicalAddress = (shop.addressBook && shop.addressBook[0]) || null; + if (physicalAddress) { + physicalAddress.address = `${physicalAddress.address1}${physicalAddress.address2 ? ` ${physicalAddress.address2}` : ""}`; + } + + // Merge data into single object to pass to email template + return { + account, + billing: { + address: billingAddressForEmail, + payments: (order.payments || []).map((payment) => ({ + displayName: payment.displayName, + displayAmount: payment.amount, + })), + subtotal: subtotal, + shipping: shippingCost, + taxes: taxes, + discounts: discounts, + refunds: refundTotal, + total: + (subtotal + shippingCost + taxes - discounts) , + adjustedTotal: + (amount - refundTotal) + }, + combinedItems, + contactEmail: shop.emails[0].address, + copyrightDate, + homepage: _.get(shop, "storefrontUrls.storefrontHomeUrl", null), + legalName: _.get(shop, "addressBook[0].company"), + order: { + ...order, + shipping: adjustedOrderGroups + }, + orderDate: formatDateForEmail(order.createdAt), + orderUrl, + physicalAddress, + shipping: { + address: shippingAddressForEmail, + carrier, + tracking + }, + shop, + shopName: shop.name, + socialLinks: { + display: false + } + }; +} diff --git a/src/util/getDataForOrderEmail.test.js b/src/util/getDataForOrderEmail.test.js new file mode 100755 index 0000000..ee0eee1 --- /dev/null +++ b/src/util/getDataForOrderEmail.test.js @@ -0,0 +1,284 @@ +/* eslint-disable require-jsdoc */ +import mockContext from "@reactioncommerce/api-utils/tests/mockContext.js"; +import Factory from "../tests/factory.js"; +import getDataForOrderEmail from "./getDataForOrderEmail.js"; + +mockContext.queries.getPaymentMethodConfigByName = jest.fn().mockName("getPaymentMethodConfigByName").mockImplementation(() => ({ + functions: { + listRefunds: async () => [{ + _id: "refundId", + type: "refund", + amount: 19.99, + currency: "usd" + }] + } +})); + +beforeEach(() => { + jest.clearAllMocks(); +}); + +function setupMocks(mockShop, mockCatalogItem) { + mockContext.collections.Shops.findOne.mockReturnValueOnce(mockShop); + mockContext.collections.Catalog.toArray.mockReturnValueOnce([mockCatalogItem]); + + mockContext.queries.findVariantInCatalogProduct = jest.fn().mockName("findVariantInCatalogProduct"); + mockContext.queries.findVariantInCatalogProduct.mockReturnValueOnce({ + catalogProduct: mockCatalogItem.product, + variant: mockCatalogItem.product.variants[0] + }); +} + +test("returns expected data structure (base case)", async () => { + const mockCatalogItem = Factory.Catalog.makeOne({ + isDeleted: false, + product: Factory.CatalogProduct.makeOne({ + isDeleted: false, + isVisible: true, + variants: Factory.CatalogProductVariant.makeMany(1, { + media: [ + { + priority: 1, + productId: "mockProductId", + variantId: "mockVariantId", + URLs: { + large: "large.jpg", + medium: "medium.jpg", + original: "original.jpg", + small: "small.jpg", + thumbnail: "thumbnail.jpg" + } + } + ], + options: null, + price: 10 + }) + }) + }); + + const mockOrder = Factory.Order.makeOne({ + payments: Factory.Payment.makeMany(1, { + name: "iou_example" + }) + }); + + const mockShop = Factory.Shop.makeOne({ + addressBook: [ + { + address1: "mockAddress1", + address2: "mockAddress2", + city: "mockCity", + company: "mockCompany", + country: "mockCountry", + fullName: "mockFullName", + postal: "mockPostal", + region: "mockRegion" + } + ], + storefrontUrls: { + storefrontHomeUrl: "http://example.com/storefrontHomeUrl", + storefrontOrderUrl: "http://example.com/storefrontOrderUrl/:orderId?token=:token" + } + }); + + setupMocks(mockShop, mockCatalogItem); + + const data = await getDataForOrderEmail(mockContext, { order: mockOrder }); + + expect(data).toEqual({ + billing: { + address: { + address: "mockAddress1 mockAddress2", + city: "mockCity", + fullName: "mockFullName", + postal: "mockPostal", + region: "mockRegion" + }, + adjustedTotal: jasmine.any(String), + discounts: jasmine.any(String), + payments: [ + { + displayAmount: jasmine.any(String), + displayName: "mockDisplayName" + } + ], + refunds: jasmine.any(String), + shipping: jasmine.any(String), + subtotal: jasmine.any(String), + taxes: jasmine.any(String), + total: jasmine.any(String) + }, + combinedItems: [ + { + ...mockOrder.shipping[0].items[0], + placeholderImage: "https://app.mock/resources/placeholder.gif", + price: { + amount: jasmine.any(Number), + currencyCode: "mockCurrencyCode", + displayAmount: jasmine.any(String) + }, + productConfiguration: { + productId: "mockProductId", + productVariantId: "mockVariantId" + }, + subtotal: { + amount: jasmine.any(Number), + currencyCode: "mockCurrencyCode", + displayAmount: jasmine.any(String) + } + } + ], + contactEmail: jasmine.any(String), + copyrightDate: jasmine.any(Number), + homepage: "http://example.com/storefrontHomeUrl", + legalName: "mockCompany", + order: { + ...mockOrder, + shipping: [ + { + ...mockOrder.shipping[0], + items: [ + { + ...mockOrder.shipping[0].items[0], + placeholderImage: "https://app.mock/resources/placeholder.gif", + price: { + amount: jasmine.any(Number), + currencyCode: "mockCurrencyCode", + displayAmount: jasmine.any(String) + }, + productConfiguration: { + productId: "mockProductId", + productVariantId: "mockVariantId" + }, + subtotal: { + amount: jasmine.any(Number), + currencyCode: "mockCurrencyCode", + displayAmount: jasmine.any(String) + } + } + ] + } + ] + }, + orderDate: jasmine.any(String), + orderUrl: "http://example.com/storefrontOrderUrl/mockReferenceId?token=", + physicalAddress: { + address: "mockAddress1 mockAddress2", + address1: "mockAddress1", + address2: "mockAddress2", + city: "mockCity", + company: "mockCompany", + country: "mockCountry", + fullName: "mockFullName", + postal: "mockPostal", + region: "mockRegion" + }, + shipping: { + address: { + address: "mockAddress1 mockAddress2", + city: "mockCity", + fullName: "mockFullName", + postal: "mockPostal", + region: "mockRegion" + }, + carrier: "mockCarrier", + tracking: "mockTracking" + }, + shop: mockShop, + shopName: "mockName", + socialLinks: { + display: false + } + }); +}); + +test("storefrontUrls is optional", async () => { + const mockCatalogItem = Factory.Catalog.makeOne({ + isDeleted: false, + product: Factory.CatalogProduct.makeOne({ + isDeleted: false, + isVisible: true, + variants: Factory.CatalogProductVariant.makeMany(1, { + media: [ + { + priority: 1, + productId: "mockProductId", + variantId: "mockVariantId", + URLs: { + large: "large.jpg", + medium: "medium.jpg", + original: "original.jpg", + small: "small.jpg", + thumbnail: "thumbnail.jpg" + } + } + ], + options: null, + price: 10 + }) + }) + }); + + const mockOrder = Factory.Order.makeOne({ + payments: Factory.Payment.makeMany(1, { + name: "iou_example" + }) + }); + + const mockShop = Factory.Shop.makeOne({ + storefrontUrls: {} + }); + + setupMocks(mockShop, mockCatalogItem); + + const data = await getDataForOrderEmail(mockContext, { order: mockOrder }); + expect(data.homepage).toBeNull(); + expect(data.orderUrl).toBeNull(); +}); + +test("storefrontUrls does not use :token", async () => { + const mockCatalogItem = Factory.Catalog.makeOne({ + isDeleted: false, + product: Factory.CatalogProduct.makeOne({ + isDeleted: false, + isVisible: true, + variants: Factory.CatalogProductVariant.makeMany(1, { + media: [ + { + priority: 1, + productId: "mockProductId", + variantId: "mockVariantId", + URLs: { + large: "large.jpg", + medium: "medium.jpg", + original: "original.jpg", + small: "small.jpg", + thumbnail: "thumbnail.jpg" + } + } + ], + options: null, + price: 10 + }) + }) + }); + + const mockOrder = Factory.Order.makeOne({ + payments: Factory.Payment.makeMany(1, { + name: "iou_example" + }) + }); + delete mockOrder.accountId; + const mockShop = Factory.Shop.makeOne({ + storefrontUrls: { + storefrontHomeUrl: "http://example.com/storefrontHomeUrl", + storefrontOrderUrl: "http://example.com/storefrontOrderUrl/:orderId" + } + }); + + setupMocks(mockShop, mockCatalogItem); + + const data = await getDataForOrderEmail(mockContext, { order: mockOrder }); + expect(data.homepage).toBe(mockShop.storefrontUrls.storefrontHomeUrl); + expect(data.orderUrl).toBe(`http://example.com/storefrontOrderUrl/${mockOrder.referenceId}`); +}); diff --git a/src/util/getOrderQuery.js b/src/util/getOrderQuery.js new file mode 100755 index 0000000..3ebb061 --- /dev/null +++ b/src/util/getOrderQuery.js @@ -0,0 +1,42 @@ +import hashToken from "@reactioncommerce/api-utils/hashToken.js"; +import ReactionError from "@reactioncommerce/reaction-error"; + +/** + * @name getOrderQuery + * @method + * @memberof Order/helpers + * @summary Queries for an order and returns it if user has correct permissions + * @param {Object} context An object containing the per-request state + * @param {Object} selector Order ID or Reference ID to query + * @param {String} shopId Shop ID of the order + * @param {String} token An anonymous order token, required if the order was placed without being logged in + * @returns {Object} An order object + */ +export async function getOrderQuery(context, selector, shopId, token) { + const { collections } = context; + + const order = await collections.Orders.findOne(selector); + + if (!order) { + throw new ReactionError("not-found", "Order not found"); + } + + // If you have the hashed token, you don't need to pass a permission check + if (token && order.anonymousAccessTokens.some((accessToken) => accessToken.hashedToken === hashToken(token))) { + return order; + } + + // if you don't have the hashed token, + // you must either have `reaction:legacy:orders/read` permissions, + // or this must be your own order + await context.validatePermissions( + "reaction:legacy:orders", + "read", + { + shopId, + owner: order.accountId + } + ); + + return order; +} diff --git a/src/util/getOrderQuery.test.js b/src/util/getOrderQuery.test.js new file mode 100755 index 0000000..c8261a4 --- /dev/null +++ b/src/util/getOrderQuery.test.js @@ -0,0 +1,68 @@ +/* eslint-disable require-jsdoc */ +import hashToken from "@reactioncommerce/api-utils/hashToken.js"; +import mockContext from "@reactioncommerce/api-utils/tests/mockContext.js"; +import { getOrderQuery } from "./getOrderQuery.js"; + +function makeContext() { + return { accountId: "unit-test-account-id", userHasPermission: () => true }; +} +mockContext.validatePermissions = jest.fn("validatePermissions"); +mockContext.collections.Groups.insert = jest.fn("collections.Groups.insertOne"); +mockContext.collections.Groups.findOne = jest.fn("collections.Groups.findOne"); +mockContext.accountId = "unit-test-account-id"; +mockContext.userHasPermission = () => true; + +test("getOrderQuery, user with `reaction:legacy:orders/read` permissions", async () => { + const result = { + _id: "unit-test-order-id", + shopId: "unit-test-shop-id" + }; + const shopId = "unit-test-shop-id"; + const orderId = "unit-test-order-id"; + mockContext.collections.Orders.findOne.mockReturnValueOnce(Promise.resolve(result)); + mockContext.validatePermissions.mockReturnValueOnce(Promise.resolve(undefined)); + const query = await getOrderQuery(mockContext, { _id: orderId }, shopId, null); + expect(query).toMatchObject(result); + expect(query.accountId).toBeUndefined(); +}); + +test("getOrderQuery, user owns order", async () => { + const result = { + _id: "unit-test-order-id", + accountId: "account-id", + shopId: "unit-test-shop-id" + }; + const shopId = "unit-test-shop-id"; + const orderId = "unit-test-order-id"; + mockContext.collections.Orders.findOne.mockReturnValueOnce(Promise.resolve(result)); + mockContext.validatePermissions.mockReturnValueOnce(Promise.resolve(undefined)); + const query = await getOrderQuery(mockContext, { _id: orderId }, shopId, null); + expect(query).toMatchObject(result); + expect(query.accountId).toEqual(result.accountId); +}); + +test("getOrderQuery anonymous with token", async () => { + const result = { + referenceId: "unit-test-order-reference-id", + accountId: "account-id", + shopId: "unit-test-shop-id", + anonymousAccessTokens: [{ hashedToken: hashToken("unit-test-token") }] + }; + + mockContext.collections.Orders.findOne.mockReturnValueOnce(Promise.resolve(result)); + mockContext.validatePermissions.mockReturnValueOnce(Promise.resolve(undefined)); + const token = "unit-test-token"; + const query = await getOrderQuery(mockContext, { referenceId: result.referenceId }, result.shopId, token); + expect(query).toMatchObject(result); + expect(query.anonymousAccessTokens[0].hashedToken).toBe(hashToken(token)); +}); + +test("getOrderQuery access denied", async () => { + const shopId = "unit-test-shop-id"; + const referenceId = "unit-test-order-reference-id"; + const context = makeContext(); + context.userHasPermission = () => false; + delete context.accountId; + const query = getOrderQuery(mockContext, { referenceId }, shopId, null); + expect(query).rejects.toThrow(); +}); diff --git a/src/util/getProductbyId.js b/src/util/getProductbyId.js new file mode 100755 index 0000000..6dc87c1 --- /dev/null +++ b/src/util/getProductbyId.js @@ -0,0 +1,19 @@ +/** + * + * @method placeBidOnProduct + * @summary Get all of a Unit's Variants or only a Unit's top level Variants. + * @param {Object} context - an object containing the per-request state + * @param {String} unitOrVariantId - A Unit or top level Unit Variant ID. + * @param {Boolean} topOnly - True to return only a units top level variants. + * @param {Object} args - an object of all arguments that were sent by the client + * @param {Boolean} args.shouldIncludeHidden - Include hidden units in results + * @param {Boolean} args.shouldIncludeArchived - Include archived units in results + * @returns {Promise} Array of Unit Variant objects. + */ +export default async function getProductbyId(context, args,bid) { + const { collections } = context; + const { Products } = collections; + const {productId} = args; + let product= await Products.findOne({"_id":productId}); + return product; +} diff --git a/src/util/getSurchargesForGroup.js b/src/util/getSurchargesForGroup.js new file mode 100755 index 0000000..24fd3f0 --- /dev/null +++ b/src/util/getSurchargesForGroup.js @@ -0,0 +1,55 @@ +import xformOrderGroupToCommonOrder from "./xformOrderGroupToCommonOrder.js"; + +/** + * @summary Gets surcharge information for a fulfillment group + * @param {Object} context An object containing the per-request state + * @param {String} [accountId] ID of account that is placing or already did place the order + * @param {Object} [billingAddress] The primary billing address for the order, if known + * @param {String|null} [cartId] ID of the cart from which the order is being placed, if applicable + * @param {String} currencyCode Currency code for all money values + * @param {Number} discountTotal Calculated discount total + * @param {Object} group The fulfillment group to be mutated + * @param {String} orderId ID of existing or new order to which this group will belong + * @returns {undefined} + */ +export default async function getSurchargesForGroup(context, { + accountId, + billingAddress, + cartId, + currencyCode, + discountTotal, + group, + orderId +}) { + const { collections, getFunctionsOfType } = context; + + // Get surcharges to apply to group, if applicable + const commonOrder = await xformOrderGroupToCommonOrder({ + accountId, + billingAddress, + cartId, + collections, + currencyCode, + group, + orderId, + discountTotal + }); + + const groupSurcharges = []; + for (const func of getFunctionsOfType("getSurcharges")) { + const appliedSurcharges = await func(context, { commonOrder }); // eslint-disable-line + for (const appliedSurcharge of appliedSurcharges) { + // Set fulfillmentGroupId + appliedSurcharge.fulfillmentGroupId = group._id; + // Push to group surcharge array + groupSurcharges.push(appliedSurcharge); + } + } + + const groupSurchargeTotal = groupSurcharges.reduce((sum, surcharge) => sum + surcharge.amount, 0); + + return { + groupSurcharges, + groupSurchargeTotal + }; +} diff --git a/src/util/sendOrderEmail.js b/src/util/sendOrderEmail.js new file mode 100755 index 0000000..4a11e0c --- /dev/null +++ b/src/util/sendOrderEmail.js @@ -0,0 +1,55 @@ +import Logger from "@reactioncommerce/logger"; + +/** + * @summary Sends an email about an order. + * @param {Object} context App context + * @param {Object} order - The order document + * @param {String} [action] - The action triggering the email + * @returns {Boolean} True if sent; else false + */ +export default async function sendOrderEmail(context, order, action) { + // anonymous account orders without emails. + const to = order.email; + if (!to) { + Logger.info("No order email found. No email sent."); + return false; + } + + const dataForEmail = {}; + const getDataForOrderEmailFns = context.getFunctionsOfType("getDataForOrderEmail"); + for (const getDataForOrderEmailFn of getDataForOrderEmailFns) { + const someData = await getDataForOrderEmailFn(context, { order }); // eslint-disable-line no-await-in-loop + Object.assign(dataForEmail, someData); + } + + const language = await getLanguageForOrder(context, order); + + await context.mutations.sendOrderEmail(context, { + action, + dataForEmail, + fromShop: dataForEmail.shop, + language, + to + }); + + return true; +} + +/** + * @summary Returns language to be used for order emails. + * If cart is account based and has set language + * then returns that language, else order language. + * @param {Object} context App context + * @param {Object} order - The order document + * @returns {String} i18n language code + */ +async function getLanguageForOrder(context, { ordererPreferredLanguage, accountId }) { + const { collections: { Accounts } } = context; + // if order is anonymous return order language + if (!accountId) { + return ordererPreferredLanguage; + } + + const account = await Accounts.findOne({ _id: accountId }, { "profile.language": 1 }); + return (account && account.profile && account.profile.language) || ordererPreferredLanguage; +} diff --git a/src/util/updateGroupStatusFromItemStatus.js b/src/util/updateGroupStatusFromItemStatus.js new file mode 100755 index 0000000..5e272d8 --- /dev/null +++ b/src/util/updateGroupStatusFromItemStatus.js @@ -0,0 +1,20 @@ +const canceledStatus = "coreOrderWorkflow/canceled"; +const itemCanceledStatus = "coreOrderItemWorkflow/canceled"; + +/** + * @summary Given a fulfillment group, determines and set the correct + * current status on it based on the status of all the items in the + * group. Mutates the group object if necessary + * @param {Object} group An order fulfillment group + * @returns {undefined} + */ +export default function updateGroupStatusFromItemStatus(group) { + // If all items are canceled, set the group status to canceled + const allItemsAreCanceled = group.items.every((item) => item.workflow.status === itemCanceledStatus); + if (allItemsAreCanceled && group.workflow.status !== canceledStatus) { + group.workflow = { + status: canceledStatus, + workflow: [...group.workflow.workflow, canceledStatus] + }; + } +} diff --git a/src/util/updateGroupTotals.js b/src/util/updateGroupTotals.js new file mode 100755 index 0000000..48c4ba9 --- /dev/null +++ b/src/util/updateGroupTotals.js @@ -0,0 +1,102 @@ +import addInvoiceToGroup from "./addInvoiceToGroup.js"; +import addShipmentMethodToGroup from "./addShipmentMethodToGroup.js"; +import addTaxesToGroup from "./addTaxesToGroup.js"; +import compareExpectedAndActualTotals from "./compareExpectedAndActualTotals.js"; +import getSurchargesForGroup from "./getSurchargesForGroup.js"; + +/** + * @summary Call this with a fulfillment group when the items, item quantities, or + * something else relevant about the group may have changed. All shipping, tax, + * and surcharge values will be recalculated and invoice totals updated. + * @param {Object} context App context + * @param {String} [accountId] ID of account that is placing or already did place the order + * @param {Object} [billingAddress] The primary billing address for the order, if known + * @param {String} [cartId] ID of the cart from which the order is being placed, if applicable + * @param {String} currencyCode Currency code for all money values + * @param {Number} [discountTotal] Calculated discount total + * @param {Number} [expectedGroupTotal] Expected total, if you want to verify the calculated total matches + * @param {Object} group The fulfillment group to mutate + * @param {String} orderId ID of existing or new order to which this group will belong + * @param {String} selectedFulfillmentMethodId ID of the fulfillment method option chosen by the user + * @returns {Promise} Object with surcharge and tax info on it + */ +export default async function updateGroupTotals(context, { + accountId, + billingAddress = null, + cartId = null, + currencyCode, + discountTotal = 0, + expectedGroupTotal, + group, + orderId, + selectedFulfillmentMethodId +}) { + // Apply shipment method + await addShipmentMethodToGroup(context, { + accountId, + billingAddress, + cartId, + currencyCode, + discountTotal, + group, + orderId, + selectedFulfillmentMethodId + }); + + const { + groupSurcharges, + groupSurchargeTotal + } = await getSurchargesForGroup(context, { + accountId, + billingAddress, + cartId, + currencyCode, + discountTotal, + group, + orderId, + selectedFulfillmentMethodId + }); + + // Calculate and set taxes. Mutates group object in addition to returning the totals. + const { taxTotal, taxableAmount } = await addTaxesToGroup(context, { + accountId, + billingAddress, + cartId, + currencyCode, + discountTotal, + group, + orderId, + surcharges: groupSurcharges + }); + + // Build and set the group invoice + addInvoiceToGroup({ + currencyCode, + group, + groupDiscountTotal: discountTotal, + groupSurchargeTotal, + taxableAmount, + taxTotal + }); + + if (expectedGroupTotal) { + // For now we expect that the client has NOT included discounts in the expected total it sent. + // Note that we don't currently know which parts of `discountTotal` go with which fulfillment groups. + // This needs to be rewritten soon for discounts to work when there are multiple fulfillment groups. + // Probably the client should be sending all applied discount IDs and amounts in the order input (by group), + // and include total discount in `groupInput.totalPrice`, and then we simply verify that they are valid here. + const expectedTotal = Math.max(expectedGroupTotal - discountTotal, 0); + + // Compare expected and actual totals to make sure client sees correct calculated price + // Error if we calculate total price differently from what the client has shown as the preview. + // It's important to keep this after adding and verifying the shipmentMethod and order item prices. + compareExpectedAndActualTotals(group.invoice.total, expectedTotal); + } + + return { + groupSurcharges, + groupSurchargeTotal, + taxableAmount, + taxTotal + }; +} diff --git a/src/util/verifyPaymentsMatchOrderTotal.js b/src/util/verifyPaymentsMatchOrderTotal.js new file mode 100755 index 0000000..7eb1ebc --- /dev/null +++ b/src/util/verifyPaymentsMatchOrderTotal.js @@ -0,0 +1,27 @@ +import accounting from "accounting-js"; +import Logger from "@reactioncommerce/logger"; +import ReactionError from "@reactioncommerce/reaction-error"; + +/** + * @summary Given an array of payment input and an order total, + * checks that the sum of all payment amounts matches the order total. + * Throws a ReactionError if not. + * @param {Object[]} paymentsInput Array of PaymentInput objects, potentially empty + * @param {Number} orderTotal The grand total of the order these payments are for. + * @returns {undefined} + */ +export default function verifyPaymentsMatchOrderTotal(paymentsInput, orderTotal) { + const paymentTotal = paymentsInput.reduce((sum, paymentInput) => sum + paymentInput.amount, 0); + + // In order to prevent mismatch due to rounding, we convert these to strings before comparing. What we really + // care about is, do these match to the specificity that the shopper will see (i.e. to the scale of the currency)? + // No currencies have greater than 3 decimal places, so we'll use 3. + const paymentTotalString = accounting.toFixed(paymentTotal, 3); + const orderTotalString = accounting.toFixed(orderTotal, 3); + + if (paymentTotalString !== orderTotalString) { + Logger.debug("Error creating payments for a new order. " + + `Order total (${orderTotalString}) does not match total of all payment amounts (${paymentTotalString}).`); + throw new ReactionError("payment-failed", "Total of all payments must equal order total"); + } +} diff --git a/src/util/verifyPaymentsMatchOrderTotal.test.js b/src/util/verifyPaymentsMatchOrderTotal.test.js new file mode 100755 index 0000000..b557794 --- /dev/null +++ b/src/util/verifyPaymentsMatchOrderTotal.test.js @@ -0,0 +1,29 @@ +import verifyPaymentsMatchOrderTotal from "./verifyPaymentsMatchOrderTotal.js"; + +test("throws if does not match", () => { + const payments = [ + { amount: 5 } + ]; + + expect(() => verifyPaymentsMatchOrderTotal(payments, 6)).toThrowErrorMatchingSnapshot(); +}); + +test("does not throw if matches", () => { + const payments = [ + { amount: 5 } + ]; + + expect(() => verifyPaymentsMatchOrderTotal(payments, 5)).not.toThrow(); +}); + +test("is not confused by JavaScript floating point math errors", () => { + // Do 9.99+50+40 in JS console. Result is 99.99000000000001 due to inaccuracy + // of JS floating point math. + const payments = [ + { amount: 9.99 }, + { amount: 50 }, + { amount: 40 } + ]; + + expect(() => verifyPaymentsMatchOrderTotal(payments, 99.99)).not.toThrow(); +}); diff --git a/src/util/xformOrderGroupToCommonOrder.js b/src/util/xformOrderGroupToCommonOrder.js new file mode 100755 index 0000000..75e12d9 --- /dev/null +++ b/src/util/xformOrderGroupToCommonOrder.js @@ -0,0 +1,127 @@ +import accounting from "accounting-js"; + +/** + * @param {String} accountId Account Id + * @param {Object} [billingAddress] Billing address, if one was collected + * @param {String} [cartId] The source cart ID, if applicable + * @param {Object} collections Map of MongoDB collections + * @param {String} currencyCode The currency code + * @param {Object} group The order fulfillment group + * @param {String} orderId The order ID + * @param {Number} discountTotal Discount Total + * @param {Array} surcharges An array of Surcharges + * @returns {Object} Valid CommonOrder for the given order group + */ +export default async function xformOrderGroupToCommonOrder({ + accountId = null, + billingAddress = null, + cartId, + collections, + currencyCode, + group, + orderId, + discountTotal, + surcharges = [] + +}) { + // ** If you add any data here, be sure to add the same data to the matching xformCartGroupToCommonOrder xform + const items = group.items.map((item) => ({ + _id: item._id, + attributes: item.attributes, + isTaxable: item.isTaxable, + parcel: item.parcel, + price: item.price, + productId: item.productId, + productVendor: item.productVendor, + quantity: item.quantity, + shopId: item.shopId, + subtotal: { + amount: +accounting.toFixed(item.price.amount * item.quantity, 3), + currencyCode + }, + taxCode: item.taxCode, + title: item.title, + variantId: item.variantId, + variantTitle: item.variantTitle + })); + + const { address, shipmentMethod, shopId, type: fulfillmentType } = group; + const shop = await collections.Shops.findOne({ _id: shopId }); + + let fulfillmentPrices = { + handling: null, + shipping: null, + total: null + }; + let fulfillmentMethodId; + + if (shipmentMethod) { + fulfillmentPrices = { + handling: { + amount: shipmentMethod.handling || 0, + currencyCode + }, + shipping: { + amount: shipmentMethod.rate || 0, + currencyCode + }, + total: { + amount: +accounting.toFixed((shipmentMethod.handling || 0) + (shipmentMethod.rate || 0), 3), + currencyCode + } + }; + + fulfillmentMethodId = shipmentMethod._id; + } + + // TODO: In the future, we should update this with a discounts update + // Discounts are stored as the sum of all discounts, per cart. This will need to be updated when we refactor discounts to go by group. + const groupItemTotal = +accounting.toFixed(group.items.reduce((sum, item) => (sum + item.subtotal), 0), 3); + // orderItemTotal will need to be updated to be the actual total when we eventually have more than one group available + const orderItemTotal = groupItemTotal; + + const totals = { + groupDiscountTotal: { + amount: discountTotal, + currencyCode + }, + groupItemTotal: { + amount: groupItemTotal, + currencyCode + }, + groupTotal: { + amount: +accounting.toFixed(groupItemTotal - discountTotal, 3), + currencyCode + }, + orderDiscountTotal: { + amount: discountTotal, + currencyCode + }, + orderItemTotal: { + amount: orderItemTotal, + currencyCode + }, + orderTotal: { + amount: +accounting.toFixed(orderItemTotal - discountTotal, 3), + currencyCode + } + }; + + return { + accountId, + billingAddress, + cartId, + currencyCode, + fulfillmentMethodId, + fulfillmentPrices, + fulfillmentType, + items, + orderId, + originAddress: (shop && Array.isArray(shop.addressBook) && shop.addressBook[0]) || null, + shippingAddress: address || null, + shopId, + sourceType: "order", + totals, + surcharges + }; +} diff --git a/src/xforms/id.js b/src/xforms/id.js new file mode 100755 index 0000000..f6af97b --- /dev/null +++ b/src/xforms/id.js @@ -0,0 +1,50 @@ +import decodeOpaqueIdForNamespace from "@reactioncommerce/api-utils/decodeOpaqueIdForNamespace.js"; +import encodeOpaqueId from "@reactioncommerce/api-utils/encodeOpaqueId.js"; + +const namespaces = { + Account: "reaction/account", + Cart: "reaction/cart", + FulfillmentMethod: "reaction/fulfillmentMethod", + Order: "reaction/order", + OrderFulfillmentGroup: "reaction/orderFulfillmentGroup", + OrderItem: "reaction/orderItem", + Payment: "reaction/payment", + Product: "reaction/product", + Refund: "reaction/refund", + Shop: "reaction/shop" +}; + +export const encodeAccountOpaqueId = encodeOpaqueId(namespaces.Account); +export const encodeCartOpaqueId = encodeOpaqueId(namespaces.Cart); +export const encodeOrderFulfillmentGroupOpaqueId = encodeOpaqueId(namespaces.OrderFulfillmentGroup); +export const encodeOrderItemOpaqueId = encodeOpaqueId(namespaces.OrderItem); +export const encodeOrderOpaqueId = encodeOpaqueId(namespaces.Order); +export const encodePaymentOpaqueId = encodeOpaqueId(namespaces.Payment); +export const encodeProductOpaqueId = encodeOpaqueId(namespaces.Product); +export const encodeRefundOpaqueId = encodeOpaqueId(namespaces.Refund); +export const encodeShopOpaqueId = encodeOpaqueId(namespaces.Shop); + +export const decodeAccountOpaqueId = decodeOpaqueIdForNamespace(namespaces.Account); +export const decodeCartOpaqueId = decodeOpaqueIdForNamespace(namespaces.Cart); +export const decodeFulfillmentMethodOpaqueId = decodeOpaqueIdForNamespace(namespaces.FulfillmentMethod); +export const decodeOrderFulfillmentGroupOpaqueId = decodeOpaqueIdForNamespace(namespaces.OrderFulfillmentGroup); +export const decodeOrderItemOpaqueId = decodeOpaqueIdForNamespace(namespaces.OrderItem); +export const decodeOrderOpaqueId = decodeOpaqueIdForNamespace(namespaces.Order); +export const decodePaymentOpaqueId = decodeOpaqueIdForNamespace(namespaces.Payment); +export const decodeProductOpaqueId = decodeOpaqueIdForNamespace(namespaces.Product); +export const decodeRefundOpaqueId = decodeOpaqueIdForNamespace(namespaces.Refund); +export const decodeShopOpaqueId = decodeOpaqueIdForNamespace(namespaces.Shop); + +/** + * @param {Object[]} items Array of OrderItemInput + * @returns {Object[]} Same array with all IDs transformed to internal + */ +export function decodeOrderItemsOpaqueIds(items) { + return items.map((item) => ({ + ...item, + productConfiguration: { + productId: decodeProductOpaqueId(item.productConfiguration.productId), + productVariantId: decodeProductOpaqueId(item.productConfiguration.productVariantId) + } + })); +} diff --git a/src/xforms/xformOrderFulfillmentGroupSelectedOption.js b/src/xforms/xformOrderFulfillmentGroupSelectedOption.js new file mode 100755 index 0000000..4ef4ef3 --- /dev/null +++ b/src/xforms/xformOrderFulfillmentGroupSelectedOption.js @@ -0,0 +1,26 @@ +/** + * @summary Transform a single fulfillment group fulfillment option + * @param {Object} fulfillmentOption The group.shipmentMethod + * @returns {Object} Transformed fulfillment option + */ +export default function xformOrderFulfillmentGroupSelectedOption(fulfillmentOption) { + return { + fulfillmentMethod: { + _id: fulfillmentOption._id, + carrier: fulfillmentOption.carrier || null, + displayName: fulfillmentOption.label || fulfillmentOption.name, + group: fulfillmentOption.group || null, + name: fulfillmentOption.name, + // For now, this is always shipping. Revisit when adding download, pickup, etc. types + fulfillmentTypes: ["shipping"] + }, + handlingPrice: { + amount: fulfillmentOption.handling || 0, + currencyCode: fulfillmentOption.currencyCode + }, + price: { + amount: fulfillmentOption.rate || 0, + currencyCode: fulfillmentOption.currencyCode + } + }; +} diff --git a/src/xforms/xformOrderItems.js b/src/xforms/xformOrderItems.js new file mode 100755 index 0000000..8c5971f --- /dev/null +++ b/src/xforms/xformOrderItems.js @@ -0,0 +1,24 @@ +/** + * @param {Object} context - an object containing the per-request state + * @param {Object[]} items Array of order fulfillment group items + * @returns {Object[]} Same array with GraphQL-only props added + */ +export default async function xformOrderItems(context, items) { + const xformedItems = items.map((item) => ({ + ...item, + productConfiguration: { + productId: item.productId, + productVariantId: item.variantId + }, + subtotal: { + amount: item.subtotal, + currencyCode: item.price.currencyCode + } + })); + + for (const mutateItems of context.getFunctionsOfType("xformOrderItems")) { + await mutateItems(context, xformedItems); // eslint-disable-line no-await-in-loop + } + + return xformedItems; +} From 8d222844b065dff34231ebeeaf8b94a0452bc028 Mon Sep 17 00:00:00 2001 From: sardar umer Date: Tue, 20 Jun 2023 12:45:41 +0500 Subject: [PATCH 2/7] version changed --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index fcf6dff..60561e0 100755 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "@codistan-isb/api-plugin-dashboard-stats", "description": "Boilerplate Example plugin for the Reaction API", "label": "Example Plugin", - "version": "1.0.0-development", + "version": "1.0.1-development", "main": "index.js", "type": "module", "engines": { From 3e27e1106f39630d73fb462e3fd1c3669d207546 Mon Sep 17 00:00:00 2001 From: sardar umer Date: Wed, 19 Jul 2023 15:54:12 +0500 Subject: [PATCH 3/7] seller dashboard stats --- package.json | 2 +- src/index.js | 5 + .../Query/dashBoardShoppingActivity.js | 19 ---- src/resolvers/Query/index.js | 6 +- src/resolvers/Query/paymentsgraph.js | 98 +++++++++++++++++++ src/resolvers/Query/productImpression.js | 74 ++++++++++++++ src/schemas/schema.graphql | 21 ++-- 7 files changed, 196 insertions(+), 29 deletions(-) delete mode 100755 src/resolvers/Query/dashBoardShoppingActivity.js create mode 100644 src/resolvers/Query/paymentsgraph.js create mode 100755 src/resolvers/Query/productImpression.js diff --git a/package.json b/package.json index 60561e0..24d0506 100755 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "@codistan-isb/api-plugin-dashboard-stats", + "name": "@umerjaved1/api-plugin-dashboard-stats", "description": "Boilerplate Example plugin for the Reaction API", "label": "Example Plugin", "version": "1.0.1-development", diff --git a/src/index.js b/src/index.js index 78352e9..6333b86 100755 --- a/src/index.js +++ b/src/index.js @@ -7,6 +7,11 @@ export default async function register(app) { label: "Dashboard-stats", name: "Dashboard-stats", version: pkg.version, + collections: { + ProductImpression: { + name: "ProductImpression", + }, + }, i18n, graphQL: { resolvers, diff --git a/src/resolvers/Query/dashBoardShoppingActivity.js b/src/resolvers/Query/dashBoardShoppingActivity.js deleted file mode 100755 index 10a0068..0000000 --- a/src/resolvers/Query/dashBoardShoppingActivity.js +++ /dev/null @@ -1,19 +0,0 @@ -import { decodeOrderOpaqueId, decodeShopOpaqueId } from "../../xforms/id.js"; -import ReactionError from "@reactioncommerce/reaction-error"; -export default async function dashBoardShoppingActivity(parent, args, context) { - if (!context.user) { - throw new ReactionError("access-denied", "Access Denied"); - } - const { shopId } = args; - console.log(decodeShopOpaqueId(shopId), "new"); - return { - shopId: shopId, - _id: "123", - productsSold: 10, - productsActive: 5, - productInCart: 3, - productInCheckout: 2, - productPaymentDone: 1, - }; -} - diff --git a/src/resolvers/Query/index.js b/src/resolvers/Query/index.js index b318b9d..6e4147b 100755 --- a/src/resolvers/Query/index.js +++ b/src/resolvers/Query/index.js @@ -1,10 +1,12 @@ -import dashBoardShoppingActivity from "./dashBoardShoppingActivity.js"; +import productImpression from "./productImpression.js"; import productDataGraph from "./productDataGraph.js"; import earningDataGraph from "./earningDataGraph.js"; import productsUploaded from "./productsUploaded.js"; +import paymentsgraph from "./paymentsgraph.js"; export default { - dashBoardShoppingActivity, +productImpression, productDataGraph, productsUploaded, + paymentsgraph, earningDataGraph, }; diff --git a/src/resolvers/Query/paymentsgraph.js b/src/resolvers/Query/paymentsgraph.js new file mode 100644 index 0000000..92bc745 --- /dev/null +++ b/src/resolvers/Query/paymentsgraph.js @@ -0,0 +1,98 @@ +import { decodeOrderOpaqueId, decodeShopOpaqueId } from "../../xforms/id.js"; +import ReactionError from "@reactioncommerce/reaction-error"; +import _ from "lodash"; +export default async function paymentsgraph(parent, args, context) { + if (!context.user) { + throw new ReactionError("access-denied", "Access Denied"); + } + + const { collections } = context; + const { Payments } = collections; +const currentDate = new Date(); +const startOfMonth = new Date( + currentDate.getFullYear(), + currentDate.getMonth(), + 1 +).toUTCString(); +const endOfMonth = new Date( + currentDate.getFullYear(), + currentDate.getMonth() + 2, + 0 +).toUTCString(); +console.log(startOfMonth, endOfMonth); + +// Fetch payments from the database (e.g., MongoDB using Mongoose) +const payments = await Payments.find({ + updatedAt: { + $gte: startOfMonth, + $lte: endOfMonth, + }, +}).toArray(); + console.log(startOfMonth,"new",endOfMonth) + + try { + // Fetch payments from the database (e.g., MongoDB using Mongoose) + const payments = await Payments.find({ + updatedAt: { + $gte: startOfMonth, + + }, + }).toArray(); + console.log(payments,"new"); + + // Group and aggregate payments by week + const groupedPayments = _.groupBy(payments, (payment) => { + const paymentDate = new Date(payment.updatedAt); + const weekNumber = getWeekNumber(paymentDate); + return `Week ${weekNumber}`; + }); + + // Prepare the array of Payment objects + const result = Object.entries(groupedPayments).map(([week, payments]) => { + const totalAmount = payments.reduce( + (sum, payment) => sum + payment.amount, + 0 + ); + return { + id: week, + amount: totalAmount, + date: new Date(payments[0].updatedAt), // Assuming all payments in the same week have the same date + }; + }); +console.log(result) + return result; + } catch (error) { + // Handle the error + console.error(error); + throw new Error("Failed to fetch payments."); + } +} + +// Helper function to format the date as "Thu, 13 Jul 2023 08:23:33 GMT" +function formatDate(date) { + const options = { + weekday: "short", + day: "2-digit", + month: "short", + year: "numeric", + hour: "2-digit", + minute: "2-digit", + second: "2-digit", + timeZone: "GMT", + hour12: false, // Exclude AM/PM indicator + }; + const formattedDate = date.toLocaleString("en-US", options); + return formattedDate + " GMT"; +} +function getWeekNumber(date) { + const firstDayOfYear = new Date(date.getFullYear(), 0, 1); + const daysPassed = Math.floor( + (date - firstDayOfYear) / (24 * 60 * 60 * 1000) + ); + const weekNumber = Math.ceil((daysPassed + firstDayOfYear.getDay() + 1) / 7); + return weekNumber; +} + + + + diff --git a/src/resolvers/Query/productImpression.js b/src/resolvers/Query/productImpression.js new file mode 100755 index 0000000..88f62c4 --- /dev/null +++ b/src/resolvers/Query/productImpression.js @@ -0,0 +1,74 @@ +import { decodeOrderOpaqueId, decodeShopOpaqueId } from "../../xforms/id.js"; +import ReactionError from "@reactioncommerce/reaction-error"; +export default async function productImpression(parent, args, context) { + if (!context.user) { + throw new ReactionError("access-denied", "Access Denied"); + } + const { shopId } = args; + const { collections } = context; + const { ProductImpression } = collections; + + // const impressions = await ProductImpression.aggregate([ + // { + // $group: { + // Date: "$Date", + // count: { $sum: 1 }, + // }, + // }, + // { + // $sort: { + // _id: 1, + // }, + // }, + // ]).toArray(); + const impressions = await ProductImpression.aggregate([ + // { + // $match: { + // slugOrId: "sana-safinaz-embroidered-shirt", + // }, + // }, + { + $group: { + _id: { + month: { + $dateToString: { format: "%m", date: { $toDate: "$Date" } }, + }, + year: { $dateToString: { format: "%Y", date: { $toDate: "$Date" } } }, + }, + count: { $sum: 1 }, + }, + }, + { + $project: { + month: { $toInt: "$_id.month" }, + year: { $toInt: "$_id.year" }, + count: 1, + _id: 0, + }, + }, + ]).toArray(); + + console.log(impressions); + return impressions; +} +// const order = ProductImpression.find([ +// { +// $unwind: "$items", // Unwind the "items" array +// }, +// { +// $group: { +// _id: "$items.productId", // Group by the product ID +// count: { $sum: "$items.quantity" }, // Calculate the count for each product +// }, +// }, +// ]); +// return { +// shopId: shopId, +// _id: "123", +// productsSold: order, +// productsActive: 5, +// productInCart: 3, +// productInCheckout: 2, +// productPaymentDone: 1, +// }; +// } diff --git a/src/schemas/schema.graphql b/src/schemas/schema.graphql index d3a4ef3..8d8c115 100755 --- a/src/schemas/schema.graphql +++ b/src/schemas/schema.graphql @@ -23,19 +23,26 @@ type EarningData { earning: Int! } type ProductUpload { - date: String! - count: Int! + month:String, + year:String, + count: Int } type ProductUploads { currentMonthData: [ProductUpload!]! previousMonthData: [ProductUpload!]! } - + type Payment { + id: ID + amount: Money + date: DateTime + week:String + } extend type Query { "Get an order by its ID" - dashBoardShoppingActivity(shopId: ID!): DashBoardStats - productDataGraph: [ProductData!]! - earningDataGraph: [EarningData!]! - productsUploaded: ProductUploads! + productImpression(shopId: ID!): [ProductUpload] + productDataGraph: [ProductData!]! + earningDataGraph: [EarningData!]! + paymentsgraph: [Payment!]! + productsUploaded: ProductUploads! } From 15d53470271d7652b67167115edd87c670402ede Mon Sep 17 00:00:00 2001 From: sardar umer Date: Thu, 20 Jul 2023 16:44:13 +0500 Subject: [PATCH 4/7] productuploaded --- src/resolvers/Query/paymentsgraph.js | 75 +++++++++++++------------ src/resolvers/Query/productsUploaded.js | 62 +++++++++++++------- src/schemas/schema.graphql | 24 +++++--- 3 files changed, 98 insertions(+), 63 deletions(-) diff --git a/src/resolvers/Query/paymentsgraph.js b/src/resolvers/Query/paymentsgraph.js index 92bc745..1dc4240 100644 --- a/src/resolvers/Query/paymentsgraph.js +++ b/src/resolvers/Query/paymentsgraph.js @@ -1,6 +1,6 @@ -import { decodeOrderOpaqueId, decodeShopOpaqueId } from "../../xforms/id.js"; import ReactionError from "@reactioncommerce/reaction-error"; import _ from "lodash"; + export default async function paymentsgraph(parent, args, context) { if (!context.user) { throw new ReactionError("access-denied", "Access Denied"); @@ -8,58 +8,62 @@ export default async function paymentsgraph(parent, args, context) { const { collections } = context; const { Payments } = collections; -const currentDate = new Date(); -const startOfMonth = new Date( - currentDate.getFullYear(), - currentDate.getMonth(), - 1 -).toUTCString(); -const endOfMonth = new Date( - currentDate.getFullYear(), - currentDate.getMonth() + 2, - 0 -).toUTCString(); -console.log(startOfMonth, endOfMonth); - -// Fetch payments from the database (e.g., MongoDB using Mongoose) -const payments = await Payments.find({ - updatedAt: { - $gte: startOfMonth, - $lte: endOfMonth, - }, -}).toArray(); - console.log(startOfMonth,"new",endOfMonth) - + const currentDate = new Date(); + const startOfMonth = new Date( + currentDate.getFullYear(), + currentDate.getMonth(), + 1 + ).toUTCString(); + const endOfMonth = new Date( + currentDate.getFullYear(), + currentDate.getMonth() + 1, + ).toUTCString(); +console.log(startOfMonth,endOfMonth) try { // Fetch payments from the database (e.g., MongoDB using Mongoose) - const payments = await Payments.find({ + const payment = await Payments.find({ updatedAt: { $gte: startOfMonth, - }, }).toArray(); - console.log(payments,"new"); + console.log(payment, "new"); // Group and aggregate payments by week - const groupedPayments = _.groupBy(payments, (payment) => { + const groupedPayments = _.groupBy(payment, (payment) => { const paymentDate = new Date(payment.updatedAt); const weekNumber = getWeekNumber(paymentDate); return `Week ${weekNumber}`; }); + // Generate an array of all week numbers within the current month + const numberOfWeeks = + getWeekNumber(new Date(endOfMonth)) - + getWeekNumber(new Date(startOfMonth)) + + 1; + const allWeekNumbers = Array.from( + { length: numberOfWeeks }, + (_, index) => getWeekNumber(new Date(startOfMonth)) + index + ); + // Prepare the array of Payment objects - const result = Object.entries(groupedPayments).map(([week, payments]) => { - const totalAmount = payments.reduce( + const result = allWeekNumbers.map((weekNumber) => { + const weekLabel = `Week ${weekNumber}`; + const paymentsInWeek = groupedPayments[weekLabel] || []; + const totalAmount = paymentsInWeek.reduce( (sum, payment) => sum + payment.amount, 0 ); return { - id: week, - amount: totalAmount, - date: new Date(payments[0].updatedAt), // Assuming all payments in the same week have the same date + id: weekLabel, + money: totalAmount, + date: + paymentsInWeek.length > 0 + ? new Date(paymentsInWeek[0].updatedAt) + : null, }; }); -console.log(result) + + console.log(result); return result; } catch (error) { // Handle the error @@ -84,6 +88,7 @@ function formatDate(date) { const formattedDate = date.toLocaleString("en-US", options); return formattedDate + " GMT"; } + function getWeekNumber(date) { const firstDayOfYear = new Date(date.getFullYear(), 0, 1); const daysPassed = Math.floor( @@ -92,7 +97,3 @@ function getWeekNumber(date) { const weekNumber = Math.ceil((daysPassed + firstDayOfYear.getDay() + 1) / 7); return weekNumber; } - - - - diff --git a/src/resolvers/Query/productsUploaded.js b/src/resolvers/Query/productsUploaded.js index 264f71a..4d149e6 100644 --- a/src/resolvers/Query/productsUploaded.js +++ b/src/resolvers/Query/productsUploaded.js @@ -4,26 +4,50 @@ export default async function productsUploaded(parent, args, context) { if (!context.user) { throw new ReactionError("access-denied", "Access Denied"); } - const currentMonth = new Date().getMonth() + 1; - const previousMonth = currentMonth - 1; - // Dummy data for the current month and previous month - const productUploadData = [ - { date: "2023-06-01", count: 10 }, - { date: "2023-06-05", count: 15 }, - { date: "2023-06-10", count: 8 }, - { date: "2023-05-01", count: 12 }, - { date: "2023-05-05", count: 5 }, - { date: "2023-12-10", count: 18 }, - ]; + const { collections } = context; + const { Products } = collections; + const uploaded = await Products.aggregate([ + { + $match: { + "uploadedBy.userId": args.sellerId, + }, + }, + { + $group: { + _id: { + isVisible: "$isVisible", + month: { + $dateToString: { format: "%m", date: { $toDate: "$createdAt" } }, + }, + year: { + $dateToString: { format: "%Y", date: { $toDate: "$createdAt" } }, + }, + }, + count: { $sum: 1 }, + }, + }, + { + $project: { + isVisible: "$_id.isVisible", + month: { $toInt: "$_id.month" }, + year: { $toInt: "$_id.year" }, + count: 1, + _id: 0, + }, + }, + ]).toArray(); - const currentMonthData = productUploadData.filter( - (data) => new Date(data.date).getMonth() + 1 === currentMonth - ); + const uploadedTrue = uploaded.filter((imp) => imp.isVisible === true); + const uploadedFalse = uploaded.filter((imp) => imp.isVisible === false); +console.log(uploadedTrue, uploadedTrue); - const previousMonthData = productUploadData.filter( - (data) => new Date(data.date).getMonth() + 1 === previousMonth - ); - - return { currentMonthData, previousMonthData }; + return { + productsUploaded: { + isVisibleTrue: uploadedTrue.length > 0 ? uploadedTrue[0] : "new", + isVisibleFalse: uploadedFalse.length > 0 ? uploadedFalse[0] : "new", + }, + }; } + + diff --git a/src/schemas/schema.graphql b/src/schemas/schema.graphql index 8d8c115..a1e2b7a 100755 --- a/src/schemas/schema.graphql +++ b/src/schemas/schema.graphql @@ -22,27 +22,37 @@ type EarningData { # product: Product! earning: Int! } -type ProductUpload { - month:String, - year:String, - count: Int -} + + type ProductUploads { currentMonthData: [ProductUpload!]! previousMonthData: [ProductUpload!]! } +type ProductsUploadedResponse { + isVisibleTrue: [ProductUpload] + isVisibleFalse: [ProductUpload] +} +type ProductUpload { + count: Int + month: String + year: String + isVisible: Boolean +} type Payment { id: ID - amount: Money + money:String date: DateTime week:String } + # // productImpression(shopId: ID!): [ProductUpload] + # productsUploaded(shopId: ID! sellerId:ID!): ProductsUploadedResponse paymentsgraph: [Payment!]! these query working need to change accordingly extend type Query { "Get an order by its ID" productImpression(shopId: ID!): [ProductUpload] + productsUploaded(shopId: ID! sellerId:ID!): ProductsUploadedResponse productDataGraph: [ProductData!]! earningDataGraph: [EarningData!]! paymentsgraph: [Payment!]! - productsUploaded: ProductUploads! + # productsUploaded: ProductUploads! } From 304bbde524425698a35d460b7e34c001b688a644 Mon Sep 17 00:00:00 2001 From: sardar umer Date: Thu, 20 Jul 2023 17:04:46 +0500 Subject: [PATCH 5/7] product uploaded --- src/schemas/schema.graphql | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/src/schemas/schema.graphql b/src/schemas/schema.graphql index a1e2b7a..cdd40d8 100755 --- a/src/schemas/schema.graphql +++ b/src/schemas/schema.graphql @@ -28,16 +28,15 @@ type ProductUploads { currentMonthData: [ProductUpload!]! previousMonthData: [ProductUpload!]! } - -type ProductsUploadedResponse { - isVisibleTrue: [ProductUpload] - isVisibleFalse: [ProductUpload] -} type ProductUpload { count: Int month: String year: String isVisible: Boolean +} +type ProductsUploadedResponse { + isVisibleTrue: [ProductUpload] + isVisibleFalse: [ProductUpload] } type Payment { id: ID @@ -46,7 +45,7 @@ type ProductUpload { week:String } # // productImpression(shopId: ID!): [ProductUpload] - # productsUploaded(shopId: ID! sellerId:ID!): ProductsUploadedResponse paymentsgraph: [Payment!]! these query working need to change accordingly + # productsUploaded(shopId: ID! sellerId:ID!): ProductsUploadedResponse paymentsgraph: [Payment!]! these query working need to change accordingly extend type Query { "Get an order by its ID" productImpression(shopId: ID!): [ProductUpload] From 35830f414483c0fabf238c674421f6c85daca50e Mon Sep 17 00:00:00 2001 From: sardar umer Date: Thu, 20 Jul 2023 17:05:01 +0500 Subject: [PATCH 6/7] product uploaded --- src/resolvers/Query/productsUploaded.js | 19 +++++++++---------- src/schemas/schema.graphql | 21 ++++++++++----------- 2 files changed, 19 insertions(+), 21 deletions(-) diff --git a/src/resolvers/Query/productsUploaded.js b/src/resolvers/Query/productsUploaded.js index 4d149e6..a10c518 100644 --- a/src/resolvers/Query/productsUploaded.js +++ b/src/resolvers/Query/productsUploaded.js @@ -38,16 +38,15 @@ export default async function productsUploaded(parent, args, context) { }, ]).toArray(); - const uploadedTrue = uploaded.filter((imp) => imp.isVisible === true); - const uploadedFalse = uploaded.filter((imp) => imp.isVisible === false); -console.log(uploadedTrue, uploadedTrue); + const uploadedTrue = uploaded.filter((imp) => imp.isVisible === true); + const uploadedFalse = uploaded.filter((imp) => imp.isVisible === false); + console.log(uploadedTrue, uploadedTrue); - return { - productsUploaded: { - isVisibleTrue: uploadedTrue.length > 0 ? uploadedTrue[0] : "new", - isVisibleFalse: uploadedFalse.length > 0 ? uploadedFalse[0] : "new", - }, - }; -} + const returnObj = { + isVisibleTrue: uploadedTrue, + isVisibleFalse: uploadedFalse, + }; + return returnObj; +} diff --git a/src/schemas/schema.graphql b/src/schemas/schema.graphql index cdd40d8..037dbcf 100755 --- a/src/schemas/schema.graphql +++ b/src/schemas/schema.graphql @@ -23,7 +23,6 @@ type EarningData { earning: Int! } - type ProductUploads { currentMonthData: [ProductUpload!]! previousMonthData: [ProductUpload!]! @@ -38,20 +37,20 @@ type ProductsUploadedResponse { isVisibleTrue: [ProductUpload] isVisibleFalse: [ProductUpload] } - type Payment { - id: ID - money:String - date: DateTime - week:String - } - # // productImpression(shopId: ID!): [ProductUpload] - # productsUploaded(shopId: ID! sellerId:ID!): ProductsUploadedResponse paymentsgraph: [Payment!]! these query working need to change accordingly +type Payment { + id: ID + money: String + date: DateTime + week: String +} +# // productImpression(shopId: ID!): [ProductUpload] +# productsUploaded(shopId: ID! sellerId:ID!): ProductsUploadedResponse paymentsgraph: [Payment!]! these query working need to change accordingly extend type Query { "Get an order by its ID" productImpression(shopId: ID!): [ProductUpload] - productsUploaded(shopId: ID! sellerId:ID!): ProductsUploadedResponse + productsUploaded(shopId: ID!, sellerId: ID!): ProductsUploadedResponse productDataGraph: [ProductData!]! earningDataGraph: [EarningData!]! - paymentsgraph: [Payment!]! + paymentsgraph: [Payment!]! # productsUploaded: ProductUploads! } From 452311207b0929c42ba8854da67876f721a0cca3 Mon Sep 17 00:00:00 2001 From: sardar umer Date: Thu, 20 Jul 2023 17:10:13 +0500 Subject: [PATCH 7/7] args --- src/resolvers/Query/productImpression.js | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/src/resolvers/Query/productImpression.js b/src/resolvers/Query/productImpression.js index 88f62c4..748dfbe 100755 --- a/src/resolvers/Query/productImpression.js +++ b/src/resolvers/Query/productImpression.js @@ -8,19 +8,7 @@ export default async function productImpression(parent, args, context) { const { collections } = context; const { ProductImpression } = collections; - // const impressions = await ProductImpression.aggregate([ - // { - // $group: { - // Date: "$Date", - // count: { $sum: 1 }, - // }, - // }, - // { - // $sort: { - // _id: 1, - // }, - // }, - // ]).toArray(); + // is ma match krwaana h args.seelerid sy aur seller id args m b bejni h aur schema ma be define krni h const impressions = await ProductImpression.aggregate([ // { // $match: {