Module: ReceiptSpreadsheetIo
- Extended by:
- ActiveSupport::Concern
- Included in:
- Receipt
- Defined in:
- app/models/concerns/receipt_spreadsheet_io.rb
Overview
Bulk receipt creation and editing from uploaded XLSX spreadsheets:
row parsing (via Roo), pre-save validation reports, and the create/
edit importers driven by the receipts importer UI and the
CreateReceiptsWorker / EditReceiptsWorker background jobs.
Class Method Summary collapse
- .create_receipts_from_xlsx(xlsx_file) {|1, total_steps, 'Reading Excel file'| ... } ⇒ Object
- .edit_receipts_from_xlsx(xlsx_file) {|1, total_steps, 'Reading Excel file'| ... } ⇒ Object
-
.validate_data_create_receipts_from_xlsx(xlsx_file) ⇒ Array<String>
Pre-flight a "create receipts" XLSX upload: parse each row, resolve company/bank account/payor/invoice/credit-memo/GL-account, and collect a list of human-readable error strings without persisting anything.
-
.validate_data_edit_receipts_from_xlsx(xlsx_file) ⇒ Array<String>
Pre-flight an "edit receipts" XLSX upload: each row references an existing receipt by id and adds new ReceiptDetails under
new_gl_date.
Class Method Details
.create_receipts_from_xlsx(xlsx_file) {|1, total_steps, 'Reading Excel file'| ... } ⇒ Object
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 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 |
# File 'app/models/concerns/receipt_spreadsheet_io.rb', line 141 def create_receipts_from_xlsx(xlsx_file) report = { errors: [], successes: [], new_receipt_ids: [] } new_receipt = nil new_receipts = [] total_steps = 5 require 'roo' yield(1, total_steps, 'Reading Excel file') if block_given? sleep 2 receipts_xlsx = Roo::Spreadsheet.open(xlsx_file.to_file.to_s) yield(2, total_steps, 'Building receipts') if block_given? sleep 4 yield(3, total_steps, 'Adding receipt details') if block_given? sleep 4 receipts_xlsx.parse(headers: true, clean: true).each_with_index do |row, index| next unless index >= 1 if row.map { |_k, v| v }.uniq.compact.present? # get the components of receipt if row['company_id'].present? company = Company.find(row['company_id']) bank_account = company.cc_bank_account || LedgerAccount.find_by(number: row['bank_account'].to_i).ledger_company_accounts.first.bank_account customer = Party.find_by(id: row['payor_id'].to_i) receipt_date = row['receipt_date'] new_receipt = Receipt.new(company: company, bank_account: bank_account, customer: customer, category: row['receipt_category'], reference: row['reference'], currency: row['currency'], amount: row['receipt_amount'], gl_date: row['gl_date'], receipt_date: receipt_date, card_type: row['card_type'], auth_code: row['authorization_code']) end # Add all the necesary receipt details to link to its receipt if new_receipt.present? && row['receipt_detail_category'].present? case row['receipt_detail_category'] when 'Invoice' invoice = Invoice.find_by(reference_number: row['invoice_credit_memo_number']) write_off = row['write_off_amount'].present? ? row['write_off_amount'].to_f : 0 new_receipt.receipt_details << ReceiptDetail.new(category: row['receipt_detail_category'], invoice: invoice, gl_date: receipt_date, amount: row['receipt_detail_amount'].to_f, write_off: write_off, write_off_code: row['write_off_code'], remark: row['receipt_detail_remark']) when 'Credit Memo' credit_memo = CreditMemo.find_by(reference_number: row['invoice_credit_memo_number']) new_receipt.receipt_details << ReceiptDetail.new(category: row['receipt_detail_category'], credit_memo: credit_memo, gl_date: receipt_date, amount: row['receipt_detail_amount'], remark: row['receipt_detail_remark']) when 'GL Account' new_receipt.receipt_details << ReceiptDetail.new(category: row['receipt_detail_category'], amount: row['receipt_detail_amount'], ledger_company_account: LedgerCompanyAccount.joins(:company, :ledger_detail_account).where(companies: { number: row['gl_company'] }, ledger_accounts: { number: row['gl_account'] }).first, business_unit: BusinessUnit.where(number: row['business_unit']).first, ledger_detail_project: LedgerDetailProject.where(project_number: row['project']).first, remark: row['receipt_detail_remark']) end end end if new_receipt.present? && (row.map { |_k, v| v }.uniq.compact.empty? || receipts_xlsx.last_row == (index + 1)) new_receipts << new_receipt unless new_receipt.nil? new_receipt = nil end end yield(4, total_steps, 'Recording receipts in the database') if block_given? begin Receipt.transaction do new_receipts.each do |new_receipt| new_receipt.save! report[:new_receipt_ids] << new_receipt.id msg = "#{Time.current}: Created new receipt id: #{new_receipt.id}" logger.info msg report[:successes] << msg end msg = 'Receipts have been created successfully.' report[:successes] << msg end rescue StandardError => e msg = "Unable to create receipt, message : #{e}." logger.error msg report[:errors] << msg end yield(5, total_steps, 'Finishing...') if block_given? sleep 3 report end |
.edit_receipts_from_xlsx(xlsx_file) {|1, total_steps, 'Reading Excel file'| ... } ⇒ Object
322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 |
# File 'app/models/concerns/receipt_spreadsheet_io.rb', line 322 def edit_receipts_from_xlsx(xlsx_file) report = { errors: [], successes: [], edited_receipt_ids: [] } receipt = nil receipts = [] total_steps = 4 require 'roo' yield(1, total_steps, 'Reading Excel file') if block_given? sleep 2 receipts_xlsx = Roo::Spreadsheet.open(xlsx_file.to_file.to_s) yield(2, total_steps, 'Adding receipt details') if block_given? sleep 4 receipts_xlsx.parse(headers: true, clean: true).each_with_index do |row, index| next unless index >= 1 if row.map { |_k, v| v }.uniq.compact.present? # update data of existing receipt if row['receipt_number'].present? receipt = Receipt.find_by(id: row['receipt_number']) next if receipt.nil? remark = row['receipt_remark'] || receipt.remark receipt.remark = remark receipt.new_gl_date = row['new_gl_date'] end # add new receipt details to existing receipt if receipt.present? && row['receipt_detail_category'].present? if row['receipt_detail_category'] == 'Invoice' && row['invoice_credit_memo_number'].present? invoice = Invoice.find_by(reference_number: row['invoice_credit_memo_number']) write_off = row['write_off_amount'].present? ? row['write_off_amount'].to_f : 0 receipt.receipt_details << ReceiptDetail.new(category: row['receipt_detail_category'], invoice: invoice, gl_date: receipt.new_gl_date, amount: row['receipt_detail_amount'].to_f, write_off: write_off, write_off_code: row['write_off_code'], remark: row['receipt_detail_remark']) elsif row['receipt_detail_category'] == 'Credit Memo' && row['invoice_credit_memo_number'].present? credit_memo = CreditMemo.find_by(reference_number: row['invoice_credit_memo_number']) receipt.receipt_details << ReceiptDetail.new(category: row['receipt_detail_category'], credit_memo: credit_memo, gl_date: receipt.new_gl_date, amount: row['receipt_detail_amount'], remark: row['receipt_detail_remark']) elsif row['receipt_detail_category'] == 'GL Account' receipt.receipt_details << ReceiptDetail.new(category: row['receipt_detail_category'], amount: row['receipt_detail_amount'], ledger_company_account: LedgerCompanyAccount.joins(:company, :ledger_detail_account).where(companies: { number: row['gl_company'] }, ledger_accounts: { number: row['gl_account'] }).first, business_unit: BusinessUnit.where(number: row['business_unit']).first, ledger_detail_project: LedgerDetailProject.where(project_number: row['project']).first, remark: row['receipt_detail_remark']) end end end if receipt.present? && (row.map { |_k, v| v }.uniq.compact.empty? || receipts_xlsx.last_row == (index + 1)) receipts << receipt unless receipt.nil? receipt = nil end end yield(3, total_steps, 'Updating receipts in the database') if block_given? begin Receipt.transaction do receipts.each do |receipt| receipt.save! report[:edited_receipt_ids] << receipt.id msg = "#{Time.current}: Updated receipt id: #{receipt.id}" logger.info msg report[:successes] << msg end # redirect_to edited_receipts_path(edited_receipt_ids: receipt_ids) end rescue StandardError => e msg = "Unable to edit receipt, message : #{e}." logger.error msg report[:errors] << msg end yield(4, total_steps, 'Finishing...') if block_given? sleep 3 report end |
.validate_data_create_receipts_from_xlsx(xlsx_file) ⇒ Array<String>
Pre-flight a "create receipts" XLSX upload: parse each row, resolve
company/bank account/payor/invoice/credit-memo/GL-account, and
collect a list of human-readable error strings without persisting
anything. Used by the importer UI to render a validation report.
20 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 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 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 |
# File 'app/models/concerns/receipt_spreadsheet_io.rb', line 20 def validate_data_create_receipts_from_xlsx(xlsx_file) errors = [] new_receipt = nil require 'roo' receipts_xlsx = Roo::Spreadsheet.open(xlsx_file.to_file.to_s) receipt_ref = 0 receipts_xlsx.parse(headers: true, clean: true).each_with_index do |row, index| next unless index >= 1 if row.map { |_k, v| v }.uniq.compact.present? # get the data to validate the components of receipt if row['company_id'].present? receipt_ref = (index + 1) company = Company.find_by(id: row['company_id']) if company.nil? errors << "-<b> Receipt located on row #{receipt_ref}:</b> Company with ID #{row['company_id']} not found. <br>" new_receipt = nil # Clear stale receipt to prevent detail rows from using wrong receipt next end bank_account = nil if row['bank_account'].nil? errors << "-<b> Receipt located on row #{receipt_ref}:</b> Column bank_account can't be blank. <br>" else bank_account = company.cc_bank_account if bank_account.nil? ledger_account = LedgerAccount.find_by(number: row['bank_account'].to_i) if ledger_account.nil? || ledger_account.ledger_company_accounts.first.nil? errors << "-<b> Receipt located on row #{receipt_ref}:</b> Bank account #{row['bank_account']} not found. <br>" else bank_account = ledger_account.ledger_company_accounts.first.bank_account end end end customer = nil if row['payor_type'].nil? || row['payor_id'].nil? errors << "-<b> Receipt located on row #{receipt_ref}:</b> Columns payor_type and payor_id can't be blank. <br>" else customer = Party.find_by(id: row['payor_id'].to_i) errors << "-<b> Receipt located on row #{receipt_ref}:</b> Customer with ID #{row['payor_id']} not found. <br>" if customer.nil? end if row['receipt_category'].nil? || row['reference'].nil? || row['currency'].nil? || row['receipt_amount'].nil? || row['receipt_date'].nil? || row['gl_date'].nil? errors << "-<b> Receipt located on row #{receipt_ref} (#{row['payor_type']}: #{row['payor_id']}):</b> Columns receipt_category, reference, currency, receipt_amount, receipt_date and gl_date can't be blank. <br>" end new_receipt = Receipt.new(company: company, bank_account: bank_account, customer: customer, category: row['receipt_category'], reference: row['reference'], currency: row['currency'], amount: row['receipt_amount'], gl_date: row['gl_date'], receipt_date: row['receipt_date'], card_type: row['card_type'], auth_code: row['authorization_code']) end # validate all the necesary receipt details to link to its receipt if new_receipt.present? && row['receipt_detail_category'].present? receipt_ref = (index + 1) valid_categories = ['Invoice', 'Credit Memo', 'GL Account'] errors << "-<b> Receipt detail located on row #{receipt_ref}:</b> Invalid category '#{row['receipt_detail_category']}'. Must be one of: #{valid_categories.join(', ')}. <br>" unless valid_categories.include?(row['receipt_detail_category']) if ['Invoice', 'Credit Memo'].include?(row['receipt_detail_category']) && row['invoice_credit_memo_number'].nil? errors << "-<b> Receipt detail located on row #{receipt_ref}:</b> Column invoice_credit_memo_number can't be blank when an invoice or credit is present. <br>" end if row['receipt_detail_category'].present? && row['receipt_detail_amount'].nil? errors << "-<b> Receipt detail located on row #{receipt_ref} (#{row['invoice_credit_memo_number']}):</b> Column amount can't be blank when receipt_detail_category is present. <br>" end if row['write_off_amount'].present? && (row['write_off_amount'].to_f != 0) && row['write_off_code'].nil? errors << "-<b> Receipt detail located on row #{receipt_ref} (#{row['invoice_credit_memo_number']}):</b> Column write_off_code can't be blank when write_off_amount is present. <br>" end if row['receipt_detail_category'] == 'GL Account' && (row['gl_company'].nil? || row['gl_account'].nil? || row['business_unit'].nil? || row['receipt_detail_remark'].nil?) errors << "-<b> Receipt detail located on row #{receipt_ref}:</b> Columns gl_company, gl_account, business_unit and receipt_detail_remark can't be blank when an G/L is present. <br>" elsif row['receipt_detail_category'] == 'GL Account' ledger_company_account = LedgerCompanyAccount.joins(:company, :ledger_detail_account).where(companies: { number: row['gl_company'] }, ledger_accounts: { number: row['gl_account'] }).first if ledger_company_account.nil? errors << "-<b> Receipt detail located on row #{receipt_ref}:</b> No GL account found for company #{row['gl_company']} and account #{row['gl_account']}. Please verify these values exist. <br>" elsif new_receipt.company.present? && new_receipt.company != ledger_company_account.company errors << "-<b> Receipt detail located on row #{receipt_ref} (#{row['gl_company']}.#{row['gl_account']}):</b> Receipt and GL account must belong to the same company. <br>" end business_unit = BusinessUnit.find_by(number: row['business_unit']) errors << "-<b> Receipt detail located on row #{receipt_ref}:</b> Business unit #{row['business_unit']} not found. <br>" if business_unit.nil? end if row['receipt_detail_category'] == 'Invoice' && row['invoice_credit_memo_number'].present? invoice = Invoice.find_by(reference_number: row['invoice_credit_memo_number']) if invoice.nil? errors << "-<b> Receipt detail located on row #{receipt_ref} (#{row['invoice_credit_memo_number']}):</b> Invoice not found, or reference number is not an invoice. <br>" else if new_receipt.company.present? && new_receipt.company != invoice.company errors << "-<b> Receipt detail located on row #{receipt_ref} (#{row['invoice_credit_memo_number']}):</b> Receipt and invoice must belong to the same company. <br>" end if new_receipt.receipt_date.present? && invoice.gl_date.present? && invoice.gl_date > new_receipt.receipt_date errors << "-<b> Receipt detail located on row #{receipt_ref} (#{row['invoice_credit_memo_number']}):</b> Invoice date can't be greater than receipt date. <br>" end errors << "-<b> Receipt detail located on row #{receipt_ref} (#{row['invoice_credit_memo_number']}):</b> Invoice must be under an approved status to apply the receipt. <br>" if invoice.draft? if new_receipt.customer.is_a?(Customer) && !new_receipt.customer.can_apply_document_customer?(invoice.customer_id, invoice.billing_customer_id) errors << "-<b> Receipt detail located on row #{receipt_ref} (#{row['invoice_credit_memo_number']}):</b> Invoice customer is not the receipt payor or in its parent/child account family. <br>" end end elsif row['receipt_detail_category'] == 'Credit Memo' && row['invoice_credit_memo_number'].present? credit_memo = CreditMemo.find_by(reference_number: row['invoice_credit_memo_number']) if credit_memo.nil? errors << "-<b> Receipt detail located on row #{receipt_ref} (#{row['invoice_credit_memo_number']}):</b> Credit memo not found, or reference number is not a credit memo. <br>" else if new_receipt.company.present? && new_receipt.company != credit_memo.company errors << "-<b> Receipt detail located on row #{receipt_ref} (#{row['invoice_credit_memo_number']}):</b> Receipt and credit memo must belong to the same company. <br>" end if new_receipt.receipt_date.present? && credit_memo.gl_date.present? && credit_memo.gl_date > new_receipt.receipt_date errors << "-<b> Receipt detail located on row #{receipt_ref} (#{row['invoice_credit_memo_number']}):</b> Credit memo date can't be greater than receipt date. <br>" end if credit_memo.requested? || credit_memo.approved? || credit_memo.fully_offset? errors << "-<b> Receipt detail located on row #{receipt_ref} (#{row['invoice_credit_memo_number']}):</b> Credit memo must be under printed, processing refund or partially offset state to apply the receipt. <br>" end if new_receipt.customer.is_a?(Customer) && !new_receipt.customer.can_apply_document_customer?(credit_memo.customer_id, credit_memo.billing_customer_id) errors << "-<b> Receipt detail located on row #{receipt_ref} (#{row['invoice_credit_memo_number']}):</b> Credit memo customer is not the receipt payor or in its parent/child account family. <br>" end end end end end new_receipt = nil if errors.empty? && new_receipt.present? && (row.map { |_k, v| v }.uniq.compact.empty? || receipts_xlsx.last_row == (index + 1)) end errors end |
.validate_data_edit_receipts_from_xlsx(xlsx_file) ⇒ Array<String>
Pre-flight an "edit receipts" XLSX upload: each row references an
existing receipt by id and adds new ReceiptDetails under
new_gl_date. Returns a list of HTML error strings; nothing is
persisted.
229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 |
# File 'app/models/concerns/receipt_spreadsheet_io.rb', line 229 def validate_data_edit_receipts_from_xlsx(xlsx_file) errors = [] receipt = nil require 'roo' receipts_xlsx = Roo::Spreadsheet.open(xlsx_file.to_file.to_s) receipt_ref = 0 receipts_xlsx.parse(headers: true, clean: true).each_with_index do |row, index| next unless index >= 1 if row.map { |_k, v| v }.uniq.compact.present? # update data of existing receipt if row['receipt_number'].present? receipt_ref = (index + 1) receipt = Receipt.find_by(id: row['receipt_number']) if receipt.present? errors << "-<b> Receipt located on row #{receipt_ref} (#{row['receipt_number']}):</b> Column new_gl_date can't be blank. <br>" if row['new_gl_date'].nil? remark = row['receipt_remark'] || receipt.remark receipt.remark = remark receipt.new_gl_date = row['new_gl_date'] else errors << "-<b> Receipt located on row #{receipt_ref} (#{row['receipt_number']}):</b> Receipt not found. <br>" receipt = nil # Clear stale receipt to prevent detail rows from using wrong receipt end end # add new receipt details to existing receipt if receipt.present? && row['receipt_detail_category'].present? receipt_ref = (index + 1) valid_categories = ['Invoice', 'Credit Memo', 'GL Account'] errors << "-<b> Receipt detail located on row #{receipt_ref}:</b> Invalid category '#{row['receipt_detail_category']}'. Must be one of: #{valid_categories.join(', ')}. <br>" unless valid_categories.include?(row['receipt_detail_category']) if ['Invoice', 'Credit Memo'].include?(row['receipt_detail_category']) && row['invoice_credit_memo_number'].nil? errors << "-<b> Receipt detail located on row #{receipt_ref}:</b> Column invoice_credit_memo_number can't be blank when an invoice or credit is present. <br>" end if row['receipt_detail_category'].present? && row['receipt_detail_amount'].nil? errors << "-<b> Receipt detail located on row #{receipt_ref} (#{row['invoice_credit_memo_number']}):</b> Column amount can't be blank when receipt_detail_category is present. <br>" end if row['write_off_amount'].present? && (row['write_off_amount'].to_f != 0) && row['write_off_code'].nil? errors << "-<b> Receipt detail located on row #{receipt_ref} (#{row['invoice_credit_memo_number']}):</b> Column write_off_code can't be blank when write_off_amount is present. <br>" end if row['receipt_detail_category'] == 'GL Account' && (row['gl_company'].nil? || row['gl_account'].nil? || row['business_unit'].nil? || row['receipt_detail_remark'].nil?) errors << "-<b> Receipt detail located on row #{receipt_ref}:</b> Columns gl_company, gl_account, business_unit and receipt_detail_remark can't be blank when an G/L is present. <br>" elsif row['receipt_detail_category'] == 'GL Account' ledger_company_account = LedgerCompanyAccount.joins(:company, :ledger_detail_account).where(companies: { number: row['gl_company'] }, ledger_accounts: { number: row['gl_account'] }).first if ledger_company_account.nil? errors << "-<b> Receipt detail located on row #{receipt_ref}:</b> No GL account found for company #{row['gl_company']} and account #{row['gl_account']}. Please verify these values exist. <br>" elsif receipt.company.present? && receipt.company != ledger_company_account.company errors << "-<b> Receipt detail located on row #{receipt_ref} (#{row['gl_company']}.#{row['gl_account']}):</b> Receipt and GL account must belong to the same company. <br>" end business_unit = BusinessUnit.find_by(number: row['business_unit']) errors << "-<b> Receipt detail located on row #{receipt_ref}:</b> Business unit #{row['business_unit']} not found. <br>" if business_unit.nil? end if row['receipt_detail_category'] == 'Invoice' && row['invoice_credit_memo_number'].present? invoice = Invoice.find_by(reference_number: row['invoice_credit_memo_number']) if invoice.nil? errors << "-<b> Receipt detail located on row #{receipt_ref} (#{row['invoice_credit_memo_number']}):</b> Invoice not found, or reference number is not an invoice. <br>" else errors << "-<b> Receipt detail located on row #{receipt_ref} (#{row['invoice_credit_memo_number']}):</b> Receipt and invoice must belong to the same company. <br>" if receipt.company.present? && receipt.company != invoice.company if receipt.new_gl_date.present? && invoice.gl_date.present? && invoice.gl_date > receipt.new_gl_date errors << "-<b> Receipt detail located on row #{receipt_ref} (#{row['invoice_credit_memo_number']}):</b> Invoice date can't be greater than receipt date. <br>" end errors << "-<b> Receipt detail located on row #{receipt_ref} (#{row['invoice_credit_memo_number']}):</b> Invoice must be under an approved status to apply the receipt. <br>" if invoice.draft? if receipt.customer.is_a?(Customer) && !receipt.customer.can_apply_document_customer?(invoice.customer_id, invoice.billing_customer_id) errors << "-<b> Receipt detail located on row #{receipt_ref} (#{row['invoice_credit_memo_number']}):</b> Invoice customer is not the receipt payor or in its parent/child account family. <br>" end end elsif row['receipt_detail_category'] == 'Credit Memo' && row['invoice_credit_memo_number'].present? credit_memo = CreditMemo.find_by(reference_number: row['invoice_credit_memo_number']) if credit_memo.nil? errors << "-<b> Receipt detail located on row #{receipt_ref} (#{row['invoice_credit_memo_number']}):</b> Credit memo not found, or reference number is not a credit memo. <br>" else if receipt.company.present? && receipt.company != credit_memo.company errors << "-<b> Receipt detail located on row #{receipt_ref} (#{row['invoice_credit_memo_number']}):</b> Receipt and credit memo must belong to the same company. <br>" end if receipt.new_gl_date.present? && credit_memo.gl_date.present? && credit_memo.gl_date > receipt.new_gl_date errors << "-<b> Receipt detail located on row #{receipt_ref} (#{row['invoice_credit_memo_number']}):</b> Credit memo date can't be greater than receipt date. <br>" end if credit_memo.requested? || credit_memo.approved? || credit_memo.fully_offset? errors << "-<b> Receipt detail located on row #{receipt_ref} (#{row['invoice_credit_memo_number']}):</b> Credit memo must be under printed, processing refund or partially offset state to apply the receipt. <br>" end if receipt.customer.is_a?(Customer) && !receipt.customer.can_apply_document_customer?(credit_memo.customer_id, credit_memo.billing_customer_id) errors << "-<b> Receipt detail located on row #{receipt_ref} (#{row['invoice_credit_memo_number']}):</b> Credit memo customer is not the receipt payor or in its parent/child account family. <br>" end end end end end receipt = nil if errors.empty? && receipt.present? && (row.map { |_k, v| v }.uniq.compact.empty? || receipts_xlsx.last_row == (index + 1)) end errors end |