feat(integrations): add Bit CRM integration with triggers and actions - #203
Merged
Merged
Conversation
New integration for Bit CRM (leads, contacts, companies, deals, products,
tags, notes, activities, invoices), fully in the free tier — no pro addon
required.
Backend:
- Triggers/BitCrm: BitCrmController (custom_form_submission, 51 wp_hook
entries), Hooks.php, Routes.php
- Actions/BitCrm: BitCrmActionHelper (31 write methods), Hooks.php (31
Hooks::filter, all 10,2), RecordApiHelper switch (31 cases), Routes.php
- Core/Util/AllTriggersName.php: BitCrm entry, isPro:false
Frontend:
- AllIntegrations/BitCrm: staticData (31 modules + field maps),
BitCrmCommonFunc, Authorization/FieldMap/IntegLayout/BitCrm/EditBitCrm jsx
- Registered in NewInteg/EditInteg/IntegInfo/SelectAction (is_pro:false)
- bitCrm.webp logo
Wiring verified: Free withPrefix('bitcrm_*') === Pro-side withFreePrefix
convention (same VAR_FREE_PREFIX), 31 filters/handlers/apply-calls all
match at 2 args, 0 orphan field keys.
…BitCrm in flow UI BitCrmController used a non-existent bit_integrations_get_class() helper in every handler, causing a fatal error on any trigger. Replaced with direct Helper/Flow imports (matching BitSocialController), and moved prepareFetchFormatFields() formatting into flowExecute() so handlers just pass raw data. Also add BitCrm to the featured integrations list in SelectAction/SelectTrigger.
Replace generic $arg1/$arg2/$arg3 with $lead/$tag/$leadId/$stage/ $newStatus/$oldStatus etc. across all 51 trigger handlers, matching bit-pi's original BitCrmTrigger.php naming.
bitCrmFields was stored on conf only when the action select changed (handleMainAction). Editing a saved integration restores mainAction and field_map from the server but never bitCrmFields, so requiredFlds/ nonRequiredFlds resolved to [] and the field-select dropdown rendered with zero options. Derive bitCrmFields from bitCrmStaticData[mainAction] on every render instead of caching it in conf, and pass it into BitCrmFieldMap as a prop. Fixes both Create and Edit.
…refresh field-map UI
… helpers, return record data on success
- triggers: 17 created/updated handlers used empty(${arg1}) (variable-
variable → fatal on every fire, and surfaced errors on normal CRM saves);
replace with each handler's real param ($contact, $deal, …)
- convert_lead: convertToDeals() was called with no argument
(ArgumentCountError → convert always rolled back); pass the inserted
contacts from convertToContacts()
- update actions: drop the Tags dropdown — the CRM update() ignores tags
and the payload never forwarded them (silently dead)
- create_activity: omit due_date when empty (was ''), which MySQL strict
mode rejects for the nullable datetime column
- create_invoice: drop currency from the required list (the CRM ignores
invoice currency, so requiring it was savable-but-broken)
- remove the now-unused refresh_bitcrm_users route + refreshUsers() (owner
and assigned_to became email fields)
|
Caution The consumer version of Gemini Code Assist on GitHub has been sunset. All code review activity has officially ceased. |
🔍 WordPress Plugin Check Report
📊 Report
|
| 📍 Line | 🔖 Check | 💬 Message |
|---|---|---|
0 |
mismatched_plugin_name | Plugin name "Bit integrations - Form Integration, Webhook, Spreadsheets, CRM, LMS & Email Automation" is different from the name declared in plugin header "Bit Integrations". |
🤖 Generated by WordPress Plugin Check Action • Learn more about Plugin Check
Bit-Pi grew 23 write actions that never made it across: update/delete for tags and notes, the full task, meeting and call set, invoice update, status and delete, and the four client portal actions. Port them all, keeping the free-tier architecture BitCrm already uses (RecordApiHelper switch calling BitCrmActionHelper directly, is_pro false), rather than routing them through a Pro hook like most integrations do. Activity actions verify the record type before touching it, so Delete Task refuses a meeting id. Updates are partial: an empty field keeps the current value. Invoice writes honour Bit CRM's rules that paid invoices are locked and status moves must be legal transitions, and delete soft-deletes into the trash bin. Target identifiers stay in the field map so they can be mapped from trigger data; enums and capability lists are selects, and is_shared is a Utility. BREAKING CHANGE: create_activity is retired in favour of create_task, create_meeting and create_call. Flows still using it will log Invalid action and must be rebuilt.
Bit CRM fires one hook for tasks, meetings and calls alike, because they share the activities table and differ only by type. Dispatch both the generic event and a type-specific one, so a flow can subscribe at whichever granularity it needs. The generic events still fire, so saved flows keep working. The typed ids are flow keys, not WordPress hooks — no such hook exists. Activity Deleted stays generic: Bit CRM hard-deletes the row before firing and passes only the id, so the type can no longer be resolved. Attachments and links were fired by Bit CRM but listened to by nobody; wire up their five hooks too.
Selects are stored flat on conf and switching action only reset mainAction and field_map, so values the previous action wrote stayed behind. Several keys share a Bit CRM field — productStatus, activityStatus and invoiceStatus all write status, as dealType/productType write type — and the last one merged won. Configuring Update Task Status and then switching to Create Product created the product with status completed instead of active. Fix both ends: the layout now drops every select and utility key the new action does not render, and the merge refuses a key whose action list does not contain the chosen action, so a conf saved by the old code cannot corrupt a record either. Also report unresolved owner and assignee emails by name, instead of letting them surface downstream as a bare "assigned_to is required", and note on Update Invoice that existing line items keep their original tax mode.
Bit CRM Pro now fires client_portal_access_granted and client_portal_access_revoked, each carrying the contact and the portal email. Revoking works from a WordPress user, so its contact can be null when the portal account has no matching CRM contact; the email is the only value both events always carry, and the payload is the contact's fields plus that email. These live in Bit CRM Pro, so they stay silent on a Free-only install.
Create Tag called BitApps\Crm\Services\TagService::store() statically and the client portal actions used BitApps\Crm\Services\ClientPortalService, which lives in Bit CRM Pro as BitApps\CrmPro\Services\ClientPortalService, so both fataled. Bit CRM also validates a few fields as required on update, which the field maps offered as optional, and its product update request cannot exclude the product from its own unique-code check when the payload is an array, so Update Product always failed. Call the tag service on an instance, use the Pro portal class, require the fields Bit CRM demands on update, write the product directly with our own uniqueness check, and flatten the field-keyed validation errors Bit CRM returns so the message is readable instead of "Array".
Activity Created, Activity Updated and Activity Status Updated duplicated the task, meeting and call triggers that cover the same Bit CRM hooks, so remove them and dispatch only the type-specific event. Activity Deleted stays, because Bit CRM passes the id alone and the type can no longer be resolved.
Replace the bespoke Bit CRM authorization step with the shared Connections/Authorization component using the WP_PLUGIN_CHECK auth type. Verification now goes through connections/verify-plugin-activation and PluginCheck::evaluate against the BitApps\Crm\Config class, so the dedicated bitcrm_authorize route and controller method are no longer needed. No connection row is created for wp_plugin_check integrations, so existing BitCrm flows are unaffected.
"Tags Attached To Leads" sat next to "Tag Attached To Lead" with only the plural to tell them apart, so say (Bulk) outright as Bit Pi does.
Create/Update Task, Meeting and Call asked for the record as a "Record Id" in the field map, so a flow had to carry the raw id. Fetch the records of the module already selected next to it and let the user pick one, the way Bit Pi does. The list resets when the module changes, and the refresh button says what is missing until a module is chosen.
Create/Update Task, Meeting and Call asked for the assignee as an email that was looked up on every run, so a typo or a former user silently lost the assignment. Fetch the Bit CRM users and let the user pick one, as with the record above it. The email lookup stays for the owner fields, which still take an email.
Lead, contact, company and deal actions collected the owner as an email that was looked up on every run, so a typo or a former user silently left the record unowned. Fetch the Bit CRM users and let the user pick one, as the record and assignee pickers do. The email lookup stays for flows saved before this.
Create Note asked for the record as a "Record Id" in the field map, like the activity actions did. Fetch the records of the module already selected next to it and let the user pick one.
Create Invoice read a currency it never required, storing a null-currency invoice its line items cannot be priced in. Convert Lead never marked the lead converted nor fired its trigger, so the same lead could be converted again and again, each run creating another contact, company and deal. Attaching a tag that was already there still reported success. Update Task/Meeting/Call let the module move without the record, pointing the activity at whatever held that id in the new table. Update Deal Stage wrote any string into the column. The field map renders its required rows by position, so a config saved before a field became required showed one Bit CRM field while holding another; re-key those rows against the current required list when the action loads. A missing dependent list now blames the field it hangs off rather than itself.
Keeps the notes explaining Bit CRM behaviour a reader cannot infer from the call, the translator comments, and the tags carrying types the signature lacks.
… by type Bit CRM now runs its update requests through makeOnlyIdRequired(), so every field but the id is optional and an unmapped one keeps its stored value. Drop the matching checks from the update actions, mark those field maps optional again, and let Update Tag keep the tag's current title. Update Deal's stage and contact pickers are optional too, though creating a deal still needs both. Bit CRM also passes the whole activity to bit_crm/activity_deleted now, so the type is known: replace the generic Activity Deleted trigger with a task, meeting and call variant, as the created, updated and status events already do.
…date Let a partial record change through instead of rejecting it, and tidy the docblock alignment around modifyActivity and resolveInvoice.
abdul-kaioum
approved these changes
Jul 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Description
Adds a complete Bit CRM integration (free tier) to Bit Integrations. This includes 47 trigger hooks across 8 CRM entities (Leads, Contacts, Companies, Deals, Products, Tags, Notes, Activities, Invoices) and full CRUD actions for the same entities, enabling two-way automation between Bit CRM and any connected platform.
Motivation & Context
Bit CRM users need the ability to automate workflows based on CRM events and push data into the CRM from external sources. This integration bridges that gap, providing both trigger events and action endpoints for the entire Bit CRM entity model.
Type of Change
Key Changes
Triggers (backend/Triggers/BitCrm/)
do_actionhooks covering:normalizeDataandcsvListhelpers for consistent data formattingsuccessresponse wrapper so Record API returns record data on successActions (backend/Actions/BitCrm/)
BitCrmActionHelperwith centralized entity creation/update logic and tag handlingmergeConfiguredValuesfor injecting fixed-values from utility settingsnormalizehelper for consistent option formattingFrontend (frontend/src/components/AllIntegrations/BitCrm/)
EditBitCrm.jsxwebhookIntegrations.jsNewInteg.jsx,EditInteg.jsx,IntegInfo.jsxBug Fixes
bit_integrations_get_classcalls during flow execution*_idandnew_tagsfields incorrectly extracted as systemValues during entity creationRefactors
getAllTasks/taskstogetAllEvents/eventsfor clarityHooks.php, consolidated todo_actiononlyBitCrmActionHelperandRecordApiHelperfor consistencyChecklist
Changelog
do_actionfor reliable event delivery; dynamic dropdowns auto-populate entity lists and configuration options.