mirror of
https://github.com/rickbarrette/redmine_qbo_vehicles.git
synced 2026-04-02 15:11:58 -04:00
Compare commits
10 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9a1678d353 | |||
| 28957c5dff | |||
| a67b75671e | |||
| 492ff000bf | |||
| e0fc07141c | |||
| 3e6c2672e0 | |||
| 71bde3a249 | |||
| 4f2ae19460 | |||
| 9ee792cbd8 | |||
| ae7ccbf4b0 |
@@ -60,10 +60,15 @@ class VehiclesController < ApplicationController
|
||||
# display a specific vehicle
|
||||
def show
|
||||
begin
|
||||
@vehicle = Vehicle.find_by_id(params[:id])
|
||||
@vehicle = Vehicle.includes(issues: [:estimate, :invoices]).find(params[:id])
|
||||
@vin = @vehicle.vin.scan(/.{1,9}/) if @vehicle.vin
|
||||
@issues = @vehicle.issues.order(id: :desc)
|
||||
@closed_issues = (@issues - @issues.open)
|
||||
@issues = @vehicle.issues
|
||||
.joins(:status)
|
||||
.includes(:estimate, :invoices, :status, :project, :tracker, :priority)
|
||||
.order(id: :desc)
|
||||
@open_issues = @issues.select { |i| !i.status.is_closed }
|
||||
@closed_issues = @issues.select { |i| i.status.is_closed }
|
||||
|
||||
flash[:error] = t :alert_no_customer if @vehicle.customer.nil?
|
||||
rescue
|
||||
flash[:error] = t :alert_vehicle_not_found
|
||||
|
||||
@@ -10,115 +10,24 @@
|
||||
|
||||
class Vehicle < ActiveRecord::Base
|
||||
|
||||
include Redmine::Acts::Searchable
|
||||
include Redmine::Acts::Event
|
||||
|
||||
belongs_to :customer
|
||||
has_many :issues
|
||||
validates_presence_of :customer
|
||||
validates :vin, uniqueness: true
|
||||
before_save :decode_vin
|
||||
|
||||
def self.searchable_columns
|
||||
['vin', 'make', 'model', 'year']
|
||||
end
|
||||
acts_as_searchable columns: %w[vin make model year],
|
||||
scope: ->(_context) { left_joins(:project) },
|
||||
date_column: :updated_at
|
||||
|
||||
def self.searchable_options
|
||||
{
|
||||
columns: searchable_columns,
|
||||
scope: self.all
|
||||
}
|
||||
end
|
||||
|
||||
def self.search_result_ranks_and_ids(tokens, user, project = nil, options = {})
|
||||
return {} if tokens.blank?
|
||||
|
||||
scope = self.all
|
||||
|
||||
tokens.each do |token|
|
||||
q = "%#{sanitize_sql_like(token)}%"
|
||||
scope = where("vin LIKE ? OR make LIKE ? OR model LIKE ? OR year LIKE ?", "%#{q}%", "%#{q}%", "%#{q}%", "%#{q}%")
|
||||
end
|
||||
|
||||
ids = scope.distinct.limit(options[:limit] || 100).pluck(:id)
|
||||
|
||||
# Assign simple uniform ranking
|
||||
ids.each_with_object({}) { |id, h| h[id] = id }
|
||||
end
|
||||
|
||||
def self.search_results_from_ids(ids, *args)
|
||||
return [] if ids.blank?
|
||||
|
||||
Rails.logger.warn "IDS: #{ids.inspect}"
|
||||
|
||||
ids = ids.map(&:to_i)
|
||||
records = where(id: ids).index_by(&:id)
|
||||
ids.map { |id| records[id] }.compact
|
||||
end
|
||||
|
||||
# ---- Redmine Search Event Interface ----
|
||||
def event_type
|
||||
'vehicle'
|
||||
end
|
||||
|
||||
def event_title
|
||||
to_s
|
||||
end
|
||||
|
||||
def event_description
|
||||
"#{vin} #{year} #{make} #{model}"
|
||||
end
|
||||
|
||||
def event_url
|
||||
Rails.application.routes.url_helpers.vehicle_path(self)
|
||||
end
|
||||
|
||||
def event_datetime
|
||||
updated_at || created_at
|
||||
end
|
||||
|
||||
def project
|
||||
# Vehicles are not project scoped in your schema.
|
||||
# Return nil so global search works cleanly.
|
||||
nil
|
||||
end
|
||||
|
||||
# returns a human readable string
|
||||
def to_s
|
||||
if year.nil? or make.nil? or model.nil?
|
||||
return "#{vin}"
|
||||
else
|
||||
split_vin = vin.scan(/.{1,9}/)
|
||||
return "#{year} #{make} #{model} - #{split_vin[1]}"
|
||||
end
|
||||
end
|
||||
|
||||
# returns the raw JSON details from NHTSA
|
||||
def details
|
||||
get_details if @details.nil?
|
||||
return @details
|
||||
end
|
||||
|
||||
# Force Upper Case for make numbers
|
||||
def make=(val)
|
||||
# The to_s is in case you get nil/non-string
|
||||
write_attribute(:make, val.to_s.titleize)
|
||||
end
|
||||
|
||||
# Force Upper Case for model numbers
|
||||
def model=(val)
|
||||
# The to_s is in case you get nil/non-string
|
||||
write_attribute(:model, val.to_s.titleize)
|
||||
end
|
||||
|
||||
# Force Upper Case & strip VIN of all illegal chars (for barcode scanner)
|
||||
def vin=(val)
|
||||
val = val.to_s.upcase.gsub(/[^A-HJ-NPR-Za-hj-npr-z\d]+/,"")
|
||||
write_attribute(:vin, val)
|
||||
end
|
||||
|
||||
# search for a vehicle by vin, make, model, or year
|
||||
def self.search(query)
|
||||
q = sanitize_sql_like(query)
|
||||
where("vin LIKE ? OR make LIKE ? OR model LIKE ? OR year LIKE ?", "%#{q}%", "%#{q}%", "%#{q}%", "%#{q}%")
|
||||
end
|
||||
acts_as_event :title => Proc.new {|o| "#{o.to_s}"},
|
||||
:url => Proc.new {|o| { :controller => 'vehicles', :action => 'show', :id => o.id} },
|
||||
:type => :to_s,
|
||||
:description => Proc.new {|o| "#{o.vin} - #{o.customer}"},
|
||||
:datetime => Proc.new {|o| o.updated_at || o.created_at}
|
||||
|
||||
# decodes a vin and updates self
|
||||
def decode_vin
|
||||
@@ -137,9 +46,10 @@ class Vehicle < ActiveRecord::Base
|
||||
self.name = to_s
|
||||
end
|
||||
|
||||
# reurns all invoices for this vehicle
|
||||
def invoices
|
||||
self.issues.flat_map(&:invoices).uniq.compact
|
||||
# returns the raw JSON details from NHTSA
|
||||
def details
|
||||
get_details if @details.nil?
|
||||
return @details
|
||||
end
|
||||
|
||||
# returns all estimates for this vehicle
|
||||
@@ -147,6 +57,64 @@ class Vehicle < ActiveRecord::Base
|
||||
self.issues.flat_map(&:estimate).uniq.compact
|
||||
end
|
||||
|
||||
# reurns all invoices for this vehicle
|
||||
def invoices
|
||||
self.issues.flat_map(&:invoices).uniq.compact
|
||||
end
|
||||
|
||||
# Force Upper Case for make numbers
|
||||
def make=(val)
|
||||
# The to_s is in case you get nil/non-string
|
||||
write_attribute(:make, val.to_s.titleize)
|
||||
end
|
||||
|
||||
# Force Upper Case for model numbers
|
||||
def model=(val)
|
||||
# The to_s is in case you get nil/non-string
|
||||
write_attribute(:model, val.to_s.titleize)
|
||||
end
|
||||
|
||||
# needed for redmine's search and event system, but we don't want to tie vehicles to projects
|
||||
def project
|
||||
nil
|
||||
end
|
||||
|
||||
# search for a vehicle by vin, make, model, or year
|
||||
def self.search(query)
|
||||
q = sanitize_sql_like(query)
|
||||
where("vin LIKE ? OR make LIKE ? OR model LIKE ? OR year LIKE ?", "%#{q}%", "%#{q}%", "%#{q}%", "%#{q}%")
|
||||
end
|
||||
|
||||
# Override the defult redmine seach method to rank results by id
|
||||
def self.search_result_ranks_and_ids(tokens, user, project = nil, options = {})
|
||||
return {} if tokens.blank?
|
||||
|
||||
scope = self.all
|
||||
|
||||
tokens.each do |token|
|
||||
scope = scope.search(token)
|
||||
end
|
||||
|
||||
ids = scope.distinct.limit(options[:limit] || 100).pluck(:id)
|
||||
ids.index_with { |id| id }
|
||||
end
|
||||
|
||||
# returns a human readable string
|
||||
def to_s
|
||||
if year.nil? or make.nil? or model.nil?
|
||||
return "#{vin}"
|
||||
else
|
||||
split_vin = vin.scan(/.{1,9}/)
|
||||
return "#{year} #{make} #{model} - #{split_vin[1]}"
|
||||
end
|
||||
end
|
||||
|
||||
# Force Upper Case & strip VIN of all illegal chars (for barcode scanner)
|
||||
def vin=(val)
|
||||
val = val.to_s.upcase.gsub(/[^A-HJ-NPR-Za-hj-npr-z\d]+/,"")
|
||||
write_attribute(:vin, val)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# init method to pull JSON details from NHTSA
|
||||
|
||||
@@ -14,9 +14,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<h3><%=@issues.open.count%> <%=t(:label_open_issues)%></h3>
|
||||
<h3><%=@open_issues.count%> <%=t(:label_open_issues)%></h3>
|
||||
|
||||
<%= render partial: 'issues/list_simple', locals: {issues: @issues.open} %>
|
||||
<%= render partial: 'issues/list_simple', locals: {issues: @open_issues} %>
|
||||
|
||||
<h3><%=@closed_issues.count%> <%=t(:label_closed_issues)%></h3>
|
||||
|
||||
|
||||
@@ -12,6 +12,11 @@ async function handleCopy(event) {
|
||||
link = event.target;
|
||||
}
|
||||
|
||||
// If the text is already "Copied!", don't do anything
|
||||
if (text == "Copied!") {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Write to clipboard
|
||||
await navigator.clipboard.writeText(text);
|
||||
|
||||
2
init.rb
2
init.rb
@@ -14,7 +14,7 @@ Redmine::Plugin.register :redmine_qbo_vehicles do
|
||||
name 'Redmine QBO Vehicles plugin'
|
||||
author 'Rick Barrette'
|
||||
description 'This is a plugin for Redmine to intergrate with the redmine_qbo plugin to provide vehicle data tracking'
|
||||
version '2026.2.6'
|
||||
version '2026.3.0'
|
||||
url 'https://github.com/rickbarrette/redmine_qbo_vehicles'
|
||||
author_url 'https://barrettefabrication.com'
|
||||
requires_redmine version_or_higher: '6.1.0'
|
||||
|
||||
@@ -17,13 +17,13 @@ module Vehicles
|
||||
|
||||
# Called by Redmine QBO Invoice
|
||||
def process_invoice_custom_fields(context={})
|
||||
Rails.logger.info "redmine_qbo_vehicles.process_invoice_custom_fields"
|
||||
log "Processing invoice custom fields for invoice ##{context[:invoice].id}"
|
||||
issue = context[:issue]
|
||||
|
||||
# update the invoive custom fields with infomation from the issue if available
|
||||
context[:invoice].custom_fields.each do |cf|
|
||||
|
||||
Rails.logger.info "Checking invoice.custom field: #{cf.name}"
|
||||
log "Checking invoice custom field: #{cf.name}"
|
||||
|
||||
# VIN from the attached vehicle
|
||||
begin
|
||||
@@ -32,13 +32,13 @@ module Vehicles
|
||||
# TODO check cf_sync_confict flag once implemented
|
||||
if cf.string_value.to_s.blank?
|
||||
|
||||
Rails.logger.info "VIN was blank, updating the invoice vin in quickbooks"
|
||||
log "VIN was blank, updating the invoice vin in quickbooks"
|
||||
vin = context[:issue].vehicle.vin
|
||||
break if vin.nil?
|
||||
|
||||
if not cf.string_value.to_s.eql? vin
|
||||
cf.string_value = vin.to_s
|
||||
Rails.logger.info "VIN has changed"
|
||||
log "VIN has changed"
|
||||
context[:is_changed] = true
|
||||
end
|
||||
|
||||
@@ -47,7 +47,7 @@ module Vehicles
|
||||
end
|
||||
rescue
|
||||
#do nothing
|
||||
Rails.logger.info "redmine_qbo_vehicles.process_invoice_custom_fields failed, skipping"
|
||||
log "redmine_qbo_vehicles.process_invoice_custom_fields failed, skipping"
|
||||
return nil
|
||||
end
|
||||
end
|
||||
@@ -56,6 +56,12 @@ module Vehicles
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def log(msg)
|
||||
Rails.logger.info "[InvoiceHookListener] #{msg}"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user