diff --git a/app/services/customer_sync_service.rb b/app/services/customer_sync_service.rb index 54c1dfb..873b346 100644 --- a/app/services/customer_sync_service.rb +++ b/app/services/customer_sync_service.rb @@ -17,16 +17,13 @@ class CustomerSyncService < SyncServiceBase Customer end - # Determine if the remote entity should be deleted locally (e.g. if it's marked inactive in QBO) - def destroy_remote?(remote) + # Determine if the local entity should be deleted (e.g. if it's marked inactive in QBO) + def destroy_local?(remote) !remote.active? end - # Map relevant attributes from the QBO Customer to the local Customer model - def process_attributes(local, remote) - local.name = remote.display_name - local.phone_number = remote.primary_phone&.free_form_number&.gsub(/\D/, '') - local.mobile_phone_number = remote.mobile_phone&.free_form_number&.gsub(/\D/, '') - end + map_attribute :name, ->(remote) { remote.display_name } + map_attribute :phone_number, ->(remote) { remote.primary_phone&.free_form_number&.gsub(/\D/, '') } + map_attribute :mobile_phone_number, ->(remote) { remote.mobile_phone&.free_form_number&.gsub(/\D/, '') } end \ No newline at end of file diff --git a/app/services/employee_sync_service.rb b/app/services/employee_sync_service.rb index e8b99fa..441d500 100644 --- a/app/services/employee_sync_service.rb +++ b/app/services/employee_sync_service.rb @@ -17,14 +17,11 @@ class EmployeeSyncService < SyncServiceBase Employee end - # Determine if the remote entity should be deleted locally (e.g. if it's marked inactive in QBO) - def destroy_remote?(remote) + # Determine if the local entity should be deleted (e.g. if it's marked inactive in QBO) + def destroy_local?(remote) !remote.active? end - # Map relevant attributes from the QBO Employee to the local Employee model - def process_attributes(local, remote) - local.name = remote.display_name - end + map_attribute :name, ->(remote) { remote.display_name } end \ No newline at end of file diff --git a/app/services/estimate_sync_service.rb b/app/services/estimate_sync_service.rb index 269a88a..7251475 100644 --- a/app/services/estimate_sync_service.rb +++ b/app/services/estimate_sync_service.rb @@ -17,11 +17,6 @@ class EstimateSyncService < SyncServiceBase Estimate end - # Map relevant attributes from the QBO Estimate to the local Estimate model - def process_attributes(local, remote) - local.doc_number = remote.doc_number - local.txn_date = remote.txn_date - local.customer = Customer.find_by(id: remote.customer_ref&.value) - end + map_attribute :customer, ->(remote) { Customer.find_by(id: remote.customer_ref&.value) } end \ No newline at end of file diff --git a/app/services/invoice_sync_service.rb b/app/services/invoice_sync_service.rb index 5f1157d..1ffafad 100644 --- a/app/services/invoice_sync_service.rb +++ b/app/services/invoice_sync_service.rb @@ -16,20 +16,13 @@ class InvoiceSyncService < SyncServiceBase def self.model_class Invoice end - - # Map relevant attributes from the QBO Invoice to the local Invoice model - def process_attributes(local, remote) - local.doc_number = remote.doc_number - local.txn_date = remote.txn_date - local.due_date = remote.due_date - local.total_amount = remote.total - local.balance = remote.balance - local.qbo_updated_at = remote.meta_data&.last_updated_time - local.customer = Customer.find_by(id: remote.customer_ref&.value) - end # Attach QBO Invoices to the local Issues def attach_documents(local, remote) InvoiceAttachmentService.new(local, remote).attach end + + map_attribute :customer, ->(remote) { Customer.find_by(id: remote.customer_ref&.value) } + map_attribute :total_amount, ->(remote) { remote.total } + map_attribute :qbo_updated_at, ->(remote) { remote.meta_data&.last_updated_time } end \ No newline at end of file diff --git a/app/services/sync_service_base.rb b/app/services/sync_service_base.rb index 645814b..81ce737 100644 --- a/app/services/sync_service_base.rb +++ b/app/services/sync_service_base.rb @@ -57,6 +57,10 @@ class SyncServiceBase private + def attach_documents(local, remote) + # Override in subclasses if the entity has attachments (e.g. Invoice) + end + # Builds a QBO query for retrieving entities def build_query(full_sync) if full_sync @@ -72,13 +76,28 @@ class SyncServiceBase end end - def attach_documents(local, remote) - # Override in subclasses if the entity has attachments (e.g. Invoice) + # Determine if a remote entity should be deleted locally (e.g. if it's marked inactive in QBO) + def destroy_local?(remote) + false end - # Determine if a remote entity should be deleted locally (e.g. if it's marked inactive in QBO) - def destroy_remote?(remote) - false + def extract_value(remote, remote_attr) + case remote_attr + when Proc + remote_attr.call(remote) + else + remote.public_send(remote_attr) + end + end + + class << self + def map_attribute(local, remote = nil, &block) + attribute_map[local] = block || remote + end + + def attribute_map + @attribute_map ||= {} + end end # Log messages with the entity type for better traceability @@ -90,7 +109,7 @@ class SyncServiceBase def persist(remote) local = @entity.find_or_initialize_by(id: remote.id) - if destroy_remote?(remote) + if destroy_local?(remote) if local.persisted? local.destroy log "Deleted #{@entity.name} #{remote.id}" @@ -111,9 +130,13 @@ class SyncServiceBase log "Failed to sync #{@entity.name} #{remote.id}: #{e.message}" end - # This method should be implemented in subclasses to map remote attributes to local model + # Maps remote attributes to local model def process_attributes(local, remote) - raise NotImplementedError, "Subclasses must implement process_attributes" + log "Processing #{@entity} ##{remote.id}" + self.class.attribute_map.each do |local_attr, remote_attr| + value = extract_value(remote, remote_attr) + local.public_send("#{local_attr}=", value) + end end # Dynamically get the Quickbooks Service Class