From a4d84e1a3b2e157280b71d08e4db0fefe76264ab Mon Sep 17 00:00:00 2001 From: Oleksii Kriuchykhin Date: Wed, 12 Jan 2022 17:13:50 +0100 Subject: [PATCH] Add initial implementation of inventory stock management [SCI-6402] --- app/models/my_module_repository_row.rb | 16 ++ app/models/repository_ledger_record.rb | 6 + app/models/repository_row.rb | 5 + app/models/repository_stock_value.rb | 54 ++++++ ...0151006_add_repository_stock_management.rb | 26 +++ db/structure.sql | 172 +++++++++++++++++- 6 files changed, 277 insertions(+), 2 deletions(-) create mode 100644 app/models/repository_ledger_record.rb create mode 100644 app/models/repository_stock_value.rb create mode 100644 db/migrate/20220110151006_add_repository_stock_management.rb diff --git a/app/models/my_module_repository_row.rb b/app/models/my_module_repository_row.rb index 48f096c13..acad0c607 100644 --- a/app/models/my_module_repository_row.rb +++ b/app/models/my_module_repository_row.rb @@ -10,4 +10,20 @@ class MyModuleRepositoryRow < ApplicationRecord inverse_of: :my_module_repository_rows validates :repository_row, uniqueness: { scope: :my_module } + + around_save :deduct_stock_balance, if: :stock_consumption_changed? + + private + + def deduct_stock_balance + stock_value = repository_row.repository_stock_value + delta = stock_consumption_was.to_d - stock_consumption.to_d + lock! + stock_value.lock! + stock_value.amount = stock_value.amount - delta + yield + stock_value.save! + stock_value.repository_ledger_records.create!(user: last_modified_by, amount: delta, balance: stock_value.amount) + save! + end end diff --git a/app/models/repository_ledger_record.rb b/app/models/repository_ledger_record.rb new file mode 100644 index 000000000..39c0e9fd6 --- /dev/null +++ b/app/models/repository_ledger_record.rb @@ -0,0 +1,6 @@ +# frozen_string_literal: true + +class RepositoryLedgerRecord < ApplicationRecord + belongs_to :repository_row + belongs_to :reference, polymorphic: true, inverse_of: :repository_ledger_record +end diff --git a/app/models/repository_row.rb b/app/models/repository_row.rb index c27e07853..6172ae7f1 100644 --- a/app/models/repository_row.rb +++ b/app/models/repository_row.rb @@ -42,6 +42,11 @@ class RepositoryRow < ApplicationRecord source: :value, source_type: class_name end + has_one :repository_stock_cell, -> { where(value_type: 'RepositoryStockValue') }, class_name: 'RepositoryCell', + inverse_of: :repository_row + has_one :repository_stock_value, class_name: 'RepositoryStockValue', through: :repository_stock_cell, + source: :value, source_type: 'RepositoryStockValue' + has_many :repository_columns, through: :repository_cells has_many :my_module_repository_rows, inverse_of: :repository_row, dependent: :destroy diff --git a/app/models/repository_stock_value.rb b/app/models/repository_stock_value.rb new file mode 100644 index 000000000..319d7fccd --- /dev/null +++ b/app/models/repository_stock_value.rb @@ -0,0 +1,54 @@ +# frozen_string_literal: true + +class RepositoryStockValue < ApplicationRecord + belongs_to :created_by, class_name: 'User', optional: true, inverse_of: :created_repository_stock_value + belongs_to :last_modified_by, class_name: 'User', optional: true, inverse_of: :modified_repository_stock_value + has_one :repository_cell, as: :value, dependent: :destroy, inverse_of: :value + has_many :repository_ledger_records, dependent: :destroy + accepts_nested_attributes_for :repository_cell + + validates :repository_cell, presence: true + + SORTABLE_COLUMN_NAME = 'repository_stock_values.amount' + + def formatted + "#{amount} #{units}" + end + + def data_changed?(new_data) + BigDecimal(new_data.to_s) != data + end + + def update_data!(new_data, user) + self.data = BigDecimal(new_data.to_s) + self.last_modified_by = user + save! + end + + def snapshot!(cell_snapshot) + value_snapshot = dup + value_snapshot.assign_attributes( + repository_cell: cell_snapshot, + created_at: created_at, + updated_at: updated_at + ) + value_snapshot.save! + end + + def data + amount + end + + def self.new_with_payload(payload, attributes) + value = new(attributes) + value.amount = payload + value + end + + def update_stock_with_ledger!(amount) + delta = amount.to_d - self.amount.to_d + repository_ledger_records.create!(user: last_modified_by, amount: delta, balance: amount) + end + + alias export_formatted formatted +end diff --git a/db/migrate/20220110151006_add_repository_stock_management.rb b/db/migrate/20220110151006_add_repository_stock_management.rb new file mode 100644 index 000000000..a46135696 --- /dev/null +++ b/db/migrate/20220110151006_add_repository_stock_management.rb @@ -0,0 +1,26 @@ +# frozen_string_literal: true + +class AddRepositoryStockManagement < ActiveRecord::Migration[6.1] + def change + create_table :repository_ledger_records do |t| + t.references :repository_row, null: false, index: true, foreign_key: true + t.references :reference, polymorphic: true, null: false + t.decimal :amount + t.decimal :balance + t.references :user, null: true + + t.timestamps + end + + create_table :repository_stock_values do |t| + t.decimal :amount, index: true + t.string :units + t.references :last_modified_by, index: true, foreign_key: { to_table: :users } + t.references :created_by, index: true, foreign_key: { to_table: :users } + + t.timestamps + end + + add_column :my_module_repository_rows, :stock_consumption, :decimal + end +end diff --git a/db/structure.sql b/db/structure.sql index 57a26358e..8c54ac592 100644 --- a/db/structure.sql +++ b/db/structure.sql @@ -703,7 +703,8 @@ CREATE TABLE public.my_module_repository_rows ( my_module_id integer, assigned_by_id bigint NOT NULL, created_at timestamp without time zone, - updated_at timestamp without time zone + updated_at timestamp without time zone, + stock_consumption numeric ); @@ -1758,6 +1759,42 @@ CREATE SEQUENCE public.repository_date_time_values_id_seq ALTER SEQUENCE public.repository_date_time_values_id_seq OWNED BY public.repository_date_time_values.id; +-- +-- Name: repository_ledger_records; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.repository_ledger_records ( + id bigint NOT NULL, + repository_row_id bigint NOT NULL, + reference_type character varying NOT NULL, + reference_id bigint NOT NULL, + amount numeric, + balance numeric, + user_id bigint, + created_at timestamp(6) without time zone NOT NULL, + updated_at timestamp(6) without time zone NOT NULL +); + + +-- +-- Name: repository_ledger_records_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.repository_ledger_records_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: repository_ledger_records_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.repository_ledger_records_id_seq OWNED BY public.repository_ledger_records.id; + + -- -- Name: repository_list_items; Type: TABLE; Schema: public; Owner: - -- @@ -1967,6 +2004,40 @@ CREATE SEQUENCE public.repository_status_values_id_seq ALTER SEQUENCE public.repository_status_values_id_seq OWNED BY public.repository_status_values.id; +-- +-- Name: repository_stock_values; Type: TABLE; Schema: public; Owner: - +-- + +CREATE TABLE public.repository_stock_values ( + id bigint NOT NULL, + amount numeric, + units character varying, + last_modified_by_id bigint, + created_by_id bigint, + created_at timestamp(6) without time zone NOT NULL, + updated_at timestamp(6) without time zone NOT NULL +); + + +-- +-- Name: repository_stock_values_id_seq; Type: SEQUENCE; Schema: public; Owner: - +-- + +CREATE SEQUENCE public.repository_stock_values_id_seq + START WITH 1 + INCREMENT BY 1 + NO MINVALUE + NO MAXVALUE + CACHE 1; + + +-- +-- Name: repository_stock_values_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: - +-- + +ALTER SEQUENCE public.repository_stock_values_id_seq OWNED BY public.repository_stock_values.id; + + -- -- Name: repository_table_states; Type: TABLE; Schema: public; Owner: - -- @@ -3419,6 +3490,13 @@ ALTER TABLE ONLY public.repository_date_time_range_values ALTER COLUMN id SET DE ALTER TABLE ONLY public.repository_date_time_values ALTER COLUMN id SET DEFAULT nextval('public.repository_date_time_values_id_seq'::regclass); +-- +-- Name: repository_ledger_records id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.repository_ledger_records ALTER COLUMN id SET DEFAULT nextval('public.repository_ledger_records_id_seq'::regclass); + + -- -- Name: repository_list_items id; Type: DEFAULT; Schema: public; Owner: - -- @@ -3461,6 +3539,13 @@ ALTER TABLE ONLY public.repository_status_items ALTER COLUMN id SET DEFAULT next ALTER TABLE ONLY public.repository_status_values ALTER COLUMN id SET DEFAULT nextval('public.repository_status_values_id_seq'::regclass); +-- +-- Name: repository_stock_values id; Type: DEFAULT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.repository_stock_values ALTER COLUMN id SET DEFAULT nextval('public.repository_stock_values_id_seq'::regclass); + + -- -- Name: repository_table_states id; Type: DEFAULT; Schema: public; Owner: - -- @@ -4060,6 +4145,14 @@ ALTER TABLE ONLY public.repository_date_time_values ADD CONSTRAINT repository_date_time_values_pkey PRIMARY KEY (id); +-- +-- Name: repository_ledger_records repository_ledger_records_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.repository_ledger_records + ADD CONSTRAINT repository_ledger_records_pkey PRIMARY KEY (id); + + -- -- Name: repository_list_items repository_list_items_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -4108,6 +4201,14 @@ ALTER TABLE ONLY public.repository_status_values ADD CONSTRAINT repository_status_values_pkey PRIMARY KEY (id); +-- +-- Name: repository_stock_values repository_stock_values_pkey; Type: CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.repository_stock_values + ADD CONSTRAINT repository_stock_values_pkey PRIMARY KEY (id); + + -- -- Name: repository_table_states repository_table_states_pkey; Type: CONSTRAINT; Schema: public; Owner: - -- @@ -5416,6 +5517,27 @@ CREATE INDEX index_repository_date_time_range_values_on_last_modified_by_id ON p CREATE INDEX index_repository_date_time_range_values_on_start_time ON public.repository_date_time_range_values USING btree (start_time); +-- +-- Name: index_repository_ledger_records_on_reference; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_repository_ledger_records_on_reference ON public.repository_ledger_records USING btree (reference_type, reference_id); + + +-- +-- Name: index_repository_ledger_records_on_repository_row_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_repository_ledger_records_on_repository_row_id ON public.repository_ledger_records USING btree (repository_row_id); + + +-- +-- Name: index_repository_ledger_records_on_user_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_repository_ledger_records_on_user_id ON public.repository_ledger_records USING btree (user_id); + + -- -- Name: index_repository_list_items_on_created_by_id; Type: INDEX; Schema: public; Owner: - -- @@ -5577,6 +5699,27 @@ CREATE INDEX index_repository_status_values_on_created_by_id ON public.repositor CREATE INDEX index_repository_status_values_on_last_modified_by_id ON public.repository_status_values USING btree (last_modified_by_id); +-- +-- Name: index_repository_stock_values_on_amount; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_repository_stock_values_on_amount ON public.repository_stock_values USING btree (amount); + + +-- +-- Name: index_repository_stock_values_on_created_by_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_repository_stock_values_on_created_by_id ON public.repository_stock_values USING btree (created_by_id); + + +-- +-- Name: index_repository_stock_values_on_last_modified_by_id; Type: INDEX; Schema: public; Owner: - +-- + +CREATE INDEX index_repository_stock_values_on_last_modified_by_id ON public.repository_stock_values USING btree (last_modified_by_id); + + -- -- Name: index_repository_table_states_on_repository_id; Type: INDEX; Schema: public; Owner: - -- @@ -6281,6 +6424,14 @@ ALTER TABLE ONLY public.team_repositories ADD CONSTRAINT fk_rails_15daa6a6bf FOREIGN KEY (repository_id) REFERENCES public.repositories(id); +-- +-- Name: repository_ledger_records fk_rails_16d35cbff3; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.repository_ledger_records + ADD CONSTRAINT fk_rails_16d35cbff3 FOREIGN KEY (repository_row_id) REFERENCES public.repository_rows(id); + + -- -- Name: zip_exports fk_rails_1952fc2261; Type: FK CONSTRAINT; Schema: public; Owner: - -- @@ -7137,6 +7288,14 @@ ALTER TABLE ONLY public.repository_asset_values ADD CONSTRAINT fk_rails_bb983a4d66 FOREIGN KEY (created_by_id) REFERENCES public.users(id); +-- +-- Name: repository_stock_values fk_rails_c111ff8695; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.repository_stock_values + ADD CONSTRAINT fk_rails_c111ff8695 FOREIGN KEY (created_by_id) REFERENCES public.users(id); + + -- -- Name: my_module_status_flows fk_rails_c19dc6b9e9; Type: FK CONSTRAINT; Schema: public; Owner: - -- @@ -7441,6 +7600,14 @@ ALTER TABLE ONLY public.report_elements ADD CONSTRAINT fk_rails_f5d944fc4d FOREIGN KEY (asset_id) REFERENCES public.assets(id); +-- +-- Name: repository_stock_values fk_rails_f83c438412; Type: FK CONSTRAINT; Schema: public; Owner: - +-- + +ALTER TABLE ONLY public.repository_stock_values + ADD CONSTRAINT fk_rails_f83c438412 FOREIGN KEY (last_modified_by_id) REFERENCES public.users(id); + + -- -- Name: tags fk_rails_f95c7c81ac; Type: FK CONSTRAINT; Schema: public; Owner: - -- @@ -7699,6 +7866,7 @@ INSERT INTO "schema_migrations" (version) VALUES ('20210720112050'), ('20210811103123'), ('20210906132120'), -('20211103115450'); +('20211103115450'), +('20220110151006');