Inter-organizational Contact and Time Transfers
https://github.com/user-attachments/assets/fbd567db-5463-4829-8837-85bbbe75cbcd
This pull request introduces contact and time transfers between organizations, fixes how movements derived from these flows are displayed, and adds clear messaging for users. The goal is to allow members of different time banks to collaborate without exposing private data while keeping hour accounting balanced on both sides. The changes span controllers, models, views, routes, locales, and a database migration.
Organization-to-Organization Contact
-
New
contactaction in PostsController: Allows a member of another organization to request the offerer's email; the request is authorized only if the requester is active in their bank and the offer is not from their own organization. -
"Request contact" button added to offers and inquiries views when the visitor meets the above conditions.
-
Help text: Includes a banner explaining that contact data is not visible and guides the user to use the button.
-
Reusable route: The action is declared as
post :contact, on: :memberand added via concern to offers, inquiries, and posts, maintaining consistency with existing convention. -
Purpose: Facilitate collaboration between organizations without exposing emails or phone numbers and without breaking the current publication flow.
Cross-Bank Transfers
-
TransferFactory
- Adds
cross_bankflag and stores metadata (source_organization_id,destination_organization_id,final_destination_user_id) in meta. - Determines the correct destination account based on whether the transfer is cross-bank or internal.
- Adds
-
Transfer
- New
make_cross_bank_movementsmethod that generates the 6 movements needed for the User A → Bank A → Bank B → User B flow, keeping the balance at zero in each organization. - Helper
related_account_forused by the movements view to show the real counterpart in each entry.
- New
-
TransfersController
- Auxiliary action
create_cross_bank_transferthat encapsulates the logic and simplifies thecreateaction. - The
transfers/newview displays an informative banner when@cross_bankis true.
- Auxiliary action
-
Migration
20250418100031_add_cross_bank_fields_to_transfers.rb- Adds two columns (
is_cross_bankandmeta) and an index to the transfers table to manage inter-bank transfers and facilitate analytical reports. is_cross_bank(boolean, default: false) indicates if the transfer involves multiple organizations.- The
metafield (jsonb) stores additional details such as organization and end-user identifiers. - The
index_transfers_on_is_cross_bankindex optimizes frequent queries.
- Adds two columns (
-
Purpose: Explicitly record inter-bank transfers, provide transparency to users, and maintain correct accounting on both sides.
Movement View Improvements
-
_movements.html.erbnow invokesrelated_account_forwhen the transfer is cross-bank, so each row shows the account the user expects to see (not the intermediate step). -
There are now two types of transfers:
- Internal: between two members of the same time bank.
- Cross-bank: between members of different banks (the new feature in this PR).
-
Previously, the movements view showed the counterpart using
mv.other_side.account-
This works well for internal transfers, as there are only two movements (debit-credit) and the "other side" is always the receiving user.
-
For cross-bank transfers, we generate 6 movements; there
other_sideis insufficient, which is why we addedTransfer#related_account_forand use it only ifmv.transfer.is_cross_bankis true. -
If the transfer is internal → the view continues to show
other_side.account(historical logic). -
If it is cross-bank → the view uses
related_account_forso that each entry shows the account actually involved in that step of the chain.
-
-
Purpose: Eliminate confusion in the movements table and promote transparency in transfers between organizations.
Internationalization and UX
-
New keys in
en.ymlandes.ymlfor buttons, banners, and flash messages. -
Minor style adjustments (
btn-primary me-2,bg-info) to maintain consistency with Bootstrap. -
Purpose: Provide clear messages in both official languages and maintain the project's visual style.
Tests
- Finally, several tests are added to ensure the functionality works as expected.