7 Commits
11 changed files with 69 additions and 55 deletions

No files matched your search

+21 -17
View File
@@ -64,26 +64,30 @@ Restart your application server (Puma, Passenger, etc.) to initialize the plugin
---
## Configuration
This plugin depends on the **Redmine QuickBooks Online** plugin.
Before using this plugin:
1. Install and configure the parent plugin.
2. Ensure your **QuickBooks Online** company file is connected.
3. Sync Accounts & Items via plugin settings
4. Set default income account for new items via plugin settings
---
## Usage
Add appointments
1. Managing Customer Appointments
* Viewing the Calendar: Navigate to the Customer Calendar tab within a project or via the top menu to view scheduled customer appointments.
* Adding Appointments: Click on available days on the calendar grid to schedule a new appointment (restricted by user permissions, working days, and configured holidays).
* Editing & Deleting: Existing appointments can be modified or removed directly from the calendar interface depending on user privileges.
* My Page Integration: Users can add the Upcoming Appointments block to their personal My Page dashboard to track their schedule at a glance.
2. Holidays and Non-Working Days
Administrators can manage the holiday calendar to prevent appointments from being scheduled on non-working days.
* Go to Administration -> Holidays (or the corresponding holidays menu item).
* Fixed Holidays: Set specific dates (e.g., December 25th) that recur annually or apply to a single year.
* Dynamic Holidays: Configure rules for floating holidays (e.g., the 1st Monday of September).
* Validation: The system automatically blocks appointment creation on standard weekends, non-working days, and defined holidays.
3. Permissions & Global Access
Appointment permissions are configured globally to apply system-wide:
1. Navigate to Administration -> Roles and permissions.
2. Scroll to the global permissions section.
3. Assign the following permissions as needed based on user roles (such as Non-member or custom staff roles):
* View customer appointments
* Add customer appointments
* Edit customer appointments
* Delete customer appointments
---
@@ -12,7 +12,8 @@ class CustomerAppointmentsController < ApplicationController
before_action :require_login
before_action :find_project, except: [:index]
before_action :find_appointment, only: [ :show, :edit, :update, :destroy ]
#before_action :authorize
# This tells Redmine to check global permissions for the current controller/action
before_action :authorize_global
helper :projects
@@ -85,7 +86,7 @@ class CustomerAppointmentsController < ApplicationController
def destroy
@appointment.destroy
flash[:notice] = l(:notice_successful_delete)
redirect_to project_customer_appointments_path( @project)
redirect_to issues_calendar_path
end
private
@@ -116,6 +117,8 @@ class CustomerAppointmentsController < ApplicationController
def find_appointment
@appointment = CustomerAppointment.find(params[:id])
rescue
render_404
end
def log(msg)
+4 -2
View File
@@ -197,15 +197,17 @@
</span>
<!-- Center: New Appointment Action -->
<% if User.current.allowed_to?(:add_customer_appointments, nil, global: true) %>
<span class="day-num-center" style="display: inline-flex; align-items: center; justify-self: center;">
<%# Only show ADD button if it's in the future, NOT a weekend, and NOT a custom holiday %>
<% if day >= User.current.today && !is_standard_weekend && !is_custom_holiday %>
<%# Only show ADD button if it's in the future, NOT a weekend, NOT a holiday, AND user has permission %>
<% if day >= User.current.today && !is_standard_weekend && !is_custom_holiday && User.current.allowed_to?(:add_customer_appointments, nil, global: true) %>
<%= link_to sprite_icon('add'),
new_customer_appointment_path(start_date: day),
class: 'icon-only icon-add',
title: l(:label_appointment_new, default: 'New Appointment') %>
<% end %>
</span>
<% end %>
<!-- Right: Total Hours -->
<span class="day-num-right" style="display: inline-flex; align-items: center; justify-self: end;">
@@ -1,4 +1,6 @@
<% if User.current.allowed_to?(:add_customer_appointments, nil, global: true) %>
<p>
<% customer = context[:customer] %>
<%= link_to l(:label_add_appointment), new_customer_appointment_path(customer_id: customer.id), target: :_blank, id: :appointment_link %>
</p>
</p>\
<% end %>
Whitespace-only changes.
@@ -92,6 +92,8 @@
<hr>
<% if User.current.allowed_to?(:edit_customer_appointments, nil, global: true) %>
<p class="buttons">
<%= link_to l(:label_open_appointment), customer_appointment_path(appointment), class: "icon icon-edit" %>
</p>
<% end %>
@@ -5,9 +5,11 @@
<i class="fa-regular fa-calendar-days"></i> <%= l(:button_calendar) %>
<% end %>
<% if User.current.allowed_to?(:add_customer_appointments, nil, global: true) %>
<%= link_to new_customer_appointment_path do %>
<i class="fa-regular fa-pen-to-square"></i> <%= l(:button_add) %>
<% end %>
<% end %>
</div>
@@ -3,17 +3,23 @@
<i class="fa-regular fa-calendar-days"></i> <%= l(:button_calendar) %>
<% end %>
<% if User.current.allowed_to?(:add_customer_appointments, nil, global: true) %>
<%= link_to new_issue_path(issue: { customer_id: @appointment.customer_id, vehicle_id: @appointment.vehicle_id, estimate_id: @appointment.estimate_id }), target: :_blank do %>
<i class="fa-solid fa-plus"></i> <%= l(:label_new_issue) %>
<% end %>
<% end %>
<% if User.current.allowed_to?(:edit_customer_appointments, nil, global: true) %>
<%= link_to edit_customer_appointment_path(@appointment) do %>
<i class="fa-solid fa-pen-to-square"></i> <%= l(:button_edit) %>
<% end %>
<% end %>
<% if User.current.allowed_to?(:delete_customer_appointments, nil, global: true) %>
<%= link_to customer_appointment_path(@appointment), method: :delete, data: { confirm: l(:text_are_you_sure) } do %>
<i class="fa-solid fa-trash"></i> <%= l(:button_delete) %>
<% end %>
<% end %>
</div>
<h2>
@@ -36,6 +42,7 @@
</span>
</h2>
<% if User.current.allowed_to?(:edit_customer_appointments, nil, global: true) %>
<%# Quick Status Action Buttons %>
<%
statuses = CustomerAppointment::STATUSES
@@ -50,6 +57,7 @@
class: "quick-status-btn status-#{status_key.parameterize}" %>
<% end %>
</div>
<% end %>
<div class="issue details">
<div class="attributes">
+7 -1
View File
@@ -34,6 +34,7 @@ en:
field_vehicle: Vehicle
label_add_appointment: Add Appointment
label_appointment: Appointment
label_appointment_new: "New Appointment"
label_appointments: Appointments
label_customer_appointment: Customer Appointment
label_customer_calendar: Customer Calendar
@@ -43,9 +44,11 @@ en:
label_end_date: End Date
label_estimated_hours: Estimated Time
label_event: Event
label_hide_past_weeks: "Hide Past Weeks"
label_holiday_edit: Edit Holiday
label_holiday_new: New Holiday
label_holiday_plural: Holidays
label_legend: "Legend"
label_new_appointment: New Appointment
label_of: "of"
label_only_customer_appointments: Show Customer Appointments Only
@@ -65,6 +68,9 @@ en:
label_week_4th: "4th"
label_week_last: "Last"
notice_unable_delete_holiday: "Unable to delete holiday"
permission_add_customer_appointments: "Add customer appointments"
permission_delete_customer_appointments: "Delete customer appointments"
permission_edit_customer_appointments: "Edit customer appointments"
permission_manage_customer_appointments: Manage Customer Appointments
permission_view_customer_appointments: View Customer Appointments
permission_view_customer_appointments: "View customer appointments"
title_customer_appointments: Customer Appointments
+7 -32
View File
@@ -18,7 +18,6 @@ Redmine::Plugin.register :redmine_qbo_calendar do
url 'https://github.com/rickbarrette/redmine_qbo_calendar'
author_url 'https://barrettefabrication.com'
requires_redmine version_or_higher: '7.0.0'
settings partial: 'calendar/settings'
# Add Holidays index to the Redmine Administration Menu
menu :admin_menu, :holidays, { controller: 'holidays', action: 'index' }, caption: :label_holiday_plural, html: { class: 'icon icon-list' }
@@ -30,39 +29,15 @@ Redmine::Plugin.register :redmine_qbo_calendar do
raise 'Please install the redmine_qbo plugin (https://github.com/rickbarrette/redmine_qbo)'
end
project_module :customer_calendar do
permission :view_customer_appointments,
{ customer_appointments: [:index, :show] }
# Global Permissions
permission :view_customer_appointments, { customer_appointments: [:index, :show] }, global: true
permission :add_customer_appointments, { customer_appointments: [:new, :create] }, global: true
permission :edit_customer_appointments, { customer_appointments: [:edit, :update] }, global: true
permission :delete_customer_appointments, { customer_appointments: [:destroy] }, global: true
permission :manage_customer_appointments,
{
customer_appointments: [
:new,
:create,
:edit,
:update,
:destroy
]
}
end
# Register top menu items
menu :top_menu, :calendar, { controller: :calendars, action: :show }, caption: :label_calendar, if: Proc.new {User.current.logged?}
menu :project_menu,
:customer_calendar,
{ controller: 'events',
action: 'index' },
caption: 'Customer Calendar',
after: :calendar,
param: :project_id
end
Rails.configuration.to_prepare do
ApplicationController.prepend_view_path( File.expand_path("app/views", __dir__) )
Redmine::Views::MyPage::Block.register(
'upcoming_appointments',
label: :label_upcoming_appointments,
partial: 'my/blocks/upcoming_appointments'
)
end
RedmineQboCalendar.setup
+10
View File
@@ -20,5 +20,15 @@ module RedmineQboCalendar
RedmineQboCalendar::Hooks::ViewHookListener
RedmineQboCalendar::Hooks::CustomerActionsHookListener
RedmineQboCalendar::Hooks::CustomerShowHookListener
Rails.configuration.to_prepare do
ApplicationController.prepend_view_path( File.expand_path("app/views", __dir__) )
Redmine::Views::MyPage::Block.register(
'upcoming_appointments',
label: :label_upcoming_appointments,
partial: 'my/blocks/upcoming_appointments'
)
end
end
end