mirror of
https://github.com/rickbarrette/redmine_qbo_vehicles.git
synced 2026-10-03 17:10:43 -04:00
Compare commits
9
Commits
2026.8.0
...
b021ca9f7d
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b021ca9f7d | ||
|
|
feb357b9a4 | ||
|
|
df41436e61 | ||
|
|
849dcabff7 | ||
|
|
5eac85351d | ||
|
|
b2f65f90b6 | ||
|
|
05bbcb43db | ||
|
|
9cc2af9cec | ||
|
|
9d101fef38 |
No files matched your search
@@ -13,6 +13,8 @@ class VehiclesController < ApplicationController
|
||||
|
||||
include AuthHelper
|
||||
|
||||
# This tells Redmine to check global permissions for the current controller/action
|
||||
before_action :authorize_global
|
||||
before_action :require_user
|
||||
|
||||
def allowed_params
|
||||
|
||||
@@ -33,6 +33,23 @@ class Vehicle < ApplicationRecord
|
||||
issues.includes(:invoices).flat_map(&:invoices).uniq.compact
|
||||
end
|
||||
|
||||
def last_service
|
||||
return issues
|
||||
.joins(:custom_values)
|
||||
.where(custom_values: { custom_field_id: CustomField.find_by(name: "Mileage")&.id })
|
||||
.where.not(custom_values: { value: ['', nil] })
|
||||
.order(created_on: :desc)
|
||||
.first
|
||||
end
|
||||
|
||||
def last_service_date
|
||||
return last_service.created_on&.to_date
|
||||
end
|
||||
|
||||
def last_service_mileage
|
||||
return last_service&.custom_values&.find_by(custom_field_id: CustomField.find_by(name: "Mileage")&.id)&.value.presence
|
||||
end
|
||||
|
||||
def make=(val)
|
||||
super(val.to_s.strip)
|
||||
end
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
<% if User.current.allowed_to?(:add_vehicles, nil, global: true) %>
|
||||
<% if appointment.customer.present? %>
|
||||
<% unless appointment.vehicle.present? &&%>
|
||||
<p>
|
||||
@@ -7,3 +8,4 @@
|
||||
</p>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<% end %>
|
||||
@@ -1,5 +1,7 @@
|
||||
<h4><%=t(:field_vehicles)%>:</h4>
|
||||
<%= render partial: 'vehicles/list', locals: { vehicles: customer.vehicles.paginate(page: params[:page]), show_customer: false, show_checkbox: true } %>
|
||||
<div style="float: right;">
|
||||
<% if User.current.allowed_to?(:add_vehicles, nil, global: true) %>
|
||||
<%= button_to t(:button_new_vehicle), new_customer_vehicle_path(customer), method: :get %>
|
||||
<% end %>
|
||||
</div>
|
||||
@@ -8,10 +8,13 @@
|
||||
<div class="value" id="vin">
|
||||
<div id="copyLink" onclick="handleCopy(event)"><%= split_vin[0] if split_vin %><b><%= split_vin[1] if split_vin %></b></div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="vehicle_notes attribute">
|
||||
<div class="label"><%= t(:field_notes) %>:</div>
|
||||
<pre class="value" id="note-display" style="text-align: left; white-space: pre-wrap; font-family: inherit; "><%=notes%></pre>
|
||||
<% if vehicle.present? %>
|
||||
<div class="value wiki">
|
||||
<%= textilizable(@issue.vehicle, :notes) %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
@@ -5,7 +5,6 @@
|
||||
|
||||
<table>
|
||||
<tbody>
|
||||
|
||||
<tr>
|
||||
<th><%= t(:field_customer) %></th>
|
||||
<td><%= vehicle.customer ? link_to(vehicle.customer.name, customer_path(vehicle.customer)) : t(:no_customer) %></td>
|
||||
@@ -23,45 +22,42 @@
|
||||
</td>
|
||||
</tr>
|
||||
|
||||
<tr>
|
||||
<th><%= t(:label_trim) %></th>
|
||||
<td><%= vehicle.doors %> <%= t(:label_door) if vehicle.doors? %> <%= vehicle.trim %></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="splitcontentleft">
|
||||
|
||||
<div class="splitcontentright">
|
||||
<h4><%= t(:field_notes) %>:</h4>
|
||||
<pre id="note-display" style="text-align: left; white-space: pre-wrap; font-family: inherit; ">
|
||||
<td><%= vehicle.notes %></td>
|
||||
</pre>
|
||||
</tr>
|
||||
|
||||
<script>
|
||||
const preElement = document.getElementById('note-display');
|
||||
// This takes the text, trims the edges, and puts it back
|
||||
preElement.textContent = preElement.textContent.trim();
|
||||
</script>
|
||||
|
||||
<div class="vehicle_notes attribute">
|
||||
<div class="label"><%= t(:field_notes) %>:</div>
|
||||
<% if vehicle.notes.present? %>
|
||||
<div class="value wiki">
|
||||
<%= textilizable(vehicle, :notes) %>
|
||||
</div>
|
||||
<% end %>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<% last = vehicle.issues.last %>
|
||||
<% if last %>
|
||||
<% mileage = last.custom_values.find_by(custom_field_id: CustomField.find_by_name("Mileage").id) %>
|
||||
<% mileage = vehicle.last_service_mileage %>
|
||||
|
||||
<% if mileage %>
|
||||
<p>
|
||||
<b><%=t(:label_last_service) %></b> <%= last&.closed_on&.to_date || l(:label_unknown) %>
|
||||
at <%= mileage.value %>
|
||||
<%=t(:label_miles) %>
|
||||
<b><%= t(:label_last_service) %></b> <%= vehicle.last_service_date %>
|
||||
at <%= mileage %> <%= t(:label_miles) %>
|
||||
</p>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<div style="float: right;">
|
||||
<% if User.current.allowed_to?(:edit_vehicles, nil, global: true) %>
|
||||
<%= button_to t(:label_edit), edit_vehicle_path(vehicle), method: :get %>
|
||||
<% end %>
|
||||
<% if User.current.allowed_to?(:delete_vehicles, nil, global: true) %>
|
||||
<%= button_to t(:label_delete), vehicle, method: :delete, data: { confirm: t(:warn_ru_sure) } %>
|
||||
<% end %>
|
||||
</div>
|
||||
@@ -42,7 +42,14 @@
|
||||
<div class="clearfix">
|
||||
<%=t(:field_notes)%>:
|
||||
<div class="input">
|
||||
<%= f.text_area :notes, cols: 60, rows: 10, no_label: true %>
|
||||
<p>
|
||||
<%= f.text_area :notes,
|
||||
rows: 8,
|
||||
class: 'wiki-edit',
|
||||
style: 'width: 95%;',
|
||||
id: 'customer_appointment_description' %>
|
||||
<%= wikitoolbar_for 'customer_appointment_description' %>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<div class="container">
|
||||
|
||||
<% if show_checkbox %>
|
||||
<%= check_box_tag "vehicle_ids[]", vehicle.id, false, onchange: "updateLink()", data: { url: vehicle_path(vehicle).html_safe, text: vehicle.to_s }, class: "appointment checkbox" %>
|
||||
<%= check_box_tag "vehicle_ids[]", vehicle.id, false, onchange: "updateLink()", data: { url: vehicle_path(vehicle).html_safe, text: vehicle.to_s }, class: "vehicle-checkbox appointment checkbox" %>
|
||||
<% else %>
|
||||
<div class='checkbox'>
|
||||
</div>
|
||||
@@ -21,10 +21,22 @@
|
||||
<% if show_customer %>
|
||||
<%= vehicle.customer %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<%
|
||||
mileage = vehicle.last_service_mileage
|
||||
%>
|
||||
|
||||
<% if mileage %>
|
||||
<br/>
|
||||
<b><%= t(:label_last_service) %></b> <%= vehicle.last_service_date %>
|
||||
at <%= mileage %> <%= t(:label_miles) %>
|
||||
<% end %>
|
||||
|
||||
</div>
|
||||
<br/>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
|
||||
@@ -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.8.0'
|
||||
version '2026.9.1'
|
||||
url 'https://github.com/rickbarrette/redmine_qbo_vehicles'
|
||||
author_url 'https://barrettefabrication.com'
|
||||
requires_redmine version_or_higher: '6.1.0'
|
||||
@@ -29,11 +29,15 @@ Redmine::Plugin.register :redmine_qbo_vehicles do
|
||||
# Add safe attributes for core models
|
||||
Issue.safe_attributes :vehicle_id
|
||||
|
||||
# Permissions for security
|
||||
permission :view_vehicles, vehicles: :new, public: false
|
||||
# Global Permissions
|
||||
permission :view_vehicles, { vehicles: [:index, :show] }, global: true
|
||||
permission :add_vehicles, { vehicles: [:new, :create] }, global: true
|
||||
permission :edit_vehicles, { vehicles: [:edit, :update] }, global: true
|
||||
permission :delete_vehicles, { vehicles: [:destroy] }, global: true
|
||||
|
||||
|
||||
# Register top menu items
|
||||
menu :top_menu, :vehicles, { controller: :vehicles, action: :index }, caption: :field_vehicles, if: Proc.new { User.current.logged? }
|
||||
menu :top_menu, :vehicles, { controller: :vehicles, action: :index }, caption: :field_vehicles, if: Proc.new { User.current.allowed_to?(:view_vehicles, nil, global: true) }
|
||||
|
||||
Redmine::Search.map do |search|
|
||||
search.register :vehicles
|
||||
|
||||
@@ -19,5 +19,13 @@ module RedmineQboVehicles
|
||||
Vehicles::Hooks::PdfHookListener
|
||||
Vehicles::Hooks::ViewHookListener
|
||||
Vehicles::Hooks::CustomerAppointmentShowHookListener
|
||||
|
||||
# If the SubtaskFieldCopier plugin is installed, register our fields
|
||||
if defined?(SubtaskFieldCopier)
|
||||
SubtaskFieldCopier.registered_fields << :vehicle
|
||||
|
||||
# Ensure there are no duplicates in case of hot-reloads in development mode
|
||||
SubtaskFieldCopier.registered_fields.uniq!
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -20,8 +20,7 @@ module Vehicles
|
||||
partial: 'issues/show_issue_view_right',
|
||||
locals: {
|
||||
vehicle: context[:issue].vehicle ? link_to(context[:issue].vehicle) : nil,
|
||||
split_vin: context[:issue].vehicle ? context[:issue].vehicle.vin.to_s.scan(/.{1,9}/) : nil,
|
||||
notes: context[:issue].vehicle ? context[:issue].vehicle.notes : nil
|
||||
split_vin: context[:issue].vehicle ? context[:issue].vehicle.vin.to_s.scan(/.{1,9}/) : nil
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
Reference in new issue
Block a user