GnuCash c935c2f+
Loading...
Searching...
No Matches
dialog-job.c
1/*
2 * dialog-job.c -- Dialog for Job entry
3 * Copyright (C) 2001, 2002 Derek Atkins
4 * Author: Derek Atkins <warlord@MIT.EDU>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of
9 * the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, contact:
18 *
19 * Free Software Foundation Voice: +1-617-542-5942
20 * 51 Franklin Street, Fifth Floor Fax: +1-617-542-2652
21 * Boston, MA 02110-1301, USA gnu@gnu.org
22 */
23
24#include <config.h>
25
26#include <gtk/gtk.h>
27#include <glib/gi18n.h>
28
29#include "dialog-utils.h"
30#include "gnc-amount-edit.h"
31#include "gnc-component-manager.h"
32#include "gnc-ui.h"
33#include "gnc-gui-query.h"
34#include "qof.h"
35#include "dialog-search.h"
36#include "search-param.h"
37
38#include "gncBusiness.h"
39#include "gncJob.h"
40#include "gncJobP.h"
41
42#include "business-gnome-utils.h"
43#include "dialog-job.h"
44#include "dialog-invoice.h"
45#include "dialog-payment.h"
46
47#define DIALOG_NEW_JOB_CM_CLASS "dialog-new-job"
48#define DIALOG_EDIT_JOB_CM_CLASS "dialog-edit-job"
49
50#define GNC_PREFS_GROUP_SEARCH "dialogs.business.job-search"
51
52void gnc_job_window_ok_cb (GtkWidget *widget, gpointer data);
53void gnc_job_window_cancel_cb (GtkWidget *widget, gpointer data);
54void gnc_job_window_help_cb (GtkWidget *widget, gpointer data);
55void gnc_job_window_destroy_cb (GtkWidget *widget, gpointer data);
56void gnc_job_name_changed_cb (GtkWidget *widget, gpointer data);
57
58typedef enum
59{
60 NEW_JOB,
61 EDIT_JOB
62} JobDialogType;
63
65{
66 QofBook *book;
67 GncOwner *owner;
68 QofQuery *q;
69 GncOwner owner_def;
70};
71
73{
74 GtkWidget * dialog;
75 GtkWidget * id_entry;
76 GtkWidget * cust_edit;
77 GtkWidget * name_entry;
78 GtkWidget * desc_entry;
79 GtkWidget * rate_entry;
80 GtkWidget * active_check;
81
82 JobDialogType dialog_type;
83 GncGUID job_guid;
84 gint component_id;
85 QofBook * book;
86 GncJob * created_job;
87
88 GncOwner owner;
89
90};
91
92static GncJob *
93jw_get_job (JobWindow *jw)
94{
95 if (!jw)
96 return NULL;
97
98 return gncJobLookup (jw->book, &jw->job_guid);
99}
100
101static void gnc_ui_to_job (JobWindow *jw, GncJob *job)
102{
103 gnc_suspend_gui_refresh ();
104 gncJobBeginEdit (job);
105
106 qof_event_gen(QOF_INSTANCE(job), QOF_EVENT_ADD, NULL);
107
108 gncJobSetID (job, gtk_entry_get_text (GTK_ENTRY (jw->id_entry)));
109 gncJobSetName (job, gtk_entry_get_text (GTK_ENTRY (jw->name_entry)));
110 gncJobSetReference (job, gtk_entry_get_text (GTK_ENTRY (jw->desc_entry)));
111 gncJobSetRate (job, gnc_amount_edit_get_amount
112 (GNC_AMOUNT_EDIT (jw->rate_entry)));
113 gncJobSetActive (job, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
114 (jw->active_check)));
115 {
116 GncOwner * old = gncJobGetOwner (job);
117 gnc_owner_get_owner (jw->cust_edit, &(jw->owner));
118 if (! gncOwnerEqual (old, &(jw->owner)))
119 gncJobSetOwner (job, &(jw->owner));
120 }
121
122 gncJobCommitEdit (job);
123 gnc_resume_gui_refresh ();
124}
125
126static gboolean
127gnc_job_verify_ok (JobWindow *jw)
128{
129 const char *res;
130 gchar *string;
131
132 /* Check for valid name */
133 res = gtk_entry_get_text (GTK_ENTRY (jw->name_entry));
134 if (g_strcmp0 (res, "") == 0)
135 {
136 const char *message = _("The Job must be given a name.");
137 gnc_error_dialog (GTK_WINDOW (jw->dialog), "%s", message);
138 return FALSE;
139 }
140
141 /* Check for owner */
142 gnc_owner_get_owner (jw->cust_edit, &(jw->owner));
143 res = gncOwnerGetName (&(jw->owner));
144 if (res == NULL || g_strcmp0 (res, "") == 0)
145 {
146 const char *message = _("You must choose an owner for this job.");
147 gnc_error_dialog (GTK_WINDOW (jw->dialog), "%s", message);
148 return FALSE;
149 }
150
151 /* Check for valid rate */
152 if (!gnc_amount_edit_evaluate (GNC_AMOUNT_EDIT(jw->rate_entry), NULL))
153 {
154 const char *message = _("The rate amount must be valid or you must leave it blank.");
155 gnc_error_dialog (GTK_WINDOW (jw->dialog), "%s", message);
156 return FALSE;
157 }
158
159 /* Set a valid id if one was not created */
160 res = gtk_entry_get_text (GTK_ENTRY (jw->id_entry));
161 if (g_strcmp0 (res, "") == 0)
162 {
163 string = gncJobNextID(jw->book);
164 gtk_entry_set_text (GTK_ENTRY (jw->id_entry), string);
165 g_free(string);
166 }
167
168 /* Now save it off */
169 {
170 GncJob *job = jw_get_job (jw);
171 if (job)
172 {
173 gnc_ui_to_job (jw, job);
174 }
175 }
176
177 /* Ok, it's been saved... Change to an editor.. */
178 jw->dialog_type = EDIT_JOB;
179
180 return TRUE;
181}
182
183void
184gnc_job_window_ok_cb (GtkWidget *widget, gpointer data)
185{
186 JobWindow *jw = data;
187
188 /* Make sure this is ok */
189 if (!gnc_job_verify_ok (jw))
190 return;
191
192 /* Now save off the job so we can return it */
193 jw->created_job = jw_get_job (jw);
194 jw->job_guid = *guid_null ();
195
196 gnc_close_gui_component (jw->component_id);
197}
198
199void
200gnc_job_window_cancel_cb (GtkWidget *widget, gpointer data)
201{
202 JobWindow *jw = data;
203
204 gnc_close_gui_component (jw->component_id);
205}
206
207void
208gnc_job_window_help_cb (GtkWidget *widget, gpointer data)
209{
210 JobWindow *jw = data;
211 gnc_gnome_help (GTK_WINDOW(jw->dialog), DF_MANUAL, DL_USAGE_JOB);
212}
213
214
215void
216gnc_job_window_destroy_cb (GtkWidget *widget, gpointer data)
217{
218 JobWindow *jw = data;
219 GncJob *job = jw_get_job (jw);
220
221 gnc_suspend_gui_refresh ();
222
223 if (jw->dialog_type == NEW_JOB && job != NULL)
224 {
225 gncJobBeginEdit (job);
226 gncJobDestroy (job);
227 jw->job_guid = *guid_null ();
228 }
229
230 gnc_unregister_gui_component (jw->component_id);
231 gnc_resume_gui_refresh ();
232
233 g_free (jw);
234}
235
236void
237gnc_job_name_changed_cb (GtkWidget *widget, gpointer data)
238{
239 JobWindow *jw = data;
240 if (!jw)
241 return;
242
243 gnc_owner_window_set_title (GTK_WINDOW (jw->dialog),
244 (jw->dialog_type == EDIT_JOB) ?
245 _("Edit Job") : _("New Job"),
246 jw->name_entry, jw->id_entry);
247}
248
249static void
250gnc_job_window_close_handler (gpointer user_data)
251{
252 JobWindow *jw = user_data;
253
254 gtk_widget_destroy (jw->dialog);
255 /* jw is already freed at this point
256 jw->dialog = NULL; */
257}
258
259static void
260gnc_job_window_refresh_handler (GHashTable *changes, gpointer user_data)
261{
262 JobWindow *jw = user_data;
263 const EventInfo *info;
264 GncJob *job = jw_get_job (jw);
265
266 /* If there isn't a job behind us, close down */
267 if (!job)
268 {
269 gnc_close_gui_component (jw->component_id);
270 return;
271 }
272
273 /* Next, close if this is a destroy event */
274 if (changes)
275 {
276 info = gnc_gui_get_entity_events (changes, &jw->job_guid);
277 if (info && (info->event_mask & QOF_EVENT_DESTROY))
278 {
279 gnc_close_gui_component (jw->component_id);
280 return;
281 }
282 }
283}
284
285static gboolean
286find_handler (gpointer find_data, gpointer user_data)
287{
288 const GncGUID *job_guid = find_data;
289 JobWindow *jw = user_data;
290
291 return(jw && guid_equal(&jw->job_guid, job_guid));
292}
293
294static JobWindow *
295gnc_job_new_window (GtkWindow *parent, QofBook *bookp, GncOwner *owner, GncJob *job)
296{
297 JobWindow *jw;
298 GtkBuilder *builder;
299 GtkWidget *owner_box, *owner_label, *edit, *hbox;
300
301 /*
302 * Find an existing window for this job. If found, bring it to
303 * the front.
304 */
305 if (job)
306 {
307 GncGUID job_guid;
308
309 job_guid = *gncJobGetGUID (job);
310 jw = gnc_find_first_gui_component (DIALOG_EDIT_JOB_CM_CLASS,
311 find_handler, &job_guid);
312 if (jw)
313 {
314 gtk_window_set_transient_for (GTK_WINDOW(jw->dialog), parent);
315 gtk_window_present (GTK_WINDOW(jw->dialog));
316 return(jw);
317 }
318 }
319
320 /*
321 * No existing job window found. Build a new one.
322 */
323 jw = g_new0 (JobWindow, 1);
324 jw->book = bookp;
325 gncOwnerCopy (owner, &(jw->owner)); /* save it off now, we know it's valid */
326
327 /* Load the Glade File */
328 builder = gtk_builder_new();
329 gnc_builder_add_from_file (builder, "dialog-job.glade", "job_dialog");
330
331 /* Find the dialog */
332 jw->dialog = GTK_WIDGET(gtk_builder_get_object (builder, "job_dialog"));
333 gtk_window_set_transient_for (GTK_WINDOW(jw->dialog), parent);
334
335 // Set the name for this dialog so it can be easily manipulated with css
336 gtk_widget_set_name (GTK_WIDGET(jw->dialog), "gnc-id-job");
337 gnc_widget_style_context_add_class (GTK_WIDGET(jw->dialog), "gnc-class-jobs");
338
339 /* Get entry points */
340 jw->id_entry = GTK_WIDGET(gtk_builder_get_object (builder, "id_entry"));
341 jw->name_entry = GTK_WIDGET(gtk_builder_get_object (builder, "name_entry"));
342 jw->desc_entry = GTK_WIDGET(gtk_builder_get_object (builder, "desc_entry"));
343 jw->active_check = GTK_WIDGET(gtk_builder_get_object (builder, "active_check"));
344
345 owner_box = GTK_WIDGET(gtk_builder_get_object (builder, "customer_hbox"));
346 owner_label = GTK_WIDGET(gtk_builder_get_object (builder, "owner_label"));
347
348 edit = gnc_amount_edit_new();
349 gnc_amount_edit_set_evaluate_on_enter (GNC_AMOUNT_EDIT (edit), TRUE);
350
351 jw->rate_entry = edit;
352 gtk_widget_show (edit);
353
354 hbox = GTK_WIDGET(gtk_builder_get_object (builder, "rate_entry"));
355 gtk_box_pack_start (GTK_BOX (hbox), edit, TRUE, TRUE, 0);
356
357 /* Setup signals */
358 gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, jw);
359
360
361 /* Set initial entries */
362 if (job != NULL)
363 {
364 jw->job_guid = *gncJobGetGUID (job);
365
366 jw->dialog_type = EDIT_JOB;
367 jw->cust_edit = gnc_owner_edit_create (owner_label, owner_box,
368 bookp, owner);
369
370 gtk_entry_set_text (GTK_ENTRY (jw->id_entry), gncJobGetID (job));
371 gtk_entry_set_text (GTK_ENTRY (jw->name_entry), gncJobGetName (job));
372 gtk_entry_set_text (GTK_ENTRY (jw->desc_entry), gncJobGetReference (job));
373 gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT (jw->rate_entry),
374 gncJobGetRate (job));
375 gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (jw->active_check),
376 gncJobGetActive (job));
377
378 jw->component_id = gnc_register_gui_component (DIALOG_EDIT_JOB_CM_CLASS,
379 gnc_job_window_refresh_handler,
380 gnc_job_window_close_handler,
381 jw);
382 }
383 else
384 {
385 job = gncJobCreate (bookp);
386 gncJobSetOwner (job, owner);
387 jw->job_guid = *gncJobGetGUID (job);
388
389 jw->dialog_type = NEW_JOB;
390
391 /* If we are passed a real owner, don't allow the user to change it */
392 if (owner->owner.undefined)
393 {
394 jw->cust_edit = gnc_owner_edit_create (owner_label, owner_box,
395 bookp, owner);
396 }
397 else
398 {
399 jw->cust_edit = gnc_owner_select_create (owner_label, owner_box,
400 bookp, owner);
401 }
402
403 jw->component_id = gnc_register_gui_component (DIALOG_NEW_JOB_CM_CLASS,
404 gnc_job_window_refresh_handler,
405 gnc_job_window_close_handler,
406 jw);
407 }
408
409 gnc_job_name_changed_cb (NULL, jw);
410 gnc_gui_component_watch_entity_type (jw->component_id,
411 GNC_JOB_MODULE_NAME,
412 QOF_EVENT_MODIFY | QOF_EVENT_DESTROY);
413
414 gtk_widget_show_all (jw->dialog);
415
416 // The job name should have keyboard focus
417 gtk_widget_grab_focus(jw->name_entry);
418 // Or should the owner field have focus?
419// if (GNC_IS_GENERAL_SEARCH(jw->cust_edit))
420// {
421// gnc_general_search_grab_focus(GNC_GENERAL_SEARCH(jw->cust_edit));
422// }
423
424 g_object_unref(G_OBJECT(builder));
425
426 return jw;
427}
428
429JobWindow *
430gnc_ui_job_new (GtkWindow *parent, GncOwner *ownerp, QofBook *bookp)
431{
432 JobWindow *jw;
433 GncOwner owner;
434
435 /* Make sure required options exist */
436 if (!bookp) return NULL;
437
438 if (ownerp)
439 {
440 g_return_val_if_fail ((gncOwnerGetType (ownerp) == GNC_OWNER_CUSTOMER) ||
441 (gncOwnerGetType (ownerp) == GNC_OWNER_VENDOR),
442 NULL);
443 gncOwnerCopy (ownerp, &owner);
444 }
445 else
446 gncOwnerInitCustomer (&owner, NULL); /* XXX */
447
448 jw = gnc_job_new_window (parent, bookp, &owner, NULL);
449 return jw;
450}
451
452JobWindow *
453gnc_ui_job_edit (GtkWindow *parent, GncJob *job)
454{
455 JobWindow *jw;
456
457 if (!job) return NULL;
458
459 jw = gnc_job_new_window (parent, gncJobGetBook(job), gncJobGetOwner(job), job);
460 return jw;
461}
462
463/* Search functionality */
464
465static void
466edit_job_cb (GtkWindow *dialog, gpointer *job_p, gpointer user_data)
467{
468 GncJob *job;
469
470 g_return_if_fail (job_p && user_data);
471
472 job = *job_p;
473
474 if (!job)
475 return;
476
477 gnc_ui_job_edit (dialog, job);
478}
479
480static void
481invoice_job_cb (GtkWindow *dialog, gpointer *job_p, gpointer user_data)
482{
483 struct _job_select_window * sw = user_data;
484 GncJob *job;
485 GncOwner owner;
486
487 g_return_if_fail (job_p && user_data);
488
489 job = *job_p;
490 if (!job)
491 return;
492
493 gncOwnerInitJob (&owner, job);
494 gnc_invoice_search (dialog, NULL, &owner, sw->book);
495}
496
497static void
498payment_job_cb (GtkWindow *dialog, gpointer *job_p, gpointer user_data)
499{
500 struct _job_select_window *sw = user_data;
501 GncOwner owner;
502 GncJob *job;
503
504 g_return_if_fail (job_p && user_data);
505
506 job = *job_p;
507
508 if (!job)
509 return;
510
511 gncOwnerInitJob (&owner, job);
512 gnc_ui_payment_new (dialog, &owner, sw->book);
513 return;
514}
515
516static gpointer
517new_job_cb (GtkWindow *dialog, gpointer user_data)
518{
519 struct _job_select_window *sw = user_data;
520 JobWindow *jw;
521
522 g_return_val_if_fail (user_data, NULL);
523
524 jw = gnc_ui_job_new (dialog, sw->owner, sw->book);
525 return jw_get_job (jw);
526}
527
528static void
529free_userdata_cb (gpointer user_data)
530{
531 struct _job_select_window *sw = user_data;
532
533 g_return_if_fail (sw);
534
535 qof_query_destroy (sw->q);
536 g_free (sw);
537}
538
539GNCSearchWindow *
540gnc_job_search (GtkWindow *parent, GncJob *start, GncOwner *owner, QofBook *book)
541{
542 QofQuery *q, *q2 = NULL;
543 QofIdType type = GNC_JOB_MODULE_NAME;
544 struct _job_select_window *sw;
545 static GList *params = NULL;
546 static GList *columns = NULL;
547 static GNCSearchCallbackButton buttons[] =
548 {
549 { N_("View/Edit Job"), edit_job_cb, NULL, TRUE},
550 { N_("View Invoices"), invoice_job_cb, NULL, TRUE},
551 { N_("Process Payment"), payment_job_cb, NULL, FALSE},
552 { NULL },
553 };
554
555 g_return_val_if_fail (book, NULL);
556
557 /* Build parameter list in reverse order */
558 if (params == NULL)
559 {
560 params = gnc_search_param_prepend (params, _("Owner's Name"), NULL, type,
561 JOB_OWNER, OWNER_NAME, NULL);
562 params = gnc_search_param_prepend (params, _("Only Active?"), NULL, type,
563 JOB_ACTIVE, NULL);
564 params = gnc_search_param_prepend (params, _("Billing ID"), NULL, type,
565 JOB_REFERENCE, NULL);
566 params = gnc_search_param_prepend (params, _("Rate"), NULL, type,
567 JOB_RATE, NULL);
568 params = gnc_search_param_prepend (params, _("Job Number"), NULL, type,
569 JOB_ID, NULL);
570 params = gnc_search_param_prepend (params, _("Job Name"), NULL, type,
571 JOB_NAME, NULL);
572 }
573
574 /* Build the column list in reverse order */
575 if (columns == NULL)
576 {
577 columns = gnc_search_param_prepend (columns, _("Billing ID"), NULL, type,
578 JOB_REFERENCE, NULL);
579 columns = gnc_search_param_prepend (columns, _("Rate"), NULL, type,
580 JOB_RATE, NULL);
581 columns = gnc_search_param_prepend (columns, _("Company"), NULL, type,
582 JOB_OWNER, OWNER_NAME, NULL);
583 columns = gnc_search_param_prepend (columns, _("Job Name"), NULL, type,
584 JOB_NAME, NULL);
585 columns = gnc_search_param_prepend (columns, _("ID #"), NULL, type,
586 JOB_ID, NULL);
587 }
588
589 /* Build the queries */
590 q = qof_query_create_for (type);
591 qof_query_set_book (q, book);
592
593 /* If we have a start job but, for some reason, not an owner -- grab
594 * the owner from the starting job.
595 */
596 if ((!owner || !gncOwnerGetGUID (owner)) && start)
597 owner = gncJobGetOwner (start);
598
599 /* If owner is supplied, limit all searches to invoices who's owner
600 * is the supplied owner! Show all invoices by this owner.
601 */
602 if (owner && gncOwnerGetGUID (owner))
603 {
604 qof_query_add_guid_match (q, g_slist_prepend
605 (g_slist_prepend (NULL, QOF_PARAM_GUID),
606 JOB_OWNER),
607 gncOwnerGetGUID (owner), QOF_QUERY_AND);
608
609 q2 = qof_query_copy (q);
610 }
611
612#if 0
613 if (start)
614 {
615 if (q2 == NULL)
616 q2 = qof_query_copy (q);
617
618 qof_query_add_guid_match (q2, g_slist_prepend (NULL, QOF_PARAM_GUID),
619 gncJobGetGUID (start), QOF_QUERY_AND);
620 }
621#endif
622
623 /* launch select dialog and return the result */
624 sw = g_new0 (struct _job_select_window, 1);
625
626 if (owner)
627 {
628 gncOwnerCopy (owner, &(sw->owner_def));
629 sw->owner = &(sw->owner_def);
630 }
631 sw->book = book;
632 sw->q = q;
633
634 return gnc_search_dialog_create (parent, type, _("Find Job"),
635 params, columns, q, q2, buttons, NULL,
636 new_job_cb, sw, free_userdata_cb,
637 GNC_PREFS_GROUP_SEARCH, NULL,
638 "gnc-class-jobs");
639}
640
641/* Functions for widgets for job selection */
642
643GNCSearchWindow *
644gnc_job_search_select (GtkWindow *parent, gpointer start, gpointer book)
645{
646 GncJob *j = start;
647 GncOwner owner, *ownerp;
648
649 if (!book) return NULL;
650
651 if (j)
652 {
653 ownerp = gncJobGetOwner (j);
654 gncOwnerCopy (ownerp, &owner);
655 }
656 else
657 gncOwnerInitCustomer (&owner, NULL); /* XXX */
658
659 return gnc_job_search (parent, start, &owner, book);
660}
661
662GNCSearchWindow *
663gnc_job_search_edit (GtkWindow *parent, gpointer start, gpointer book)
664{
665 if (start)
666 gnc_ui_job_edit (parent, start);
667
668 return NULL;
669}
-- Business Helper Functions
Job Interface.
const gchar * QofIdType
QofIdType declaration.
Definition qofid.h:80
void qof_event_gen(QofInstance *entity, QofEventId event_id, gpointer event_data)
Invoke all registered event handlers using the given arguments.
Definition qofevent.cpp:231
const GncGUID * guid_null(void)
Returns a GncGUID which is guaranteed to never reference any entity.
Definition guid.cpp:165
gboolean guid_equal(const GncGUID *guid_1, const GncGUID *guid_2)
Given two GUIDs, return TRUE if they are non-NULL and equal.
Definition guid.cpp:237
void gnc_gnome_help(GtkWindow *parent, const char *file_name, const char *anchor)
Launch the systems default help browser, gnome's yelp for linux, and open to a given link within a gi...
#define gncJobGetBook(x)
deprecated functions
Definition gncJob.h:117
gboolean gncOwnerEqual(const GncOwner *a, const GncOwner *b)
Assess equality by checking.
Definition gncOwner.c:404
const GncGUID * gncOwnerGetGUID(const GncOwner *owner)
Get the GncGUID of the immediate owner.
Definition gncOwner.c:518
GncOwnerType gncOwnerGetType(const GncOwner *owner)
Returns the GncOwnerType of this owner.
Definition gncOwner.c:200
void qof_query_add_guid_match(QofQuery *q, QofQueryParamList *param_list, const GncGUID *guid, QofQueryOp op)
DOCUMENT ME !!
QofQuery * qof_query_copy(QofQuery *q)
Make a copy of the indicated query.
void qof_query_set_book(QofQuery *query, QofBook *book)
Set the book to be searched.
void qof_query_destroy(QofQuery *query)
Frees the resources associate with a Query object.
The type used to store guids in C.
Definition guid.h:75
QofBook reference.
Definition qofbook-p.hpp:47
A Query.
Definition qofquery.cpp:75