diff --git a/app/classes/woo/products.rb b/app/classes/woo/products.rb index dd852108d..37d9f8046 100644 --- a/app/classes/woo/products.rb +++ b/app/classes/woo/products.rb @@ -3,10 +3,10 @@ class Woo::Products < Woo::Base # 2. update stock of products in webstore # Create products to woocommerce - def create + def create(all: false) created_count = 0 - products.each do |product| + products_to_create(all: all).each do |product| if get_sku(product.tuoteno) logger.info "Tuote #{product.tuoteno} on jo verkkokaupassa" @@ -16,14 +16,16 @@ def create created_count += create_product(product) end + Keyword::WooCheckpoint.update_timestamp(:create) + logger.info "Lisättiin #{created_count} tuotetta verkkokauppaan" end # Update product stock quantity - def update + def update(all: false) updated_count = 0 - products.each do |product| + products_to_update(all: all).each do |product| woo_product = get_sku(product.tuoteno) unless woo_product @@ -35,6 +37,8 @@ def update updated_count += update_stock(woo_product['id'], product) end + Keyword::WooCheckpoint.update_timestamp(:update) + logger.info "Päivitettiin #{updated_count} tuotteen saldot" end @@ -46,6 +50,25 @@ def products Product.where(status: %w(A P)).where(hinnastoon: 'W') end + def products_to_create(all: false) + timestamp = all ? nil : Keyword::WooCheckpoint.last_run_at(:create) + + return products unless timestamp + + products.where('luontiaika >= ?', timestamp) + end + + def products_to_update(all: false) + timestamp = all ? nil : Keyword::WooCheckpoint.last_run_at(:update) + + return products unless timestamp + + products.select do |product| + product.rows.where('laadittu >= ?', timestamp).any? || + product.transactions.where('laadittu >= ?', timestamp).any? + end + end + def product_hash(product) defaults = { name: product.nimitys, diff --git a/app/models/keyword.rb b/app/models/keyword.rb index 41dce0194..36ac7fd0d 100644 --- a/app/models/keyword.rb +++ b/app/models/keyword.rb @@ -140,6 +140,7 @@ def self.child_class_names VARASTOLUOKKA: Keyword, VARASTORYHMA: Keyword, VARTOIMTULOSTIN: Keyword, + WOO_CHECKPOINT: Keyword::WooCheckpoint, WOO_FIELD: Keyword::WooField, Y: Keyword, }.stringify_keys diff --git a/app/models/keyword/woo_checkpoint.rb b/app/models/keyword/woo_checkpoint.rb new file mode 100644 index 000000000..a84087d5c --- /dev/null +++ b/app/models/keyword/woo_checkpoint.rb @@ -0,0 +1,21 @@ +class Keyword::WooCheckpoint < Keyword + def self.sti_name + :WOO_CHECKPOINT + end + + def self.update_timestamp(run_type) + checkpoint = last || new + timestamps = checkpoint.selite.present? ? JSON.parse(checkpoint.selite, symbolize_names: true) : {} + timestamps[run_type.to_sym] = Time.current.change(usec: 0) + checkpoint.selite = timestamps.to_json + checkpoint.save! + end + + def self.last_run_at(run_type) + checkpoint = last + return unless checkpoint + timestamp = JSON.parse(checkpoint.selite, symbolize_names: true)[run_type.to_sym] + return unless timestamp + Time.zone.parse(timestamp) + end +end diff --git a/app/models/product.rb b/app/models/product.rb index 2c452506f..0793fe5da 100644 --- a/app/models/product.rb +++ b/app/models/product.rb @@ -32,9 +32,11 @@ class Product < BaseModel o.has_many :manufacture_rows, class_name: 'ManufactureOrder::Row' o.has_many :product_suppliers, class_name: 'Product::Supplier' o.has_many :purchase_order_rows, class_name: 'PurchaseOrder::Row' + o.has_many :rows, class_name: 'Row' o.has_many :sales_order_rows, class_name: 'SalesOrder::Row' o.has_many :shelf_locations o.has_many :stock_transfer_rows, class_name: 'StockTransfer::Row' + o.has_many :transactions, class_name: 'Product::Transaction' end has_many :warehouses, through: :shelf_locations diff --git a/app/models/product/transaction.rb b/app/models/product/transaction.rb new file mode 100644 index 000000000..e08da74ad --- /dev/null +++ b/app/models/product/transaction.rb @@ -0,0 +1,7 @@ +class Product::Transaction < BaseModel + belongs_to :product, foreign_key: :tuoteno, primary_key: :tuoteno + + self.table_name = :tapahtuma + self.primary_key = :tunnus + self.record_timestamps = false +end diff --git a/test/classes/woo/products_test.rb b/test/classes/woo/products_test.rb index 12fb296a1..1237388e1 100644 --- a/test/classes/woo/products_test.rb +++ b/test/classes/woo/products_test.rb @@ -4,6 +4,7 @@ class Woo::ProductsTest < ActiveSupport::TestCase fixtures %w( products + sales_order/rows ) setup do @@ -16,6 +17,9 @@ class Woo::ProductsTest < ActiveSupport::TestCase Product.update_all(hinnastoon: '') products(:ski).update(hinnastoon: 'w') + + Keyword::WooCheckpoint.delete_all + Product::Transaction.delete_all end test 'should initialize' do @@ -103,4 +107,210 @@ class Woo::ProductsTest < ActiveSupport::TestCase product = @woocommerce.send(:products).first assert_equal response, @woocommerce.send(:product_hash, product) end + + test 'checkpoint is created after first create run' do + @woocommerce.stub :get_sku, nil do + @woocommerce.stub :woo_post, 'id' => 1 do + assert_difference 'Keyword::WooCheckpoint.count' do + @woocommerce.create + end + end + end + + assert_in_delta Keyword::WooCheckpoint.last_run_at(:create), Time.current, 10 + end + + test 'checkpoint is updated if already present when creating products' do + @woocommerce.stub :get_sku, nil do + @woocommerce.stub :woo_post, 'id' => 1 do + assert_difference 'Keyword::WooCheckpoint.count' do + 2.times { @woocommerce.create } + end + end + end + + assert_in_delta Keyword::WooCheckpoint.last_run_at(:create), Time.current, 10 + end + + test 'checkpoint is updated after updating stocks' do + @woocommerce.stub :get_sku, {} do + @woocommerce.stub :woo_put, 'id' => 1 do + assert_difference 'Keyword::WooCheckpoint.count' do + 2.times { @woocommerce.update } + end + end + end + + assert_in_delta Keyword::WooCheckpoint.last_run_at(:update), Time.current, 10 + end + + test 'different timestamps in checkpoint work independently' do + @woocommerce.stub :get_sku, nil do + @woocommerce.stub :woo_post, 'id' => 1 do + assert_difference 'Keyword::WooCheckpoint.count' do + travel(-1.day) { @woocommerce.create } + @woocommerce.update + end + end + end + + refute_in_delta Keyword::WooCheckpoint.last_run_at(:create), Time.current, 10 + assert_in_delta Keyword::WooCheckpoint.last_run_at(:update), Time.current, 10 + end + + test 'all products are created if no create timestamp in checkpoint' do + counter = 0 + + block = proc do + counter += 1 + { 'id' => 1 } + end + + @woocommerce.stub :get_sku, nil do + @woocommerce.stub :woo_post, block do + @woocommerce.create + end + end + + assert_equal 1, counter + end + + test 'only new products are created when create timestamp is found' do + products(:ski).update!(luontiaika: 1.second.ago) + + Keyword::WooCheckpoint.update_timestamp(:create) + + counter = 0 + + block = proc do + counter += 1 + { 'id' => 1 } + end + + @woocommerce.stub :get_sku, nil do + @woocommerce.stub :woo_post, block do + @woocommerce.create + end + end + + assert_equal 0, counter + end + + test 'all products can be created by passing option even when timestamp is found' do + products(:ski).update!(luontiaika: 1.second.ago) + + Keyword::WooCheckpoint.update_timestamp(:create) + + counter = 0 + + block = proc do + counter += 1 + { 'id' => 1 } + end + + @woocommerce.stub :get_sku, nil do + @woocommerce.stub :woo_post, block do + @woocommerce.create(all: true) + end + end + + assert_equal 1, counter + end + + test 'all products are updated if no update timestamp in checkpoint' do + counter = 0 + + block = proc do + counter += 1 + { 'id' => 1 } + end + + @woocommerce.stub :get_sku, {} do + @woocommerce.stub :woo_put, block do + @woocommerce.update + end + end + + assert_equal 1, counter + end + + test 'only products with new rows are updated when update timestamp is found' do + Keyword::WooCheckpoint.update_timestamp(:update) + + counter = 0 + + block = proc do + counter += 1 + { 'id' => 1 } + end + + @woocommerce.stub :get_sku, {} do + @woocommerce.stub :woo_put, block do + @woocommerce.update + end + end + + assert_equal 0, counter + end + + test 'products with new rows are updated when timestamp is found' do + Keyword::WooCheckpoint.update_timestamp(:update) + + products(:ski).sales_order_rows.create!(laadittu: 1.second.from_now) + + counter = 0 + + block = proc do + counter += 1 + { 'id' => 1 } + end + + @woocommerce.stub :get_sku, {} do + @woocommerce.stub :woo_put, block do + @woocommerce.update + end + end + + assert_equal 1, counter + end + + test 'products with new transactions are updated when timestamp is found' do + Keyword::WooCheckpoint.update_timestamp(:update) + + products(:ski).transactions.create!(laadittu: 1.second.from_now) + + counter = 0 + + block = proc do + counter += 1 + { 'id' => 1 } + end + + @woocommerce.stub :get_sku, {} do + @woocommerce.stub :woo_put, block do + @woocommerce.update + end + end + + assert_equal 1, counter + end + + test 'all products can be updated by passing option even when timestamp is found' do + Keyword::WooCheckpoint.update_timestamp(:update) + + counter = 0 + + block = proc do + counter += 1 + { 'id' => 1 } + end + + @woocommerce.stub :get_sku, {} do + @woocommerce.stub :woo_put, block do + @woocommerce.update(all: true) + end + end + + assert_equal 1, counter + end end