Compare commits

..

14 Commits

7 changed files with 48 additions and 33 deletions

View File

@@ -3,7 +3,7 @@
<br/>
<br/>
<%= link_to t(:label_create_estimate), "https://qbo.intuit.com/app/estimate?nameId=#{@customer.id}", target: :_blank, method: :get %>
<%= link_to t(:label_create_estimate), "https://qbo.intuit.com/app/estimate?nameId=#{@customer.id}", target: :_blank %>
<br/>
<br/>

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,31 +1,22 @@
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 value of estimateIds) {
output += `%0A<a href="${window.location.origin}/estimates/${value}">Estimate:%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`;
}
}
// You can return the array or use it as needed
return output;
}

View File

@@ -100,4 +100,5 @@ en:
notice_forbidden: "You do not have permission to access this resource"
notice_issue_not_found: "Issue not found"
customer_details: "Customer Details"
notice_error_project_nil: "The issue's project is nil, set project to: "
notice_error_project_nil: "The issue's project is nil, set project to: "
notice_error_tracker_nil: "The issue's tracker is nil, set tracker to: "

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.3
version '2026.2.6'
url 'https://github.com/rickbarrette/redmine_qbo'
author_url 'https://barrettefabrication.com'
settings default: {empty: true}, partial: 'qbo/settings'

View File

@@ -14,21 +14,41 @@ module RedmineQbo
include IssuesHelper
# 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={})
if context[:issue].project.nil?
context[:issue].project = projects_for_select(context[:issue]).first
context[:controller].flash[:error] = I18n.t(:notice_error_project_nil) + context[:issue].project.to_s
end
# 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={})
return context
end
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
Rails.logger.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
Rails.logger.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]
@@ -42,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,