From ac42b913d0f8b78f30a58b22f342499e73121ea0 Mon Sep 17 00:00:00 2001
From: Daniele Palombo <387690+DanielePalombo@users.noreply.github.com>
Date: Wed, 13 Sep 2023 08:57:09 +0200
Subject: [PATCH 1/2] Rescue PromotionCode creation

Before this fix, when the Spree::PromotionCode creation failed, the
exception was raised and the job fails with error.
This commit rescues the creation exception and remove the failed code
from the codes list.
---
 .../spree/promotion_code/batch_builder.rb     |  6 ++++--
 .../promotion_code/batch_builder_spec.rb      | 19 +++++++++++++++++++
 2 files changed, 23 insertions(+), 2 deletions(-)

diff --git a/core/app/models/spree/promotion_code/batch_builder.rb b/core/app/models/spree/promotion_code/batch_builder.rb
index 26fa1d63745..ad312629927 100644
--- a/core/app/models/spree/promotion_code/batch_builder.rb
+++ b/core/app/models/spree/promotion_code/batch_builder.rb
@@ -39,13 +39,15 @@ def generate_random_codes
       new_codes = Array.new(max_codes_to_generate) { generate_random_code }.uniq
       codes_for_current_batch = get_unique_codes(new_codes)
 
-      codes_for_current_batch.each do |value|
+      codes_for_current_batch = codes_for_current_batch.map do |value|
         Spree::PromotionCode.create!(
           value: value,
           promotion: promotion,
           promotion_code_batch: promotion_code_batch
         )
-      end
+      rescue ActiveRecord::RecordInvalid
+        nil
+      end.compact
       created_codes += codes_for_current_batch.size
     end
   end
diff --git a/core/spec/models/spree/promotion_code/batch_builder_spec.rb b/core/spec/models/spree/promotion_code/batch_builder_spec.rb
index 67e513ba11b..e056d62893a 100644
--- a/core/spec/models/spree/promotion_code/batch_builder_spec.rb
+++ b/core/spec/models/spree/promotion_code/batch_builder_spec.rb
@@ -72,6 +72,25 @@
         subject.build_promotion_codes
         expect(promotion.codes.size).to eq(number_of_codes)
       end
+
+      context "when promotion_code creation returns an error" do
+        before do
+          @raise_exception = true
+          allow(Spree::PromotionCode).to receive(:create!) do
+            if @raise_exception
+              @raise_exception = false
+              raise(ActiveRecord::RecordInvalid)
+            else
+              create(:promotion_code, promotion: promotion)
+            end
+          end
+        end
+
+        it "creates the correct number of codes anyway" do
+          subject.build_promotion_codes
+          expect(promotion.codes.size).to eq(number_of_codes)
+        end
+      end
     end
   end
 

From 50ad9a0c7ba9f8cf5940a7c70ce8a00bbcd1988d Mon Sep 17 00:00:00 2001
From: Daniele Palombo <387690+DanielePalombo@users.noreply.github.com>
Date: Wed, 13 Sep 2023 08:57:52 +0200
Subject: [PATCH 2/2] Add the ability to restart PromotionCode Batch

---
 core/app/models/spree/promotion_code/batch_builder.rb  |  3 ++-
 .../models/spree/promotion_code/batch_builder_spec.rb  | 10 ++++++++++
 2 files changed, 12 insertions(+), 1 deletion(-)

diff --git a/core/app/models/spree/promotion_code/batch_builder.rb b/core/app/models/spree/promotion_code/batch_builder.rb
index ad312629927..0b8c80a532a 100644
--- a/core/app/models/spree/promotion_code/batch_builder.rb
+++ b/core/app/models/spree/promotion_code/batch_builder.rb
@@ -30,7 +30,8 @@ def build_promotion_codes
   private
 
   def generate_random_codes
-    created_codes = 0
+    created_codes = promotion_code_batch.promotion_codes.count
+
     batch_size = @options[:batch_size]
 
     while created_codes < number_of_codes
diff --git a/core/spec/models/spree/promotion_code/batch_builder_spec.rb b/core/spec/models/spree/promotion_code/batch_builder_spec.rb
index e056d62893a..7b52fffe859 100644
--- a/core/spec/models/spree/promotion_code/batch_builder_spec.rb
+++ b/core/spec/models/spree/promotion_code/batch_builder_spec.rb
@@ -91,6 +91,16 @@
           expect(promotion.codes.size).to eq(number_of_codes)
         end
       end
+
+      context "when same promotion_codes are already present" do
+        before do
+          create_list(:promotion_code, 11, promotion: promotion, promotion_code_batch: promotion_code_batch)
+        end
+
+        it "creates only the missing promotion_codes" do
+          expect { subject.build_promotion_codes }.to change { promotion.codes.size }.by(39)
+        end
+      end
     end
   end