spree_emails 6.0.0.beta4 → 6.0.0.rc2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +30 -74
  3. data/Rakefile +23 -0
  4. data/app/mailers/spree/company_mailer.rb +4 -7
  5. data/app/mailers/spree/customer_mailer.rb +6 -8
  6. data/app/mailers/spree/digital_asset_mailer.rb +18 -11
  7. data/app/mailers/spree/fulfillment_mailer.rb +11 -7
  8. data/app/mailers/spree/newsletter_mailer.rb +1 -2
  9. data/app/mailers/spree/order_group_mailer.rb +16 -20
  10. data/app/mailers/spree/order_mailer.rb +15 -20
  11. data/app/mailers/spree/return_mailer.rb +10 -5
  12. data/app/mailers/spree/seller_mailer.rb +8 -13
  13. data/app/models/spree/emails/amount_line.rb +32 -0
  14. data/app/serializers/spree/emails/address_serializer.rb +6 -0
  15. data/app/serializers/spree/emails/amount_line_serializer.rb +13 -0
  16. data/app/serializers/spree/emails/applied_promotion_serializer.rb +7 -0
  17. data/app/serializers/spree/emails/customer_group_serializer.rb +6 -0
  18. data/app/serializers/spree/emails/customer_serializer.rb +19 -0
  19. data/app/serializers/spree/emails/delivery_method_serializer.rb +6 -0
  20. data/app/serializers/spree/emails/delivery_rate_serializer.rb +12 -0
  21. data/app/serializers/spree/emails/delivery_serializer.rb +6 -0
  22. data/app/serializers/spree/emails/fee_serializer.rb +7 -0
  23. data/app/serializers/spree/emails/freight_summary_serializer.rb +6 -0
  24. data/app/serializers/spree/emails/fulfillment_group_serializer.rb +25 -0
  25. data/app/serializers/spree/emails/fulfillment_serializer.rb +21 -0
  26. data/app/serializers/spree/emails/gift_card_serializer.rb +7 -0
  27. data/app/serializers/spree/emails/line_item_serializer.rb +24 -0
  28. data/app/serializers/spree/emails/market_serializer.rb +6 -0
  29. data/app/serializers/spree/emails/newsletter_subscriber_serializer.rb +6 -0
  30. data/app/serializers/spree/emails/option_value_serializer.rb +6 -0
  31. data/app/serializers/spree/emails/order_group_serializer.rb +50 -0
  32. data/app/serializers/spree/emails/order_serializer.rb +27 -0
  33. data/app/serializers/spree/emails/parcel_item_serializer.rb +36 -0
  34. data/app/serializers/spree/emails/payment_method_serializer.rb +6 -0
  35. data/app/serializers/spree/emails/payment_serializer.rb +11 -0
  36. data/app/serializers/spree/emails/purchase_summary_attributes.rb +41 -0
  37. data/app/serializers/spree/emails/purchased_item_attributes.rb +28 -0
  38. data/app/serializers/spree/emails/return_serializer.rb +19 -0
  39. data/app/serializers/spree/emails/stock_location_serializer.rb +6 -0
  40. data/app/services/spree/emails/samples/base.rb +79 -0
  41. data/app/services/spree/emails/samples/company_invitation.rb +19 -0
  42. data/app/services/spree/emails/samples/data_export.rb +11 -0
  43. data/app/services/spree/emails/samples/downloads.rb +28 -0
  44. data/app/services/spree/emails/samples/fulfillment.rb +33 -0
  45. data/app/services/spree/emails/samples/newsletter_confirmation.rb +11 -0
  46. data/app/services/spree/emails/samples/order.rb +22 -0
  47. data/app/services/spree/emails/samples/order_group.rb +22 -0
  48. data/app/services/spree/emails/samples/password_reset.rb +15 -0
  49. data/app/services/spree/emails/samples/payment_link.rb +17 -0
  50. data/app/services/spree/emails/samples/return.rb +29 -0
  51. data/app/subscribers/spree/order_email_subscriber.rb +2 -2
  52. data/app/subscribers/spree/order_group_email_subscriber.rb +1 -1
  53. data/app/subscribers/spree/return_email_subscriber.rb +2 -0
  54. data/app/views/spree/company_mailer/invitation_email.liquid +13 -0
  55. data/app/views/spree/customer_mailer/data_export_email.liquid +18 -0
  56. data/app/views/spree/customer_mailer/password_reset_email.liquid +14 -0
  57. data/app/views/spree/digital_asset_mailer/files_ready_email.liquid +23 -0
  58. data/app/views/spree/fulfillment_mailer/fulfilled_email.liquid +21 -0
  59. data/app/views/spree/newsletter_mailer/email_confirmation.liquid +13 -0
  60. data/app/views/spree/order_group_mailer/confirm_email.liquid +35 -0
  61. data/app/views/spree/order_group_mailer/store_owner_notification_email.liquid +11 -0
  62. data/app/views/spree/order_mailer/cancel_email.liquid +12 -0
  63. data/app/views/spree/order_mailer/confirm_email.liquid +12 -0
  64. data/app/views/spree/order_mailer/payment_link_email.liquid +12 -0
  65. data/app/views/spree/order_mailer/store_owner_notification_email.liquid +11 -0
  66. data/app/views/spree/return_mailer/refunded_email.liquid +19 -0
  67. data/app/views/spree/seller_mailer/approved_email.liquid +13 -0
  68. data/app/views/spree/seller_mailer/rejected_email.liquid +11 -0
  69. data/app/views/spree/seller_mailer/suspended_email.liquid +11 -0
  70. data/app/views/spree/shared/_fulfillment_group.liquid +25 -0
  71. data/app/views/spree/shared/_line_item.liquid +29 -0
  72. data/app/views/spree/shared/_order_summary.liquid +17 -0
  73. data/app/views/spree/shared/_purchase_totals.liquid +35 -0
  74. data/config/initializers/typelizer.rb +61 -0
  75. data/config/locales/ar.yml +40 -0
  76. data/config/locales/be.yml +37 -0
  77. data/config/locales/bg.yml +31 -0
  78. data/config/locales/ca.yml +35 -0
  79. data/config/locales/cs.yml +38 -0
  80. data/config/locales/da.yml +37 -0
  81. data/config/locales/de-CH.yml +11 -0
  82. data/config/locales/de.yml +38 -0
  83. data/config/locales/el.yml +37 -0
  84. data/config/locales/en.yml +0 -25
  85. data/config/locales/es-CL.yml +21 -0
  86. data/config/locales/es-EC.yml +22 -0
  87. data/config/locales/es-MX.yml +26 -0
  88. data/config/locales/es.yml +40 -0
  89. data/config/locales/et.yml +31 -0
  90. data/config/locales/fa.yml +37 -0
  91. data/config/locales/fi.yml +37 -0
  92. data/config/locales/fr.yml +38 -0
  93. data/config/locales/hu.yml +37 -0
  94. data/config/locales/id.yml +37 -0
  95. data/config/locales/it.yml +38 -0
  96. data/config/locales/ja.yml +36 -0
  97. data/config/locales/km.yml +37 -0
  98. data/config/locales/ko.yml +36 -0
  99. data/config/locales/ku.yml +37 -0
  100. data/config/locales/lt.yml +37 -0
  101. data/config/locales/lv.yml +39 -0
  102. data/config/locales/nb.yml +37 -0
  103. data/config/locales/ne.yml +37 -0
  104. data/config/locales/nl.yml +38 -0
  105. data/config/locales/pl.yml +39 -0
  106. data/config/locales/pt-BR.yml +34 -0
  107. data/config/locales/pt.yml +34 -0
  108. data/config/locales/ro.yml +37 -0
  109. data/config/locales/ru.yml +38 -0
  110. data/config/locales/sk.yml +31 -0
  111. data/config/locales/sl-SI.yml +31 -0
  112. data/config/locales/sv.yml +37 -0
  113. data/config/locales/th.yml +37 -0
  114. data/config/locales/tr.yml +38 -0
  115. data/config/locales/uk.yml +42 -0
  116. data/config/locales/vi.yml +35 -0
  117. data/config/locales/zh-CN.yml +38 -0
  118. data/config/locales/zh-TW.yml +40 -0
  119. data/lib/spree/emails/engine.rb +27 -3
  120. data/lib/spree/emails/previews/company_preview.rb +10 -0
  121. data/lib/spree/emails/previews/customer_preview.rb +4 -0
  122. data/lib/spree/emails/previews/digital_asset_preview.rb +20 -0
  123. metadata +110 -55
  124. data/app/helpers/spree/base_helper.rb +0 -45
  125. data/app/helpers/spree/digital_asset_helper.rb +0 -16
  126. data/app/helpers/spree/fulfillment_helper.rb +0 -12
  127. data/app/helpers/spree/mail_helper.rb +0 -10
  128. data/app/views/spree/company_mailer/invitation_email.html.erb +0 -8
  129. data/app/views/spree/company_mailer/invitation_email.text.erb +0 -11
  130. data/app/views/spree/customer_mailer/data_export_email.html.erb +0 -12
  131. data/app/views/spree/customer_mailer/data_export_email.text.erb +0 -9
  132. data/app/views/spree/customer_mailer/password_reset_email.html.erb +0 -10
  133. data/app/views/spree/customer_mailer/password_reset_email.text.erb +0 -13
  134. data/app/views/spree/digital_asset_mailer/files_ready_email.html.erb +0 -20
  135. data/app/views/spree/digital_asset_mailer/files_ready_email.text.erb +0 -10
  136. data/app/views/spree/fulfillment_mailer/fulfilled_email.html.erb +0 -22
  137. data/app/views/spree/fulfillment_mailer/fulfilled_email.text.erb +0 -18
  138. data/app/views/spree/newsletter_mailer/email_confirmation.html.erb +0 -12
  139. data/app/views/spree/newsletter_mailer/email_confirmation.text.erb +0 -9
  140. data/app/views/spree/order_group_mailer/confirm_email.html.erb +0 -38
  141. data/app/views/spree/order_group_mailer/confirm_email.text.erb +0 -26
  142. data/app/views/spree/order_group_mailer/store_owner_notification_email.html.erb +0 -14
  143. data/app/views/spree/order_group_mailer/store_owner_notification_email.text.erb +0 -11
  144. data/app/views/spree/order_mailer/cancel_email.html.erb +0 -12
  145. data/app/views/spree/order_mailer/cancel_email.text.erb +0 -13
  146. data/app/views/spree/order_mailer/confirm_email.html.erb +0 -12
  147. data/app/views/spree/order_mailer/confirm_email.text.erb +0 -13
  148. data/app/views/spree/order_mailer/payment_link_email.html.erb +0 -11
  149. data/app/views/spree/order_mailer/store_owner_notification_email.html.erb +0 -11
  150. data/app/views/spree/order_mailer/store_owner_notification_email.text.erb +0 -13
  151. data/app/views/spree/return_mailer/refunded_email.html.erb +0 -22
  152. data/app/views/spree/return_mailer/refunded_email.text.erb +0 -20
  153. data/app/views/spree/seller_mailer/approved_email.html.erb +0 -8
  154. data/app/views/spree/seller_mailer/approved_email.text.erb +0 -8
  155. data/app/views/spree/seller_mailer/rejected_email.html.erb +0 -6
  156. data/app/views/spree/seller_mailer/rejected_email.text.erb +0 -7
  157. data/app/views/spree/seller_mailer/suspended_email.html.erb +0 -6
  158. data/app/views/spree/seller_mailer/suspended_email.text.erb +0 -7
  159. data/app/views/spree/shared/_fulfillment_group_section.html.erb +0 -37
  160. data/app/views/spree/shared/_fulfillment_group_section.text.erb +0 -12
  161. data/app/views/spree/shared/_item_thumbnail_link.html.erb +0 -18
  162. data/app/views/spree/shared/_order_summary_section.html.erb +0 -20
  163. data/app/views/spree/shared/_po_number.text.erb +0 -6
  164. data/app/views/spree/shared/_purchase_totals.html.erb +0 -62
  165. data/app/views/spree/shared/_purchase_totals.text.erb +0 -24
  166. data/app/views/spree/shared/_purchased_items_table.html.erb +0 -41
  167. data/app/views/spree/shared/_purchased_items_table.text.erb +0 -28
  168. data/app/views/spree/shared/purchased_items_table/_line_item.html.erb +0 -24
  169. data/app/views/spree/shared/purchased_items_table/_subtotal.html.erb +0 -3
  170. data/app/views/spree/shared/purchased_items_table/_summary_row.html.erb +0 -14
  171. data/app/views/spree/shared/purchased_items_table/_total.html.erb +0 -4
  172. data/config/i18n-tasks.yml +0 -22
@@ -0,0 +1,6 @@
1
+ module Spree
2
+ module Emails
3
+ class FreightSummarySerializer < Spree::Api::V3::FreightSummarySerializer
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,25 @@
1
+ module Spree
2
+ module Emails
3
+ # One real parcel of a purchase: what it carries, who sold it and what it
4
+ # cost to deliver.
5
+ class FulfillmentGroupSerializer
6
+ include Alba::Resource
7
+
8
+ attributes :name
9
+
10
+ # A freight parcel awaiting its forwarder's quote has no cost yet.
11
+ attribute :unpriced, &:unpriced?
12
+
13
+ attribute :cost do |group|
14
+ Spree::Money::Rounding.format(group.cost, group.currency)
15
+ end
16
+
17
+ attribute :seller_names do |group|
18
+ group.sellers.map(&:name)
19
+ end
20
+
21
+ many :manifest, key: :items, source: proc { manifest.select(&:line_item) },
22
+ resource: Spree::Emails::ParcelItemSerializer
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,21 @@
1
+ module Spree
2
+ module Emails
3
+ class FulfillmentSerializer < Spree::Api::V3::FulfillmentSerializer
4
+ without_display_money
5
+
6
+ typelize delivery_method_name: [:string, nullable: true]
7
+
8
+ attribute :delivery_method_name do |fulfillment|
9
+ fulfillment.delivery_method&.name
10
+ end
11
+
12
+ many :deliveries, resource: Spree::Emails::DeliverySerializer
13
+ one :delivery_method, resource: Spree::Emails::DeliveryMethodSerializer
14
+ one :stock_location, resource: Spree::Emails::StockLocationSerializer
15
+ many :delivery_rates, resource: Spree::Emails::DeliveryRateSerializer
16
+
17
+ many :manifest, key: :manifest_items, source: proc { manifest.select(&:line_item) },
18
+ resource: Spree::Emails::ParcelItemSerializer
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,7 @@
1
+ module Spree
2
+ module Emails
3
+ class GiftCardSerializer < Spree::Api::V3::GiftCardSerializer
4
+ without_display_money
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,24 @@
1
+ module Spree
2
+ module Emails
3
+ class LineItemSerializer < Spree::Api::V3::LineItemSerializer
4
+ include Spree::Emails::PurchasedItemAttributes
5
+
6
+ without_display_money
7
+
8
+ many :option_values, resource: Spree::Emails::OptionValueSerializer
9
+
10
+ typelize sku: [:string, nullable: true], amount: :string,
11
+ url: [:string, nullable: true], image_url: [:string, nullable: true]
12
+
13
+ attributes :sku
14
+
15
+ # Price times quantity, before discounts — what the line cost.
16
+ attribute :amount do |line_item|
17
+ Spree::Money::Rounding.format(line_item.amount, line_item.currency)
18
+ end
19
+
20
+ # The full-size image; emails show the thumbnail in `image_url`.
21
+ attributes :thumbnail_url, if: proc { false }
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,6 @@
1
+ module Spree
2
+ module Emails
3
+ class MarketSerializer < Spree::Api::V3::MarketSerializer
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module Spree
2
+ module Emails
3
+ class NewsletterSubscriberSerializer < Spree::Api::V3::NewsletterSubscriberSerializer
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ module Spree
2
+ module Emails
3
+ class OptionValueSerializer < Spree::Api::V3::OptionValueSerializer
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,50 @@
1
+ module Spree
2
+ module Emails
3
+ # A purchase that divided into several orders, as its confirmation reads
4
+ # it: every item, the parcels it ships in, and one set of totals.
5
+ class OrderGroupSerializer < Spree::Api::V3::OrderGroupSerializer
6
+ include Spree::Emails::PurchaseSummaryAttributes
7
+
8
+ without_display_money
9
+
10
+ typelize po_number: [:string, nullable: true], order_count: :number, delivery_total: :string,
11
+ additional_tax_total: :string, gift_card_total: :string
12
+
13
+ attributes :po_number
14
+
15
+ attribute :order_count do |group|
16
+ group.orders.size
17
+ end
18
+
19
+ attribute :delivery_total do |group|
20
+ Spree::Money::Rounding.format(group.delivery_total, group.currency)
21
+ end
22
+
23
+ attribute :additional_tax_total do |group|
24
+ Spree::Money::Rounding.format(group.additional_tax_total, group.currency)
25
+ end
26
+
27
+ attribute :gift_card_total do |group|
28
+ Spree::Money::Rounding.format(group.gift_card_total, group.currency)
29
+ end
30
+
31
+ many :payments, resource: Spree::Emails::PaymentSerializer
32
+
33
+ # Every child order in full is what the webhook payload carries; the
34
+ # email reads the purchase-wide fields above instead.
35
+ many :orders, resource: proc { Spree.api.order_serializer }, if: proc { false }
36
+
37
+ many :fulfillment_groups, resource: Spree::Emails::FulfillmentGroupSerializer
38
+
39
+ # What no parcel carries — a download, an emailed gift card.
40
+ many :unfulfilled_items,
41
+ source: proc { unfulfilled_line_items },
42
+ resource: Spree::Emails::LineItemSerializer
43
+
44
+ # One delivery row for the whole purchase; the parcels break it down.
45
+ many :delivery_lines,
46
+ source: proc { delivery_total.zero? ? [] : [Spree::Emails::AmountLine.new(amount: delivery_total, currency: currency)] },
47
+ resource: Spree::Emails::AmountLineSerializer
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,27 @@
1
+ module Spree
2
+ module Emails
3
+ # An order as its emails read it: the Store API order plus what only an
4
+ # email shows — who to greet, the summary rows and line thumbnails.
5
+ class OrderSerializer < Spree::Api::V3::OrderSerializer
6
+ include Spree::Emails::PurchaseSummaryAttributes
7
+
8
+ without_display_money
9
+
10
+ many :order_promotions, key: :discounts, resource: Spree::Emails::AppliedPromotionSerializer
11
+ many :fees, resource: Spree::Emails::FeeSerializer
12
+ many :fulfillments, resource: Spree::Emails::FulfillmentSerializer
13
+ many :payments, resource: Spree::Emails::PaymentSerializer
14
+ one :gift_card, resource: Spree::Emails::GiftCardSerializer
15
+ one :market, resource: Spree::Emails::MarketSerializer
16
+
17
+ # One row per delivery rate, at its cost before discounts, so the rows
18
+ # add up to the total once a free-delivery promotion is listed.
19
+ many :delivery_lines,
20
+ source: proc {
21
+ Spree::Emails::AmountLine.group(fulfillments, currency: currency, amount: :cost, keep_zero: true,
22
+ label: ->(fulfillment) { fulfillment.selected_delivery_rate&.name })
23
+ },
24
+ resource: Spree::Emails::AmountLineSerializer
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,36 @@
1
+ module Spree
2
+ module Emails
3
+ # One line as it travels in one parcel. A line packed from two warehouses
4
+ # rides in two parcels, so the quantity and amount are the parcel's share
5
+ # rather than the whole line's.
6
+ class ParcelItemSerializer < Spree::Emails::BaseSerializer
7
+ include Spree::Emails::PurchasedItemAttributes
8
+
9
+ attribute :id do |item|
10
+ item.line_item.prefixed_id
11
+ end
12
+
13
+ attribute :name do |item|
14
+ item.line_item.name
15
+ end
16
+
17
+ attributes :quantity
18
+
19
+ attribute :sku do |item|
20
+ item.line_item.sku
21
+ end
22
+
23
+ attribute :options_text do |item|
24
+ item.line_item.options_text
25
+ end
26
+
27
+ attribute :price do |item|
28
+ Spree::Money::Rounding.format(item.line_item.price, item.line_item.currency, unit_price: true)
29
+ end
30
+
31
+ attribute :amount do |item|
32
+ Spree::Money::Rounding.format(item.line_item.price * item.quantity, item.line_item.currency)
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,6 @@
1
+ module Spree
2
+ module Emails
3
+ class PaymentMethodSerializer < Spree::Api::V3::PaymentMethodSerializer
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,11 @@
1
+ module Spree
2
+ module Emails
3
+ class PaymentSerializer < Spree::Api::V3::PaymentSerializer
4
+ without_display_money
5
+
6
+ typelize source: ['Record<string, unknown>', nullable: true]
7
+
8
+ one :payment_method, resource: Spree::Emails::PaymentMethodSerializer
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,41 @@
1
+ module Spree
2
+ module Emails
3
+ # What an email shows for a whole purchase — who to greet, the items and
4
+ # the summary rows — for both an order and a multi-seller purchase. The
5
+ # `spree/shared/purchase_totals` partial reads these names from either.
6
+ module PurchaseSummaryAttributes
7
+ extend ActiveSupport::Concern
8
+
9
+ included do
10
+ typelize customer_name: :string, total_minus_store_credits: :string
11
+
12
+ attribute :customer_name do |purchase|
13
+ purchase.name.presence || I18n.t('spree.customer')
14
+ end
15
+
16
+ attribute :total_minus_store_credits do |purchase|
17
+ Spree::Money::Rounding.format(purchase.total_minus_store_credits, purchase.currency)
18
+ end
19
+
20
+ many :line_items, key: :items, resource: Spree::Emails::LineItemSerializer
21
+ one :billing_address, resource: Spree::Emails::AddressSerializer
22
+ one :shipping_address, resource: Spree::Emails::AddressSerializer
23
+
24
+ many :promotion_discounts,
25
+ source: proc { Spree::Emails::AmountLine.group(discounts.promotion, currency: currency) },
26
+ resource: Spree::Emails::AmountLineSerializer
27
+
28
+ many :manual_discounts,
29
+ source: proc { Spree::Emails::AmountLine.group(discounts.manual, currency: currency) },
30
+ resource: Spree::Emails::AmountLineSerializer
31
+
32
+ # A multi-seller checkout apportions an order-level fee into one row
33
+ # per child, all under one label; summed back, it is the one charge the
34
+ # customer saw.
35
+ many :fee_lines,
36
+ source: proc { Spree::Emails::AmountLine.group(fees, currency: currency) },
37
+ resource: Spree::Emails::AmountLineSerializer
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,28 @@
1
+ module Spree
2
+ module Emails
3
+ # What an email shows for a purchased line besides its Store API fields: a
4
+ # link to the product on the storefront and a thumbnail. Shared by the line
5
+ # item and parcel item serializers.
6
+ module PurchasedItemAttributes
7
+ extend ActiveSupport::Concern
8
+ include Spree::ImagesHelper
9
+
10
+ included do
11
+ attribute :url do |item|
12
+ product = purchased_line_item(item).product
13
+ "#{params[:storefront_url]}/products/#{product.slug}" if product
14
+ end
15
+
16
+ attribute :image_url do |item|
17
+ spree_image_url(purchased_line_item(item).thumbnail, variant: :small)
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def purchased_line_item(item)
24
+ item.is_a?(Spree::LineItem) ? item : item.line_item
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,19 @@
1
+ module Spree
2
+ module Emails
3
+ class ReturnSerializer < Spree::Api::V3::ReturnSerializer
4
+ without_display_money
5
+
6
+ typelize refunded_total: :string
7
+
8
+ # What was actually paid back — not the Store API's refund_total, which
9
+ # is the figure the return was expected to refund.
10
+ attribute :refunded_total do |return_record|
11
+ Spree::Money::Rounding.format(return_record.refunded_total, return_record.currency)
12
+ end
13
+
14
+ many :returned_items,
15
+ source: proc { return_line_items.filter_map(&:line_item).uniq },
16
+ resource: Spree::Emails::LineItemSerializer
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,6 @@
1
+ module Spree
2
+ module Emails
3
+ class StockLocationSerializer < Spree::Api::V3::StockLocationSerializer
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,79 @@
1
+ module Spree
2
+ module Emails
3
+ module Samples
4
+ # Builds what an email's template receives, for previewing a template
5
+ # and checking it before it is published: from the store's most recent
6
+ # matching record, or one the merchant picks. Values that would carry a
7
+ # token (reset, invitation, download links) are always placeholders.
8
+ class Base
9
+ # @return [String, nil] the kind of record the merchant can pick
10
+ # ("order", "order_group", "fulfillment", "return"), or nil when the
11
+ # email needs none
12
+ def self.record_type
13
+ nil
14
+ end
15
+
16
+ # @return [Array<String>] the permission keys a caller needs to see this
17
+ # sample, since it shows a real record's data
18
+ def self.required_permissions
19
+ record_type ? %w[read_orders] : []
20
+ end
21
+
22
+ attr_reader :store, :record_id
23
+
24
+ # @param store [Spree::Store]
25
+ # @param record_id [String, nil] a prefixed id of the record to preview with
26
+ def initialize(store:, record_id: nil)
27
+ @store = store
28
+ @record_id = record_id.presence
29
+ end
30
+
31
+ # @return [Hash] the template's variables, before `store` and `locale` are added
32
+ def variables
33
+ raise NotImplementedError
34
+ end
35
+
36
+ # @param limit [Integer]
37
+ # @return [Array<Spree::EmailTemplates::SampleRecord>] the store's latest
38
+ # records the email can be previewed with; empty when it needs none
39
+ def recent_records(limit: 5)
40
+ return [] unless self.class.record_type
41
+
42
+ records.limit(limit).map do |record|
43
+ Spree::EmailTemplates::SampleRecord.new(
44
+ id: record.prefixed_id, label: record.try(:number).presence || record.prefixed_id, created_at: record.created_at
45
+ )
46
+ end
47
+ end
48
+
49
+ # @return [String] the currency the email's amounts are in
50
+ def currency
51
+ (self.class.record_type && record.try(:currency)).presence || store.default_currency
52
+ end
53
+
54
+ protected
55
+
56
+ def record
57
+ @record ||= if record_id
58
+ records.find_by_prefix_id!(record_id)
59
+ else
60
+ records.first || raise(Spree::EmailTemplates::NoSampleRecord, I18n.t('spree.email_templates.no_sample_record', record: self.class.record_type.to_s.humanize.downcase))
61
+ end
62
+ end
63
+
64
+ # Every lookup goes through the store, so another store's record never answers.
65
+ def records
66
+ raise NotImplementedError
67
+ end
68
+
69
+ def data(object, serializer)
70
+ Spree::Emails::TemplateData.call(object, serializer, store: store, currency: currency)
71
+ end
72
+
73
+ def placeholder_url(path)
74
+ "#{store.storefront_url.to_s.chomp('/')}/#{path}?token=preview"
75
+ end
76
+ end
77
+ end
78
+ end
79
+ end
@@ -0,0 +1,19 @@
1
+ module Spree
2
+ module Emails
3
+ module Samples
4
+ class CompanyInvitation < Base
5
+ def self.required_permissions
6
+ %w[read_customers]
7
+ end
8
+
9
+ def variables
10
+ {
11
+ company: data(store.companies.order(created_at: :desc).first || Spree::Company.new(name: 'Acme Corporation', store: store),
12
+ Spree.api.company_serializer),
13
+ accept_url: placeholder_url('account/company-invitation')
14
+ }
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,11 @@
1
+ module Spree
2
+ module Emails
3
+ module Samples
4
+ class DataExport < Base
5
+ def variables
6
+ { download_url: placeholder_url('account/data-export'), expires_at: 7.days.from_now.iso8601 }
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,28 @@
1
+ module Spree
2
+ module Emails
3
+ module Samples
4
+ # The files-ready email. Lists the order's own downloads when it has any,
5
+ # always with placeholder links.
6
+ class Downloads < Order
7
+ def variables
8
+ {
9
+ order: data(record, Spree::Emails::OrderSerializer),
10
+ downloads: downloads,
11
+ resend: false
12
+ }
13
+ end
14
+
15
+ private
16
+
17
+ def downloads
18
+ links = record.digital_links.includes(digital_asset: { attachment_attachment: :blob }).to_a
19
+ return [{ filename: 'guide.pdf', url: placeholder_url('downloads'), expires_at: nil }] if links.empty?
20
+
21
+ links.map do |link|
22
+ { filename: link.filename.to_s, url: placeholder_url('downloads'), expires_at: link.expires_at&.iso8601 }
23
+ end
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,33 @@
1
+ module Spree
2
+ module Emails
3
+ module Samples
4
+ class Fulfillment < Base
5
+ def self.required_permissions
6
+ %w[read_orders read_fulfillments]
7
+ end
8
+
9
+ def self.record_type
10
+ 'fulfillment'
11
+ end
12
+
13
+ def variables
14
+ {
15
+ fulfillment: data(record, Spree::Emails::FulfillmentSerializer),
16
+ order: data(record.order, Spree::Emails::OrderSerializer),
17
+ resend: false
18
+ }
19
+ end
20
+
21
+ def currency
22
+ record.order.currency
23
+ end
24
+
25
+ protected
26
+
27
+ def records
28
+ store.fulfillments.order(created_at: :desc)
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,11 @@
1
+ module Spree
2
+ module Emails
3
+ module Samples
4
+ class NewsletterConfirmation < Base
5
+ def variables
6
+ { confirmation_url: placeholder_url('newsletter/confirm') }
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,22 @@
1
+ module Spree
2
+ module Emails
3
+ module Samples
4
+ # Order confirmation and cancellation.
5
+ class Order < Base
6
+ def self.record_type
7
+ 'order'
8
+ end
9
+
10
+ def variables
11
+ { order: data(record, Spree::Emails::OrderSerializer), resend: false }
12
+ end
13
+
14
+ protected
15
+
16
+ def records
17
+ store.orders.complete.order(completed_at: :desc)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,22 @@
1
+ module Spree
2
+ module Emails
3
+ module Samples
4
+ # The confirmation of a purchase that divided into several orders.
5
+ class OrderGroup < Base
6
+ def self.record_type
7
+ 'order_group'
8
+ end
9
+
10
+ def variables
11
+ { order_group: data(record, Spree::Emails::OrderGroupSerializer), resend: false }
12
+ end
13
+
14
+ protected
15
+
16
+ def records
17
+ store.order_groups.order(created_at: :desc)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,15 @@
1
+ module Spree
2
+ module Emails
3
+ module Samples
4
+ class PasswordReset < Base
5
+ def variables
6
+ {
7
+ customer: data(Spree.customer_class.new(first_name: 'Jane', last_name: 'Doe', email: 'jane@example.com'),
8
+ Spree::Emails::CustomerSerializer),
9
+ reset_url: placeholder_url('account/reset-password')
10
+ }
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,17 @@
1
+ module Spree
2
+ module Emails
3
+ module Samples
4
+ class PaymentLink < Order
5
+ def variables
6
+ { order: data(record, Spree::Emails::OrderSerializer), payment_url: placeholder_url('checkout/payment') }
7
+ end
8
+
9
+ protected
10
+
11
+ def records
12
+ store.orders.order(created_at: :desc)
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,29 @@
1
+ module Spree
2
+ module Emails
3
+ module Samples
4
+ class Return < Base
5
+ def self.record_type
6
+ 'return'
7
+ end
8
+
9
+ def variables
10
+ {
11
+ return: data(record, Spree::Emails::ReturnSerializer),
12
+ order: data(record.order, Spree::Emails::OrderSerializer),
13
+ resend: false
14
+ }
15
+ end
16
+
17
+ def currency
18
+ record.order.currency
19
+ end
20
+
21
+ protected
22
+
23
+ def records
24
+ store.returns.order(created_at: :desc)
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -20,7 +20,7 @@ module Spree
20
20
 
21
21
  def send_customer_confirmation(order, event)
22
22
  return if order.confirmation_delivered?
23
- return if event.payload['notify_customer'] == false
23
+ return if event.metadata['notify_customer'] == false
24
24
  return unless order.store.prefers_send_consumer_transactional_emails?
25
25
 
26
26
  OrderMailer.confirm_email(order.id).deliver_later
@@ -46,7 +46,7 @@ module Spree
46
46
  def send_cancel_email(event)
47
47
  order = find_order(event)
48
48
  return unless order
49
- return if event.payload['notify_customer'] == false
49
+ return if event.metadata['notify_customer'] == false
50
50
 
51
51
  store = order.store
52
52
  return unless store.prefers_send_consumer_transactional_emails?