Compare commits

...

14 Commits

8 changed files with 31 additions and 31 deletions

View File

@@ -3,7 +3,7 @@
<br/> <br/>
<br/> <br/>
<%= button_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/>
<br/> <br/>

View File

@@ -2,7 +2,7 @@
<% estimates.sort.reverse.each do |estimate| %> <% estimates.sort.reverse.each do |estimate| %>
<div class="row"> <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 %> <b><%= link_to "##{estimate.doc_number}", estimate_path(estimate), target: :_blank %></b> <%= estimate.txn_date %>
</div> </div>
<% end %> <% end %>

View File

@@ -12,7 +12,7 @@
<% invoices.sort.reverse.each do |invoice| %> <% invoices.sort.reverse.each do |invoice| %>
<div class="row"> <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 %> <b><%= link_to "##{invoice.doc_number}", invoice_path(invoice), target: :_blank %></b> <%= invoice.txn_date %>
</div> </div>
<% end %> <% end %>

View File

@@ -1,31 +1,22 @@
function updateLink() { function updateLink() {
console.log("updateLink called");
const linkElement = document.getElementById("appointment_link"); const linkElement = document.getElementById("appointment_link");
const regex = /((?:<br\/>|%3Cbr\/?%3E))([\s\S]*?)(&dates)/gi; const regex = /((?:<br\/>|%3Cbr\/?%3E))([\s\S]*?)(&dates)/gi;
linkElement.href = linkElement.href.replace(regex, `$1${getSelectedDocs()}$3`); linkElement.href = linkElement.href.replace(regex, `$1${getSelectedDocs()}$3`);
} }
function getSelectedDocs() { function getSelectedDocs() {
const invoices = document.querySelectorAll('.invoice-checkbox'); const appointent_extras = document.querySelectorAll('.appointment');
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);
let output = ''; let output = '';
for (const item of appointent_extras) {
for (const value of invoiceIds) { if (item.checked) {
output += `%0A<a href="${window.location.origin}/invoices/${value}">Invoice:%20${value}</a>%0A`; 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 // You can return the array or use it as needed
return output; return output;
} }

View File

@@ -100,4 +100,5 @@ en:
notice_forbidden: "You do not have permission to access this resource" notice_forbidden: "You do not have permission to access this resource"
notice_issue_not_found: "Issue not found" notice_issue_not_found: "Issue not found"
customer_details: "Customer Details" 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' name 'Redmine QBO plugin'
author 'Rick Barrette' 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' 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.2' version '2026.2.5'
url 'https://github.com/rickbarrette/redmine_qbo' url 'https://github.com/rickbarrette/redmine_qbo'
author_url 'https://barrettefabrication.com' author_url 'https://barrettefabrication.com'
settings default: {empty: true}, partial: 'qbo/settings' settings default: {empty: true}, partial: 'qbo/settings'

View File

@@ -15,16 +15,23 @@ module RedmineQbo
include IssuesHelper 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 # 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 # See https://github.com/redmine/redmine/blob/84483d63828d0cb2efbf5bd786a2f0d22e34c93d/app/controllers/issues_controller.rb#L179
def controller_issues_new_before_save(context={}) def controller_issues_new_before_save(context={})
if context[:issue].project.nil? if context[:issue].project.nil?
context[:issue].project = projects_for_select(context[:issue]).first context[:issue].project = projects_for_select(context[:issue]).first
context[:controller].flash[:error] = I18n.t(:notice_error_project_nil) + context[:issue].project.to_s Rails.logger.error I18n.t(:notice_error_project_nil) + context[:issue].project.to_s
end context[:controller].flash[:error] = I18n.t(:notice_error_project_nil) + context[:issue].project.to_s
end
return context if context[:issue].tracker.nil?
end 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
end
return context
end
# Edit Issue Form # Edit Issue Form
# Here we build the required form components before passing them to a partial view formatting. # Here we build the required form components before passing them to a partial view formatting.

View File

@@ -15,6 +15,7 @@ module RedmineQbo
module Helper module Helper
def watcher_link(issue, user) def watcher_link(issue, user)
link = ''
link = link_to(I18n.t(:label_bill_time), bill_path( issue.id ), method: :get, class: 'icon icon-email-add') if user.admin? link = link_to(I18n.t(:label_bill_time), bill_path( issue.id ), method: :get, class: 'icon icon-email-add') if user.admin?
link << link_to(I18n.t(:label_share), share_path( issue.id ), method: :get, target: :_blank, class: 'icon icon-shared') if user.logged? link << link_to(I18n.t(:label_share), share_path( issue.id ), method: :get, target: :_blank, class: 'icon icon-shared') if user.logged?
link.html_safe + super link.html_safe + super