Compare commits

..

5 Commits

5 changed files with 33 additions and 26 deletions

View File

@@ -2,7 +2,7 @@
<% estimates.sort.reverse.each do |estimate| %>
<div class="row">
<%= check_box_tag "estimate_ids[]", estimate.id, false, onchange: "updateLink()", class: "estimate-checkbox" %>
<%= check_box_tag "estimate_ids[]", estimate.id, false, onchange: "updateLink()", data: { url: estimate_path(estimate), text: "Estimate ##{estimate.to_s}" }, class: "estimate-checkbox appointment" %>
<b><%= link_to "##{estimate.doc_number}", estimate_path(estimate), target: :_blank %></b> <%= estimate.txn_date %>
</div>
<% end %>

View File

@@ -12,7 +12,7 @@
<% invoices.sort.reverse.each do |invoice| %>
<div class="row">
<%= check_box_tag "invoice_ids[]", invoice.id, false, onchange: "updateLink()", class: "invoice-checkbox" if invoices.count > 1 %>
<%= check_box_tag "invoice_ids[]", invoice.id, false, onchange: "updateLink()", data: { url: invoice_path(invoice), text: "Invoice ##{invoice.to_s}" }, class: "invoice-checkbox appointment" if invoices.count > 1 %>
<b><%= link_to "##{invoice.doc_number}", invoice_path(invoice), target: :_blank %></b> <%= invoice.txn_date %>
</div>
<% end %>

View File

@@ -1,30 +1,21 @@
function updateLink() {
console.log("updateLink called");
const linkElement = document.getElementById("appointment_link");
const regex = /((?:<br\/>|%3Cbr\/?%3E))([\s\S]*?)(&dates)/gi;
linkElement.href = linkElement.href.replace(regex, `$1${getSelectedDocs()}$3`);
}
function getSelectedDocs() {
const invoices = document.querySelectorAll('.invoice-checkbox');
const estimates = document.querySelectorAll('.estimate-checkbox');
const invoiceIds = Array.from(invoices)
.filter(checkbox => checkbox.checked)
.map(checkbox => checkbox.value);
const estimateIds = Array.from(estimates)
.filter(checkbox => checkbox.checked)
.map(checkbox => checkbox.value);
const appointent_extras = document.querySelectorAll('.appointment');
let output = '';
for (const value of invoiceIds) {
output += `%0A<a href="${window.location.origin}/invoices/${value}">Invoice:%20${value}</a>%0A`;
for (const item of appointent_extras) {
if (item.checked) {
console.log(`Checked item: ${item.dataset.text} with URL: ${item.dataset.url}`);
output += `%0A`+ encodeURIComponent(`<a href="${window.location.origin}${item.dataset.url}">${item.dataset.text}</a>`) +`%0A`;
}
}
for (const value of estimateIds) {
output += `%0A<a href="${window.location.origin}/estimates/${value}">Estimate:%20${value}</a>%0A`;
}
// You can return the array or use it as needed
return output;

View File

@@ -14,7 +14,7 @@ Redmine::Plugin.register :redmine_qbo do
name 'Redmine QBO plugin'
author 'Rick Barrette'
description 'A pluging for Redmine to connect with QuickBooks Online to create Time Activity Entries for billable hours logged when an Issue is closed'
version '2026.2.4'
version '2026.2.5'
url 'https://github.com/rickbarrette/redmine_qbo'
author_url 'https://barrettefabrication.com'
settings default: {empty: true}, partial: 'qbo/settings'

View File

@@ -14,28 +14,41 @@ module RedmineQbo
include IssuesHelper
# Check the new issue form for a valid project.
# Check the new issue form for a valid project.
# This is added to help prevent 422 unprocessable entity errors when creating an issue
# See https://github.com/redmine/redmine/blob/84483d63828d0cb2efbf5bd786a2f0d22e34c93d/app/controllers/issues_controller.rb#L179
def controller_issues_new_before_save(context={})
Rails.logger.debug "RedmineQbo::Hooks::IssuesHookListener.controller_issues_new_before_save: Checking for nil project or tracker"
Rails.logger.debug context[:params].inspect
Rails.logger.debug context[:issue].inspect
Rails.logger.debug context[:issue].project
Rails.logger.debug context[:issue].tracker
error = ""
if context[:issue].project.nil?
context[:issue].project = projects_for_select(context[:issue]).first
context[:issue].project ||= projects_for_select(context[:issue]).first
Rails.logger.error I18n.t(:notice_error_project_nil) + context[:issue].project.to_s
context[:controller].flash[:error] = I18n.t(:notice_error_project_nil) + context[:issue].project.to_s
error = I18n.t(:notice_error_project_nil) + context[:issue].project.to_s
end
if context[:issue].tracker.nil?
context[:issue].tracker = trackers_for_select(context[:issue]).first
context[:issue].tracker ||= trackers_for_select(context[:issue]).first
Rails.logger.error I18n.t(:notice_error_tracker_nil) + context[:issue].tracker.to_s
context[:controller].flash[:error] = I18n.t(:notice_error_tracker_nil) + context[:issue].tracker.to_s
error << "\n"
error << I18n.t(:notice_error_tracker_nil) + context[:issue].tracker.to_s
end
context[:controller].flash[:error] = error unless error.blank?
Rails.logger.debug error unless error.blank?
return context
end
# Edit Issue Form
# Here we build the required form components before passing them to a partial view formatting.
def view_issues_form_details_bottom(context={})
Rails.logger.debug "RedmineQbo::Hooks::IssuesHookListener.view_issues_form_details_bottom: Building form components for quickbooks customer, estimate, and invoice data"
Rails.logger.debug context[:issue].inspect
f = context[:form]
issue = context[:issue]
@@ -49,11 +62,14 @@ module RedmineQbo
value: '#issue_customer'
}
js_path = "updateIssueFrom('/issues/new.js', this)"
js_path = "updateIssueFrom('#{escape_javascript update_issue_form_path(issue.project, issue)}', this)" unless issue.new_record?
# This hidden field is used for the customer ID for the issue
# the onchange event will reload the issue form via ajax to update the available estimates
customer_id = f.hidden_field :customer_id,
id: "issue_customer_id",
onchange: "updateIssueFrom('#{escape_javascript update_issue_form_path(issue.project, issue)}', this)".html_safe
onchange: js_path.html_safe
# Generate the drop down list of quickbooks estimates owned by the selected customer
select_estimate = f.select :estimate_id,