Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,7 +146,7 @@ This codebase (Rails 8.1)
| `Communicable` | Gives a record the `notifications` association, nested attributes, a default `communications_scope`, and a `before_validation` addressing a hand-logged communication to the record's `communications_email`. Included by every model whose form renders `shared/_comments_and_communications` |
| `AuthorCreditable` | Author attribution. Credits are formatted by the credited **person's profile** (`Person#display_name_preference`), not by the record. The record's `author_credit_preference` is the consent snapshot taken at create time and is human-editable only on the author credit divergences page. Anonymity is a one-way latch: the profile or the record can set it, neither can strip it from the other. `credits_to_org` flips the unattributed label to "AWBW Staff"; `credits_creator` (idea models, workshop logs) credits the submitting account's person when no author is named |
| `Featureable` | `featured`, `publicly_featured` scopes |
| `FilemakerCoded` | Reads `filemaker_code` as the trimmed, comma-separated **list** it is (Person, Organization, Affiliation): `#filemaker_codes` for the set a record answers to, `.join_filemaker_codes` to combine any mix of strings/lists into one normalized column value (both dedupers union the codes on merge). `filemaker_archived_in` names the `Fm*` archive table(s) a model's codes point into (`.filemaker_archives`), and `.with_filemaker_code(code)` finds records carrying a code. Nothing should compare the whole column against a single code β€” `FilemakerHelper#filemaker_code_links` renders one archive link per code, and `fm_archives#show` links back to the portal records carrying its code |
| `FilemakerCoded` | Reads `filemaker_code` as the trimmed, comma-separated **list** it is (Person, Organization, Affiliation, Payment, Workshop, Resource): `#filemaker_codes` for the set a record answers to, `.join_filemaker_codes` to combine any mix of strings/lists into one normalized column value (both dedupers union the codes on merge). `filemaker_archived_in` names the `Fm*` archive table(s) a model's codes point into (`.filemaker_archives`), and `.with_filemaker_code(code)` finds records carrying a code. Nothing should compare the whole column against a single code β€” `FilemakerHelper#filemaker_code_links` renders one archive link per code, and `fm_archives#show` links back to the portal records carrying its code |
| `Mentioner` | ActionText @mention extraction and grouping |
| `NameFilterable` | Name-based filtering |
| `PayChargeExtensions` | Included into `Pay::Charge` (via `config/initializers/pay.rb`): turns a Stripe charge into an AWBW `Payment` + `Allocation`, resolving the membership invoice it pays |
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/fm_archives_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ class FmArchivesController < ApplicationController
{ label: "Other", icon: "πŸ“Ž", tables: %w[notes] }
]

PORTAL_MODELS = [ Person, Organization, Affiliation ].freeze
PORTAL_MODELS = [ Person, Organization, Affiliation, Payment, Workshop ].freeze

def index
authorize! :fm_archive
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/payments_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -252,7 +252,7 @@ def calculate_allocation_amount(payment, allocatable)
end

def edit_payment_params
params.require(:payment).permit(:person_id, :organization_id, :form_submission_id, :payer_sgid, :additional_designation_sgid)
params.require(:payment).permit(:person_id, :organization_id, :form_submission_id, :payer_sgid, :additional_designation_sgid, :filemaker_code)
end

def redirect_path_for(allocatable)
Expand Down
2 changes: 1 addition & 1 deletion app/controllers/workshops_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ def workshop_params
params.require(:workshop).permit(
:title, :featured, :published,
:full_name, :author_id, :windows_type_id, :workshop_idea_id, :author_credit_preference,
:month, :year,
:month, :year, :filemaker_code,
:publicly_visible,
:publicly_featured,

Expand Down
2 changes: 2 additions & 0 deletions app/helpers/filemaker_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ def filemaker_portal_link(record)
when Person then [ record.full_name, person_path(record) ]
when Organization then [ record.name, organization_path(record) ]
when Affiliation then [ "#{record.person&.full_name} at #{record.organization&.name}", edit_affiliation_path(record) ]
when Payment then [ [ dollars_from_cents(record.amount_cents), record.payer&.name ].compact.join(" from "), payment_path(record) ]
when Workshop then [ record.title, workshop_path(record) ]
end

link_to text, path, class: button_classes(:primary_outline, size: :sm)
Expand Down
2 changes: 2 additions & 0 deletions app/models/payment.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
class Payment < ApplicationRecord
include FilemakerCoded
filemaker_archived_in FmPayment
has_paper_trail
PAYER_TYPES = %w[Person Organization].freeze

Expand Down
1 change: 1 addition & 0 deletions app/models/resource.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ class Resource < ApplicationRecord
include Rails.application.routes.url_helpers
include ActionText::Attachable
include Mentionable
include FilemakerCoded

# Define rich text fields for mentions functionality
def self.mentionable_rich_text_fields
Expand Down
2 changes: 2 additions & 0 deletions app/models/workshop.rb
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ class Workshop < ApplicationRecord
credits_to_org
include Featureable, Publishable, RemoteSearchable, TagFilterable, Trendable, WindowsTypeFilterable, RichTextSearchable
include Communicable
include FilemakerCoded
filemaker_archived_in FmWorkshop
include PunctuationStrippable
include Rails.application.routes.url_helpers
include ActionText::Attachable
Expand Down
17 changes: 14 additions & 3 deletions app/views/payments/edit.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,20 @@
prompt: "Search people and organizations",
label: "Additional designation" %>

<%= f.input :form_submission_id,
input_html: { class: "bg-white" },
label: "Form Submission ID" %>
<div class="grid grid-cols-1 items-start gap-4 sm:grid-cols-2">
<div class="pt-3">
<%= f.input :form_submission_id,
input_html: { class: "bg-white" },
label: "Form Submission ID" %>
</div>

<div class="admin-only rounded-md bg-blue-100 px-3 pt-3">
<%= f.input :filemaker_code,
input_html: { class: "bg-white" },
label: "FileMaker code",
hint: filemaker_code_hint(f.object) %>
</div>
</div>
</div>
<div class="action-buttons mt-8 flex justify-center gap-3">
<%= link_to "Cancel", payment_path(@payment), class: button_classes(:secondary_outline) %>
Expand Down
9 changes: 9 additions & 0 deletions app/views/resources/_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,15 @@
input_html: { class: "w-24 rounded border-gray-300 px-2 py-1", min: 1, step: 1 },
label_html: { class: "font-medium mb-1 block" } %>
</div>

<% if allowed_to?(:manage?, @resource) %>
<div class="admin-only -mt-2 w-40 shrink-0 rounded bg-blue-100 px-2 pt-2">
<%= f.input :filemaker_code,
label: "FileMaker code",
input_html: { class: "w-full rounded border-gray-300" },
label_html: { class: "font-medium mb-1 block" } %>
</div>
<% end %>
</div>
</div>

Expand Down
4 changes: 4 additions & 0 deletions app/views/workshops/_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,10 @@
} %>
</div>
</div>
<%= f.input :filemaker_code,
label: "FileMaker code",
hint: filemaker_code_hint(f.object),
input_html: { class: "block w-full rounded-md border-gray-300 shadow-sm focus:ring-blue-500 focus:border-blue-500 sm:text-sm" } %>
</div>
<div class="admin-only bg-blue-100 p-3">
<%= render "shared/visibility_info", flags: %i[published featured publicly_visible publicly_featured] %>
Expand Down
12 changes: 12 additions & 0 deletions config/features.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4261,3 +4261,15 @@
the archive record links back to the affiliation.
pro_tips:
- "An affiliation's code is usually its program's FileMaker project ID (like P2775), and occasionally a personnel ID (like PS9999)."

- name: "FileMaker codes on payments, workshops, and resources"
area: content
display_status: admin_facing
released_on: 2026-10-12
pr_number: 2656
summary: >-
The payment, workshop, and resource editors now show each record's FileMaker
code so admins can see and fix it, the same way people and organizations do.
pro_tips:
- "Separate several codes with commas β€” a record can answer to more than one."
- "Each code links to its FileMaker archive record, and that record links back to the payment or workshop."
19 changes: 10 additions & 9 deletions docs/adr/0005-filemaker-code-archive-mapping.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ drive the archive pages' links.
| `people.filemaker_code` | `fm_rolodexes` | `ID`, e.g. `00006` | `filemaker_code_links` on person edit + admin details |
| `organizations.filemaker_code` | `fm_projects`, then `fm_organizations` | `ProjectID`, e.g. `P2775`; `OrgID`, e.g. `ORG07206` | `filemaker_code_links` on org form; `filemaker_project_links` / `filemaker_organization_links` on org edit |
| `affiliations.filemaker_code` | `fm_projects`, then `fm_personnels` | `ProjectID`, e.g. `P2775`; rarely `PrsnlRecID`, e.g. `PS9999` | `filemaker_code_links` on affiliation edit |
| `payments.filemaker_code` | `fm_payments` | `RecordID`, e.g. `D29186` | dev seed (no UI link yet) |
| `workshops.filemaker_code` | `fm_workshops` | `WorkshopID` | dev seed (no UI link yet) |
| `resources.filemaker_code` | `fm_quotations` | `RecordID`, e.g. `Q10215` | no UI link yet |
| `payments.filemaker_code` | `fm_payments` | `RecordID`, e.g. `D29186` | `filemaker_code_hint` on payment edit |
| `workshops.filemaker_code` | `fm_workshops` | `WorkshopID` | `filemaker_code_hint` on workshop form |
| `resources.filemaker_code` | none | `RecordID` of the source quotation, e.g. `Q10215` | resource form (editable, no archive link) |

- **Organization β†’ Project, or β†’ Organization.** An app `Organization` is usually one
art program site, which is a FileMaker *Project*. FileMaker's own Organization row is
Expand All @@ -47,11 +47,12 @@ drive the archive pages' links.
never its org. One affiliation carries a real `PS` Personnel id, so Personnel stays
as the second table. The 13 that differ from the org's code are worth a look: the
affiliation may sit on the wrong org, or the org may be missing that code.
- **Resource β†’ Quotation.** In production, 116 of 128 resource codes are quotation ids
(`Q…`), 115 of them on Leader spotlight resources and one on a Story; 115 of the 116
exist in `fm_quotations` (quotes and testimonials, with a `WorkShopLeader` field).
Form Letters are mail-merge templates, not resources. The dev seed still leaves
`resources.filemaker_code` blank.
- **Resource β†’ none.** In production, 116 of 128 resource codes are quotation ids
(`Q…`), 115 of them on Leader spotlight resources and one on a Story: each spotlight's
body is the text of the FileMaker quotation it was built from. A resource is still its
own portal record, not an archive row, and quotations belong with the portal's quotes,
so resource codes stay editable but don't link into the archive or back. The dev seed
leaves `resources.filemaker_code` blank.

## Archive id patterns

Expand Down Expand Up @@ -173,6 +174,6 @@ Production, 2026-10-12: 84 records (41 organizations, 16 affiliations, 14 worksh

## Consequences

A UI link for payments, workshops, or resources includes `FilemakerCoded` on the
A UI link for a new FileMaker-coded column includes `FilemakerCoded` on the
model and declares the archive table above (e.g. `filemaker_archived_in FmPersonnel`);
`filemaker_code_links` reads it from there.
21 changes: 21 additions & 0 deletions spec/requests/fm_archives_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,27 @@
expect(response.body).to include(ERB::Util.html_escape("#{affiliation.person.full_name} at #{affiliation.organization.name}"))
end

it "links an archived payment back to its portal payment" do
FmPayment.create!(fm_id: "D29186")
person = create(:person)
payment = create(:payment, person:, amount_cents: 15_000, filemaker_code: "D29186")

get fm_archive_path("D29186", table: "payments")

expect(response.body).to include(payment_path(payment))
expect(response.body).to include("$150 from #{ERB::Util.html_escape(person.name)}")
end

it "links an archived workshop back to its portal workshop" do
FmWorkshop.create!(fm_id: "W0123")
workshop = create(:workshop, filemaker_code: "W0123")

get fm_archive_path("W0123", table: "workshops")

expect(response.body).to include(workshop_path(workshop))
expect(response.body).to include(ERB::Util.html_escape(workshop.title))
end

it "omits the portal section when nothing carries the code" do
FmRolodex.create!(fm_id: "00006")

Expand Down
31 changes: 31 additions & 0 deletions spec/requests/payments_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -172,9 +172,40 @@
end
end

describe "GET /payments/:id/edit FileMaker code" do
it "renders the FileMaker code field" do
payment = create(:payment, person:)

get edit_payment_path(payment)

expect(response.body).to include("payment[filemaker_code]")
end

it "links each archived code to the FileMaker payments archive" do
FmPayment.create!(fm_id: "D29186", data: {})
payment = create(:payment, person:, filemaker_code: "D29186, D99999")

get edit_payment_path(payment)

expect(response.body).to include(fm_archive_path("D29186", table: "payments"))
expect(response.body).to include("No FileMaker archive record for D99999")
expect(response.body).not_to include(fm_archive_path("D99999", table: "payments"))
end
end

describe "PATCH /payments/:id" do
let(:organization) { create(:organization) }

it "updates the FileMaker code" do
payment = create(:payment, person:)

patch payment_path(payment), params: {
payment: { payer_sgid: person.to_sgid.to_s, filemaker_code: "D29186" }
}

expect(payment.reload.filemaker_code).to eq("D29186")
end

it "updates payer and designation via sgids" do
payment = create(:payment, person:, organization: nil)

Expand Down
23 changes: 23 additions & 0 deletions spec/requests/resources_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,22 @@
expect(response.body).to include('name="resource[publicly_visible]"')
expect(response.body).to include(VisibilityFlagsHelper::FLAG_DEFINITIONS[:hidden_from_search][:description])
end

it "renders the FileMaker code field" do
resource = Resource.create! valid_attributes
get edit_resource_url(resource)

expect(response.body).to include('name="resource[filemaker_code]"')
end

it "shows the FileMaker code without an archive link" do
FmQuotation.create!(fm_id: "Q10215", data: {})
resource = Resource.create! valid_attributes.merge(filemaker_code: "Q10215")
get edit_resource_url(resource)

expect(response.body).to include('value="Q10215"')
expect(response.body).not_to include("table=quotations")
end
end

describe "POST /create" do
Expand Down Expand Up @@ -200,6 +216,13 @@
expect(resource.title).to eq("Updated Resource Title")
end

it "updates the FileMaker code" do
resource = Resource.create! valid_attributes
patch resource_url(resource), params: { resource: new_attributes.merge(filemaker_code: "R42") }

expect(resource.reload.filemaker_code).to eq("R42")
end

it "redirects to the updated resource" do
resource = Resource.create! valid_attributes
patch resource_url(resource), params: { resource: new_attributes }
Expand Down
30 changes: 30 additions & 0 deletions spec/requests/workshops_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,36 @@
end
end

describe "FileMaker code" do
let(:admin) { create(:user, :admin) }
let(:workshop) { create(:workshop) }

before { sign_in admin }

it "renders the FileMaker code field on edit" do
get edit_workshop_url(workshop)

expect(response.body).to include("workshop[filemaker_code]")
end

it "links an archived code to the FileMaker workshops archive" do
FmWorkshop.create!(fm_id: "W123", data: {})
workshop.update!(filemaker_code: "W123")

get edit_workshop_url(workshop)

expect(response.body).to include(fm_archive_path("W123", table: "workshops"))
end

it "updates the FileMaker code" do
patch workshop_url(workshop), params: {
workshop: { title: workshop.title, category_ids: [ "" ], filemaker_code: "W123" }
}

expect(workshop.reload.filemaker_code).to eq("W123")
end
end

# --- STORIES THAT USE THIS WORKSHOP ----------------------------------------
describe "GET /show stories section" do
let(:user) { create(:user) }
Expand Down
Loading