-
-
Notifications
You must be signed in to change notification settings - Fork 573
Add an option to control whether "in-kind value" of each item should be added in export of donations and distributions #5122
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 7 commits
e3f8594
49ce0a6
11e5743
3908867
ab3e61c
6c26a56
e205b5c
d653f8e
62e076f
63b333e
2abbe5e
b09b697
872544c
6dae33a
2670caa
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -833,4 +833,4 @@ DEPENDENCIES | |
| webmock (~> 3.24) | ||
|
|
||
| BUNDLED WITH | ||
| 2.6.6 | ||
| 2.6.7 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,73 @@ | ||
| module Exports | ||
| class ExportLineItemColumnsAdder | ||
| def initialize | ||
| @registry = [] | ||
| end | ||
|
|
||
| def headers(item_headers) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In general this code is written in an unusual manner... I'd need to see a bunch more comments here since I can't really understand what some of it is supposed to be doing. What does "registry" contain? What kind of "row" does
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The registry contains classes that implement
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The explanation has to be in comments, not in the PR. But in general, if you have to explain what the code is doing via comments, it might be doing things in a way that isn't straightforward. |
||
| @item_headers ||= item_headers.each_with_index.to_h | ||
| @item_headers.keys.flat_map do |header| | ||
| @registry.inject([header]) do |arr, klass| | ||
| arr.append(klass.convert_header(header)) | ||
| end | ||
| end | ||
| end | ||
|
|
||
| def register(column) | ||
| @registry.append(column) | ||
| end | ||
|
|
||
| def get_row(line_items) | ||
| chunk_size = @registry.size + 1 | ||
| row = Array.new(@item_headers.size * chunk_size, 0) | ||
| @registry.each_with_index do |klass, idx| | ||
| row.each_with_index { |_, i| row[i] = klass.init_value if i % chunk_size == idx + 1 } | ||
| end | ||
| line_items.each do |line_item| | ||
| idx = @item_headers[line_item.item.name] | ||
| next unless idx | ||
|
|
||
| process_additional_columns(row, idx, line_item) | ||
| end | ||
| row | ||
| end | ||
|
|
||
| private | ||
|
|
||
| def process_additional_columns(row, idx, line_item) | ||
| idx *= (@registry.size + 1) | ||
| row[idx] += line_item.quantity | ||
| @registry.each_with_index do |klass, index| | ||
| row[idx + index + 1] += klass.value(line_item) | ||
| end | ||
| end | ||
| end | ||
|
|
||
| class ExportColumnsBase | ||
| def self.convert_header(header) | ||
| raise NotImplementedError, "Subclass must implement convert_header." | ||
| end | ||
|
|
||
| def self.init_value | ||
| raise NotImplementedError, "Subclass must implement init_value." | ||
| end | ||
|
|
||
| def self.value(raw_value) | ||
| raise NotImplementedError, "Subclass must implement value." | ||
| end | ||
| end | ||
|
|
||
| class ExportInKindTotalValue < Exports::ExportColumnsBase | ||
| def self.convert_header(header) | ||
| "#{header} In-Kind Value" | ||
| end | ||
|
|
||
| def self.init_value | ||
| Money.new(0) | ||
| end | ||
|
|
||
| def self.value(line_item) | ||
| Money.new(line_item.value_per_line_item) | ||
| end | ||
| end | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| class AddIncludeInKindValuesInExportedFilesToOrganizations < ActiveRecord::Migration[8.0] | ||
| def change | ||
| add_column :organizations, :include_in_kind_values_in_exported_files, :boolean | ||
| add_check_constraint :organizations, "include_in_kind_values_in_exported_files IS NOT NULL", name: "include_in_kind_values_in_exported_files_not_null", validate: false | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This seems a very awkward way of just saying
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Adding the NOT NULL constraint directly will lock the table. This method helps avoid that. I don't know how big the table will be, so I think it's better to avoid it.
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Organizations is a tiny table. You can lock it. |
||
| end | ||
| end | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| class BackfillFalseToIncludeInKindValuesInExportedFilesInOrganizations < ActiveRecord::Migration[8.0] | ||
| def up | ||
| Organization.unscoped.in_batches do |relation| | ||
| relation.update_all include_in_kind_values_in_exported_files: false | ||
| sleep(0.01) | ||
| end | ||
|
|
||
| validate_check_constraint :organizations, name: "include_in_kind_values_in_exported_files_not_null" | ||
| end | ||
|
|
||
| def down | ||
|
|
||
| end | ||
| end |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,13 @@ | ||
| class EnforceNotNullOnIncludeInKindValuesInExportedFilesInOrganizations < ActiveRecord::Migration[8.0] | ||
| def up | ||
| change_column_null :organizations, :include_in_kind_values_in_exported_files, false | ||
| change_column_default :organizations, :include_in_kind_values_in_exported_files, false | ||
| remove_check_constraint :organizations, name: "include_in_kind_values_in_exported_files_not_null" | ||
| end | ||
|
|
||
| def down | ||
| add_check_constraint :organizations, "include_in_kind_values_in_exported_files IS NOT NULL", name: "include_in_kind_values_in_exported_files_not_null", validate: false | ||
| change_column_default :organizations, :include_in_kind_values_in_exported_files, nil | ||
| change_column_null :organizations, :include_in_kind_values_in_exported_files, true | ||
| end | ||
| end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems like an overly complex way of adding a column to an export. You're basically turning a single
ifstatement into a factory pattern. Can you explain why you did it this way?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If you have other attributes that need to be added to the exported CSV, just create another class and register it here. There's no need to modify any other code. Since both distribution and donation share the same export functionality, I extracted it to avoid duplication.
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a case of unnecessary DRY along with a big helping of YAGNI. As far as we know there are no additional attributes that will need this, and in general the idea that "attributes in a CSV" is a generic feature is probably not a helpful construct. I'd much prefer a simpler approach even if it means a little bit of duplicated code.