GnuCash  2.6.99
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
dialog-job.c
1 /*
2  * dialog-job.c -- Dialog for Job entry
3  * Copyright (C) 2001, 2002 Derek Atkins
4  * Author: Derek Atkins <[email protected]>
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 [email protected]
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 
52 void gnc_job_window_ok_cb (GtkWidget *widget, gpointer data);
53 void gnc_job_window_cancel_cb (GtkWidget *widget, gpointer data);
54 void gnc_job_window_help_cb (GtkWidget *widget, gpointer data);
55 void gnc_job_window_destroy_cb (GtkWidget *widget, gpointer data);
56 void gnc_job_name_changed_cb (GtkWidget *widget, gpointer data);
57 
58 typedef 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 
92 static GncJob *
93 jw_get_job (JobWindow *jw)
94 {
95  if (!jw)
96  return NULL;
97 
98  return gncJobLookup (jw->book, &jw->job_guid);
99 }
100 
101 static 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_editable_get_chars (GTK_EDITABLE (jw->id_entry),
109  0, -1));
110  gncJobSetName (job, gtk_editable_get_chars (GTK_EDITABLE (jw->name_entry),
111  0, -1));
112  gncJobSetReference (job, gtk_editable_get_chars
113  (GTK_EDITABLE (jw->desc_entry), 0, -1));
114  gncJobSetRate (job, gnc_amount_edit_get_amount
115  (GNC_AMOUNT_EDIT (jw->rate_entry)));
116  gncJobSetActive (job, gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON
117  (jw->active_check)));
118  {
119  GncOwner * old = gncJobGetOwner (job);
120  gnc_owner_get_owner (jw->cust_edit, &(jw->owner));
121  if (! gncOwnerEqual (old, &(jw->owner)))
122  gncJobSetOwner (job, &(jw->owner));
123  }
124 
125  gncJobCommitEdit (job);
126  gnc_resume_gui_refresh ();
127 }
128 
129 static gboolean
130 gnc_job_verify_ok (JobWindow *jw)
131 {
132  const char *res;
133  gchar *string;
134 
135  /* Check for valid name */
136  res = gtk_entry_get_text (GTK_ENTRY (jw->name_entry));
137  if (g_strcmp0 (res, "") == 0)
138  {
139  const char *message = _("The Job must be given a name.");
140  gnc_error_dialog(jw->dialog, "%s", message);
141  return FALSE;
142  }
143 
144  /* Check for owner */
145  gnc_owner_get_owner (jw->cust_edit, &(jw->owner));
146  res = gncOwnerGetName (&(jw->owner));
147  if (res == NULL || g_strcmp0 (res, "") == 0)
148  {
149  const char *message = _("You must choose an owner for this job.");
150  gnc_error_dialog(jw->dialog, "%s", message);
151  return FALSE;
152  }
153 
154  /* Set a valid id if one was not created */
155  res = gtk_entry_get_text (GTK_ENTRY (jw->id_entry));
156  if (g_strcmp0 (res, "") == 0)
157  {
158  string = gncJobNextID(jw->book);
159  gtk_entry_set_text (GTK_ENTRY (jw->id_entry), string);
160  g_free(string);
161  }
162 
163  /* Now save it off */
164  {
165  GncJob *job = jw_get_job (jw);
166  if (job)
167  {
168  gnc_ui_to_job (jw, job);
169  }
170  }
171 
172  /* Ok, it's been saved... Change to an editor.. */
173  jw->dialog_type = EDIT_JOB;
174 
175  return TRUE;
176 }
177 
178 void
179 gnc_job_window_ok_cb (GtkWidget *widget, gpointer data)
180 {
181  JobWindow *jw = data;
182 
183  /* Make sure this is ok */
184  if (!gnc_job_verify_ok (jw))
185  return;
186 
187  /* Now save off the job so we can return it */
188  jw->created_job = jw_get_job (jw);
189  jw->job_guid = *guid_null ();
190 
191  gnc_close_gui_component (jw->component_id);
192 }
193 
194 void
195 gnc_job_window_cancel_cb (GtkWidget *widget, gpointer data)
196 {
197  JobWindow *jw = data;
198 
199  gnc_close_gui_component (jw->component_id);
200 }
201 
202 void
203 gnc_job_window_help_cb (GtkWidget *widget, gpointer data)
204 {
205  gnc_gnome_help(HF_HELP, HL_USAGE_BSNSS);
206 }
207 
208 
209 void
210 gnc_job_window_destroy_cb (GtkWidget *widget, gpointer data)
211 {
212  JobWindow *jw = data;
213  GncJob *job = jw_get_job (jw);
214 
215  gnc_suspend_gui_refresh ();
216 
217  if (jw->dialog_type == NEW_JOB && job != NULL)
218  {
219  gncJobBeginEdit (job);
220  gncJobDestroy (job);
221  jw->job_guid = *guid_null ();
222  }
223 
224  gnc_unregister_gui_component (jw->component_id);
225  gnc_resume_gui_refresh ();
226 
227  g_free (jw);
228 }
229 
230 void
231 gnc_job_name_changed_cb (GtkWidget *widget, gpointer data)
232 {
233  JobWindow *jw = data;
234  char *fullname, *title;
235  const char *name, *id;
236 
237  if (!jw)
238  return;
239 
240  name = gtk_entry_get_text (GTK_ENTRY (jw->name_entry));
241  if (!name || *name == '\0')
242  name = _("<No name>");
243 
244  id = gtk_entry_get_text (GTK_ENTRY (jw->id_entry));
245 
246  fullname = g_strconcat (name, " (", id, ")", (char *)NULL);
247 
248  if (jw->dialog_type == EDIT_JOB)
249  title = g_strconcat (_("Edit Job"), " - ", fullname, (char *)NULL);
250  else
251  title = g_strconcat (_("New Job"), " - ", fullname, (char *)NULL);
252 
253  gtk_window_set_title (GTK_WINDOW (jw->dialog), title);
254 
255  g_free (fullname);
256  g_free (title);
257 }
258 
259 static void
260 gnc_job_window_close_handler (gpointer user_data)
261 {
262  JobWindow *jw = user_data;
263 
264  gtk_widget_destroy (jw->dialog);
265  /* jw is already freed at this point
266  jw->dialog = NULL; */
267 }
268 
269 static void
270 gnc_job_window_refresh_handler (GHashTable *changes, gpointer user_data)
271 {
272  JobWindow *jw = user_data;
273  const EventInfo *info;
274  GncJob *job = jw_get_job (jw);
275 
276  /* If there isn't a job behind us, close down */
277  if (!job)
278  {
279  gnc_close_gui_component (jw->component_id);
280  return;
281  }
282 
283  /* Next, close if this is a destroy event */
284  if (changes)
285  {
286  info = gnc_gui_get_entity_events (changes, &jw->job_guid);
287  if (info && (info->event_mask & QOF_EVENT_DESTROY))
288  {
289  gnc_close_gui_component (jw->component_id);
290  return;
291  }
292  }
293 }
294 
295 static gboolean
296 find_handler (gpointer find_data, gpointer user_data)
297 {
298  const GncGUID *job_guid = find_data;
299  JobWindow *jw = user_data;
300 
301  return(jw && guid_equal(&jw->job_guid, job_guid));
302 }
303 
304 static JobWindow *
305 gnc_job_new_window (QofBook *bookp, GncOwner *owner, GncJob *job)
306 {
307  JobWindow *jw;
308  GtkBuilder *builder;
309  GtkWidget *owner_box, *owner_label, *edit, *hbox;
310 
311  /*
312  * Find an existing window for this job. If found, bring it to
313  * the front.
314  */
315  if (job)
316  {
317  GncGUID job_guid;
318 
319  job_guid = *gncJobGetGUID (job);
320  jw = gnc_find_first_gui_component (DIALOG_EDIT_JOB_CM_CLASS,
321  find_handler, &job_guid);
322  if (jw)
323  {
324  gtk_window_present (GTK_WINDOW(jw->dialog));
325  return(jw);
326  }
327  }
328 
329  /*
330  * No existing job window found. Build a new one.
331  */
332  jw = g_new0 (JobWindow, 1);
333  jw->book = bookp;
334  gncOwnerCopy (owner, &(jw->owner)); /* save it off now, we know it's valid */
335 
336  /* Load the Glade File */
337  builder = gtk_builder_new();
338  gnc_builder_add_from_file (builder, "dialog-job.glade", "Job Dialog");
339 
340  /* Find the dialog */
341  jw->dialog = GTK_WIDGET(gtk_builder_get_object (builder, "Job Dialog"));
342 
343  /* Get entry points */
344  jw->id_entry = GTK_WIDGET(gtk_builder_get_object (builder, "id_entry"));
345  jw->name_entry = GTK_WIDGET(gtk_builder_get_object (builder, "name_entry"));
346  jw->desc_entry = GTK_WIDGET(gtk_builder_get_object (builder, "desc_entry"));
347  jw->active_check = GTK_WIDGET(gtk_builder_get_object (builder, "active_check"));
348 
349  owner_box = GTK_WIDGET(gtk_builder_get_object (builder, "customer_hbox"));
350  owner_label = GTK_WIDGET(gtk_builder_get_object (builder, "owner_label"));
351 
352  edit = gnc_amount_edit_new();
353  gnc_amount_edit_set_evaluate_on_enter (GNC_AMOUNT_EDIT (edit), TRUE);
354 
355  jw->rate_entry = edit;
356  gtk_widget_show (edit);
357 
358  hbox = GTK_WIDGET(gtk_builder_get_object (builder, "rate_entry"));
359  gtk_box_pack_start (GTK_BOX (hbox), edit, TRUE, TRUE, 0);
360 
361  /* Setup signals */
362  gtk_builder_connect_signals_full (builder, gnc_builder_connect_full_func, jw);
363 
364 
365  /* Set initial entries */
366  if (job != NULL)
367  {
368  jw->job_guid = *gncJobGetGUID (job);
369 
370  jw->dialog_type = EDIT_JOB;
371  jw->cust_edit = gnc_owner_edit_create (owner_label, owner_box,
372  bookp, owner);
373 
374  gtk_entry_set_text (GTK_ENTRY (jw->id_entry), gncJobGetID (job));
375  gtk_entry_set_text (GTK_ENTRY (jw->name_entry), gncJobGetName (job));
376  gtk_entry_set_text (GTK_ENTRY (jw->desc_entry), gncJobGetReference (job));
377  gnc_amount_edit_set_amount (GNC_AMOUNT_EDIT (jw->rate_entry),
378  gncJobGetRate (job));
379  gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (jw->active_check),
380  gncJobGetActive (job));
381 
382  jw->component_id = gnc_register_gui_component (DIALOG_EDIT_JOB_CM_CLASS,
383  gnc_job_window_refresh_handler,
384  gnc_job_window_close_handler,
385  jw);
386  }
387  else
388  {
389  job = gncJobCreate (bookp);
390  gncJobSetOwner (job, owner);
391  jw->job_guid = *gncJobGetGUID (job);
392 
393  jw->dialog_type = NEW_JOB;
394 
395  /* If we are passed a real owner, don't allow the user to change it */
396  if (owner->owner.undefined)
397  {
398  jw->cust_edit = gnc_owner_edit_create (owner_label, owner_box,
399  bookp, owner);
400  }
401  else
402  {
403  jw->cust_edit = gnc_owner_select_create (owner_label, owner_box,
404  bookp, owner);
405  }
406 
407  jw->component_id = gnc_register_gui_component (DIALOG_NEW_JOB_CM_CLASS,
408  gnc_job_window_refresh_handler,
409  gnc_job_window_close_handler,
410  jw);
411  }
412 
413  gnc_job_name_changed_cb (NULL, jw);
414  gnc_gui_component_watch_entity_type (jw->component_id,
415  GNC_JOB_MODULE_NAME,
416  QOF_EVENT_MODIFY | QOF_EVENT_DESTROY);
417 
418  gtk_widget_show_all (jw->dialog);
419 
420  // The job name should have keyboard focus
421  gtk_widget_grab_focus(jw->name_entry);
422  // Or should the owner field have focus?
423 // if (GNC_IS_GENERAL_SEARCH(jw->cust_edit))
424 // {
425 // gnc_general_search_grab_focus(GNC_GENERAL_SEARCH(jw->cust_edit));
426 // }
427 
428  g_object_unref(G_OBJECT(builder));
429 
430  return jw;
431 }
432 
433 GncJob *
434 gnc_ui_job_new_return_handle (GncOwner *owner, QofBook *book)
435 {
436  JobWindow *jw;
437  if (!book) return NULL;
438  jw = gnc_ui_job_new (owner, book);
439  return jw_get_job (jw);
440 }
441 
442 JobWindow *
443 gnc_ui_job_new (GncOwner *ownerp, QofBook *bookp)
444 {
445  JobWindow *jw;
446  GncOwner owner;
447 
448  /* Make sure required options exist */
449  if (!bookp) return NULL;
450 
451  if (ownerp)
452  {
453  g_return_val_if_fail ((gncOwnerGetType (ownerp) == GNC_OWNER_CUSTOMER) ||
454  (gncOwnerGetType (ownerp) == GNC_OWNER_VENDOR),
455  NULL);
456  gncOwnerCopy (ownerp, &owner);
457  }
458  else
459  gncOwnerInitCustomer (&owner, NULL); /* XXX */
460 
461  jw = gnc_job_new_window (bookp, &owner, NULL);
462  return jw;
463 }
464 
465 JobWindow *
466 gnc_ui_job_edit (GncJob *job)
467 {
468  JobWindow *jw;
469 
470  if (!job) return NULL;
471 
472  jw = gnc_job_new_window (gncJobGetBook(job), gncJobGetOwner(job), job);
473  return jw;
474 }
475 
476 /* Search functionality */
477 
478 static void
479 edit_job_cb (gpointer *job_p, gpointer user_data)
480 {
481  GncJob *job;
482 
483  g_return_if_fail (job_p && user_data);
484 
485  job = *job_p;
486 
487  if (!job)
488  return;
489 
490  gnc_ui_job_edit (job);
491 }
492 
493 static void
494 invoice_job_cb (gpointer *job_p, gpointer user_data)
495 {
496  struct _job_select_window * sw = user_data;
497  GncJob *job;
498  GncOwner owner;
499 
500  g_return_if_fail (job_p && user_data);
501 
502  job = *job_p;
503  if (!job)
504  return;
505 
506  gncOwnerInitJob (&owner, job);
507  gnc_invoice_search (NULL, &owner, sw->book);
508 }
509 
510 static void
511 payment_job_cb (gpointer *job_p, gpointer user_data)
512 {
513  struct _job_select_window *sw = user_data;
514  GncOwner owner;
515  GncJob *job;
516 
517  g_return_if_fail (job_p && user_data);
518 
519  job = *job_p;
520 
521  if (!job)
522  return;
523 
524  gncOwnerInitJob (&owner, job);
525  gnc_ui_payment_new (&owner, sw->book);
526  return;
527 }
528 
529 static gpointer
530 new_job_cb (gpointer user_data)
531 {
532  struct _job_select_window *sw = user_data;
533  JobWindow *jw;
534 
535  g_return_val_if_fail (user_data, NULL);
536 
537  jw = gnc_ui_job_new (sw->owner, sw->book);
538  return jw_get_job (jw);
539 }
540 
541 static void
542 free_userdata_cb (gpointer user_data)
543 {
544  struct _job_select_window *sw = user_data;
545 
546  g_return_if_fail (sw);
547 
548  qof_query_destroy (sw->q);
549  g_free (sw);
550 }
551 
553 gnc_job_search (GncJob *start, GncOwner *owner, QofBook *book)
554 {
555  QofQuery *q, *q2 = NULL;
556  QofIdType type = GNC_JOB_MODULE_NAME;
557  struct _job_select_window *sw;
558  static GList *params = NULL;
559  static GList *columns = NULL;
560  static GNCSearchCallbackButton buttons[] =
561  {
562  { N_("View/Edit Job"), edit_job_cb, NULL, TRUE},
563  { N_("View Invoices"), invoice_job_cb, NULL, TRUE},
564  { N_("Process Payment"), payment_job_cb, NULL, FALSE},
565  { NULL },
566  };
567 
568  g_return_val_if_fail (book, NULL);
569 
570  /* Build parameter list in reverse order */
571  if (params == NULL)
572  {
573  params = gnc_search_param_prepend (params, _("Owner's Name"), NULL, type,
574  JOB_OWNER, OWNER_NAME, NULL);
575  params = gnc_search_param_prepend (params, _("Only Active?"), NULL, type,
576  JOB_ACTIVE, NULL);
577  params = gnc_search_param_prepend (params, _("Billing ID"), NULL, type,
578  JOB_REFERENCE, NULL);
579  params = gnc_search_param_prepend (params, _("Rate"), NULL, type,
580  JOB_RATE, NULL);
581  params = gnc_search_param_prepend (params, _("Job Number"), NULL, type,
582  JOB_ID, NULL);
583  params = gnc_search_param_prepend (params, _("Job Name"), NULL, type,
584  JOB_NAME, NULL);
585  }
586 
587  /* Build the column list in reverse order */
588  if (columns == NULL)
589  {
590  columns = gnc_search_param_prepend (columns, _("Billing ID"), NULL, type,
591  JOB_REFERENCE, NULL);
592  columns = gnc_search_param_prepend (columns, _("Rate"), NULL, type,
593  JOB_RATE, NULL);
594  columns = gnc_search_param_prepend (columns, _("Company"), NULL, type,
595  JOB_OWNER, OWNER_NAME, NULL);
596  columns = gnc_search_param_prepend (columns, _("Job Name"), NULL, type,
597  JOB_NAME, NULL);
598  columns = gnc_search_param_prepend (columns, _("ID #"), NULL, type,
599  JOB_ID, NULL);
600  }
601 
602  /* Build the queries */
603  q = qof_query_create_for (type);
604  qof_query_set_book (q, book);
605 
606  /* If we have a start job but, for some reason, not an owner -- grab
607  * the owner from the starting job.
608  */
609  if ((!owner || !gncOwnerGetGUID (owner)) && start)
610  owner = gncJobGetOwner (start);
611 
612  /* If owner is supplied, limit all searches to invoices who's owner
613  * is the supplied owner! Show all invoices by this owner.
614  */
615  if (owner && gncOwnerGetGUID (owner))
616  {
617  qof_query_add_guid_match (q, g_slist_prepend
618  (g_slist_prepend (NULL, QOF_PARAM_GUID),
619  JOB_OWNER),
620  gncOwnerGetGUID (owner), QOF_QUERY_AND);
621 
622  q2 = qof_query_copy (q);
623  }
624 
625 #if 0
626  if (start)
627  {
628  if (q2 == NULL)
629  q2 = qof_query_copy (q);
630 
631  qof_query_add_guid_match (q2, g_slist_prepend (NULL, QOF_PARAM_GUID),
632  gncJobGetGUID (start), QOF_QUERY_AND);
633  }
634 #endif
635 
636  /* launch select dialog and return the result */
637  sw = g_new0 (struct _job_select_window, 1);
638 
639  if (owner)
640  {
641  gncOwnerCopy (owner, &(sw->owner_def));
642  sw->owner = &(sw->owner_def);
643  }
644  sw->book = book;
645  sw->q = q;
646 
647  return gnc_search_dialog_create (type, _("Find Job"),
648  params, columns, q, q2, buttons, NULL,
649  new_job_cb, sw, free_userdata_cb,
650  GNC_PREFS_GROUP_SEARCH, NULL);
651 }
652 
653 /* Functions for widgets for job selection */
654 
656 gnc_job_search_select (gpointer start, gpointer book)
657 {
658  GncJob *j = start;
659  GncOwner owner, *ownerp;
660 
661  if (!book) return NULL;
662 
663  if (j)
664  {
665  ownerp = gncJobGetOwner (j);
666  gncOwnerCopy (ownerp, &owner);
667  }
668  else
669  gncOwnerInitCustomer (&owner, NULL); /* XXX */
670 
671  return gnc_job_search (start, &owner, book);
672 }
673 
675 gnc_job_search_edit (gpointer start, gpointer book)
676 {
677  if (start)
678  gnc_ui_job_edit (start);
679 
680  return NULL;
681 }
const GncGUID * gncOwnerGetGUID(const GncOwner *owner)
Definition: gncOwner.c:496
gboolean gncOwnerEqual(const GncOwner *a, const GncOwner *b)
Definition: gncOwner.c:382
QofQuery * qof_query_copy(QofQuery *q)
struct _QofQuery QofQuery
Definition: qofquery.h:90
Definition: guid.h:65
const gchar * QofIdType
Definition: qofid.h:85
void qof_query_destroy(QofQuery *q)
Definition: gncJob.c:41
gboolean guid_equal(const GncGUID *guid_1, const GncGUID *guid_2)
void qof_query_set_book(QofQuery *q, QofBook *book)
void gnc_gnome_help(const char *file_name, const char *anchor)
void qof_query_add_guid_match(QofQuery *q, QofQueryParamList *param_list, const GncGUID *guid, QofQueryOp op)
GncOwnerType gncOwnerGetType(const GncOwner *owner)
Definition: gncOwner.c:201
Job Interface.
#define gncJobGetBook(x)
Definition: gncJob.h:114
const GncGUID * guid_null(void)
void qof_event_gen(QofInstance *entity, QofEventId event_type, gpointer event_data)
Invoke all registered event handlers using the given arguments.