Class: Edi::Commercehub::RemitMessageProcessor
- Inherits:
-
BaseEdiService
- Object
- BaseService
- BaseEdiService
- Edi::Commercehub::RemitMessageProcessor
- Defined in:
- app/services/edi/commercehub/remit_message_processor.rb
Defined Under Namespace
Classes: BatchProcessResult, Result
Constant Summary collapse
- PAYMENT_METHOD_MAP =
{ ACH: 'ACH' }
Constants included from AddressAbbreviator
AddressAbbreviator::MAX_LENGTH
Instance Attribute Summary
Attributes inherited from BaseEdiService
Instance Method Summary collapse
- #customer(segment = nil) ⇒ Object
-
#process(edi_logs = nil) ⇒ Object
Picks up the edi communication log in the queue ready to process.
-
#process_receipt(remittance_advice_message_xml, batch_number) ⇒ Object
Process a single receipt.
-
#process_receipts(remittance_advices_data) ⇒ Object
Iterates through the batch receipt/remittance advice data.
Methods inherited from BaseEdiService
#duplicate_po_already_notified?, #initialize, #mark_duplicate_po_as_notified, #report_order_creation_issues, #safe_process_edi_communication_log
Methods included from AddressAbbreviator
#abbreviate_street, #collect_street_originals, #record_address_abbreviation_notes
Methods inherited from BaseService
#initialize, #log_debug, #log_error, #log_info, #log_warning, #logger, #options, #tagged_logger
Constructor Details
This class inherits a constructor from Edi::BaseEdiService
Instance Method Details
#customer(segment = nil) ⇒ Object
16 17 18 |
# File 'app/services/edi/commercehub/remit_message_processor.rb', line 16 def customer(segment = nil) orchestrator.customer(segment) end |
#process(edi_logs = nil) ⇒ Object
Picks up the edi communication log in the queue ready to process
21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 |
# File 'app/services/edi/commercehub/remit_message_processor.rb', line 21 def process(edi_logs = nil) edi_logs ||= EdiCommunicationLog.requiring_processing.where(partner: orchestrator.partner, category: 'remittance_advice') edi_logs = [edi_logs].flatten # This way you can pass a single edi communication log # Wrap in one big transaction batch_process_results = [] edi_logs.each do |edi_log| EdiCommunicationLog.transaction do if orchestrator. # Process the receipts in the batch batch_process_result = process_receipts(edi_log.data) # Record some meta data in file info edi_log.file_info ||= {} edi_log.file_info[:receipts_in_batch] = batch_process_result.receipts_in_batch # Mark our edi log as processed, we need some error handling at one point # Associate the created receipts batch_process_result.receipts_created.each do |receipt| edi_log.edi_documents.create(receipt: receipt) end batch_process_results << batch_process_result end edi_log.complete! end # End transaction rescue StandardError => e edi_log.notes = "#{e} at #{e&.backtrace_locations&.first}" edi_log.error ErrorReporting.error(e) # End transaction end # End edi log iteration Result.new(batch_process_results: batch_process_results) end |
#process_receipt(remittance_advice_message_xml, batch_number) ⇒ Object
Process a single receipt
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 |
# File 'app/services/edi/commercehub/remit_message_processor.rb', line 68 def process_receipt(, batch_number) cust = orchestrator.partner == :commercehub_lowes_ca ? customer('lowes') : customer edi_transaction_id = .xpath('hubMessageId').text # unique EDI transaction ID for this receipt category = PAYMENT_METHOD_MAP[.xpath('paymentMethod').text] || 'ACH' # this is always ACH based on looking at all the existing EDI data, without any exceptions amount = .xpath('balanceDue').text.to_d payment_date = .xpath('paymentDate').text reference = "EDI #{category} #{cust.name} #{payment_date}" # following existing conventions of for e.g. 'WIRE HOME DEPOT 101921' but adding 'EDI' and using category (which will likely be 'ACH' and is more accurate than 'WIRE') currency = cust.store.currency company_id = cust.store.company_id bank_account_id = cust.store.company.cc_bank_account.id remark = "Payment # #{batch_number}" # also following existing conventions of for e.g. 'Payment # 16059629173' where that number is in fact the batch number receipt = cust.receipts.build( edi_transaction_id: edi_transaction_id, category: category, amount: amount, reference: reference, currency: currency, company_id: company_id, bank_account_id: bank_account_id, remark: remark, gl_date: payment_date.to_date, receipt_date: payment_date.to_date ) should_hold_for_review = false # this is set to false so the receipy will fully apply if everything is simply invoice remittances, we will set it to true i.e. hold the receipt in 'partially applied' if there are any negative remittances to manually research invoice_discounts_to_apply = 0.0 .xpath('//remittanceAdviceItem').each do |remittance_advice_item_xml| invoice_id = nil detail_category = nil invoice_ref = remittance_advice_item_xml.xpath('refInvoiceNumber').text invoice_ref = 'IN' + invoice_ref if orchestrator.partner == :commercehub_thd_us && invoice_ref.present? && invoice_ref[0] == ('V') po_number = orchestrator.partner == :commercehub_thd_us ? remittance_advice_item_xml.xpath('refOrderNumber').text : nil business_unit_id = nil ledger_company_account_id = nil ledger_detail_project_id = nil remark = nil if (invoice_ref.present? && invoice = Invoice.find_by(reference_number: invoice_ref)) || (invoice_ref.present? && po_number.present? && invoice = Invoice.find_by(po_number: po_number.to_i)) if receipt_details = ReceiptDetail.find_by(invoice_id: invoice.id) should_hold_for_review = true detail_category = 'GL Account' if orchestrator.partner == :commercehub_thd_us amount = remittance_advice_item_xml.xpath('itemBalanceDue').text.to_d else invoice_id = invoice.id detail_category = 'Invoice' amount = remittance_advice_item_xml.xpath('refInvoiceAmount').text.to_d invoice_discounts_to_apply += remittance_advice_item_xml.xpath('refInvoiceDiscAmount').text.to_d end business_unit_id = receipt.company.business_units.where("name ILIKE '%Sales%'").first&.id || receipt.company.business_units.where(number: [10_170, 20_170])&.id remark = "Invoice #{remittance_advice_item_xml.xpath('refInvoiceNumber').text} already paid in Receipt #{receipt_details.receipt.id}" if amount > 0.0 || amount < 0.0 ledger_company_account_id = receipt.company.ledger_company_accounts.where("name ILIKE '%Sales Expenses Other%'").first&.id ledger_detail_project_id = LedgerDetailProject.where("description ILIKE '%CN#{cust.id}%'").first&.id else invoice_id = invoice.id detail_category = 'Invoice' # this is how they establish this kind of detail for Invoice payment amount = remittance_advice_item_xml.xpath('refInvoiceAmount').text.to_d invoice_discounts_to_apply += remittance_advice_item_xml.xpath('refInvoiceDiscAmount').text.to_d end else # does not correspond to one of our invoices, needs manual research should_hold_for_review = true # this is likely a negative remittance to manually research, but could also be a one off invoice not in Heatwave detail_category = 'GL Account' # this is how they establish this kind of detail for credits TBD amount = if orchestrator.partner == :commercehub_thd_us remittance_advice_item_xml.xpath('itemBalanceDue').text.to_d # this will be negative else remittance_advice_item_xml.xpath('refInvoiceAmount').text.to_d end business_unit_id = receipt.company.business_units.where("name ILIKE '%Sales%'").first&.id || receipt.company.business_units.where(number: [10_170, 20_170])&.id # here we use the business unit associated with these i.e. the Sales & Affiliations (USA or Canada) unit if amount > 0.0 || amount < 0.0 # this is a positive remittance, i.e. not due to a return see EDI Communication Log ID: 582544 and Receipt ID: 105505 for an example of an unlinked credit, maybe from a dispute in our favor remark = if invoice_ref.present? "Unlinked Invoice #{remittance_advice_item_xml.xpath('refInvoiceNumber').text}, date: #{remittance_advice_item_xml.xpath('refInvoiceDate').text}, Order #{remittance_advice_item_xml.xpath('refOrderNumber').text}" else "Adjustment to Invoice #{remittance_advice_item_xml.xpath('refInvoiceAdjNumber').text}, date: #{remittance_advice_item_xml.xpath('refInvoiceAdjDate').text}" end end ledger_company_account_id = receipt.company.ledger_company_accounts.where("name ILIKE '%Sales Expenses Other%'").first&.id # here we use the ledger company account associated with these i.e. the Sales Expenses Other (USA or Canada) company account ledger_detail_project_id = LedgerDetailProject.where("description ILIKE '%CN#{cust.id}%'").first&.id # here we use the ledger detail project associated with the customer i.e. description contains CNXXXXXX where XXXXXX is the customer ID end receipt.receipt_details.build( invoice_id: invoice_id, category: detail_category, amount: amount, business_unit_id: business_unit_id, ledger_company_account_id: ledger_company_account_id, ledger_detail_project_id: ledger_detail_project_id, remark: remark, gl_date: payment_date.to_date ) end if invoice_discounts_to_apply > 0.0 receipt.receipt_details.build( category: 'GL Account', # this is how they establish this kind of detail for discounts amount: -1.0 * invoice_discounts_to_apply, business_unit_id: receipt.company.business_units.where("name ILIKE '%Administration%'").first&.id || receipt.company.business_units.where(number: [10_100, 20_100])&.id, # here we use the business unit associated with these i.e. the Administration (USA or Canada) unit ledger_company_account_id: receipt.company.ledger_company_accounts.where("name ILIKE '%Cash Discounts%'").first&.id, # here we use the ledger company account associated with these i.e. the Cash Discounts (USA or Canada) company account gl_date: payment_date.to_date ) end receipt.hold_for_review = should_hold_for_review receipt.save! receipt end |
#process_receipts(remittance_advices_data) ⇒ Object
Iterates through the batch receipt/remittance advice data
53 54 55 56 57 58 59 60 61 62 63 64 65 |
# File 'app/services/edi/commercehub/remit_message_processor.rb', line 53 def process_receipts(remittance_advices_data) xml_doc = Nokogiri::XML(remittance_advices_data) receipts_created = [] batch_number = xml_doc.xpath('//RemittanceAdvices/@messagebatch-id').text receipts_in_batch = 0 xml_doc.xpath('//RemittanceAdviceMessage').each do || receipts_in_batch += 1 receipts_created << process_receipt(, batch_number) if Receipt.where(edi_transaction_id: .xpath('hubMessageId').text).count.zero? # only process these once end BatchProcessResult.new(batch_number: batch_number, receipts_created: receipts_created, receipts_in_batch: receipts_in_batch) end |