1 /********************************************************************\
2  * gncJob.c -- the Core Job Interface                               *
3  *                                                                  *
4  * This program is free software; you can redistribute it and/or    *
5  * modify it under the terms of the GNU General Public License as   *
6  * published by the Free Software Foundation; either version 2 of   *
7  * the License, or (at your option) any later version.              *
8  *                                                                  *
9  * This program is distributed in the hope that it will be useful,  *
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of   *
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the    *
12  * GNU General Public License for more details.                     *
13  *                                                                  *
14  * You should have received a copy of the GNU General Public License*
15  * along with this program; if not, contact:                        *
16  *                                                                  *
17  * Free Software Foundation           Voice:  +1-617-542-5942       *
18  * 51 Franklin Street, Fifth Floor    Fax:    +1-617-542-2652       *
19  * Boston, MA  02110-1301,  USA       gnu@gnu.org                   *
20  *                                                                  *
21 \********************************************************************/
22 
23 /*
24  * Copyright (C) 2001, 2002 Derek Atkins
25  * Copyright (C) 2003 Linas Vepstas <linas@linas.org>
26  * Author: Derek Atkins <warlord@MIT.EDU>
27  */
28 
29 #include <config.h>
30 
31 #include <glib.h>
32 #include <string.h>
33 #include <qofinstance-p.h>
34 
35 #include "gnc-features.h"
36 #include "gncInvoice.h"
37 #include "gncJob.h"
38 #include "gncJobP.h"
39 #include "gncOwnerP.h"
40 
41 struct _gncJob
42 {
43     QofInstance inst;
44     const char *  id;
45     const char *  name;
46     const char *  desc;
47     GncOwner      owner;
48     gboolean      active;
49 };
50 
51 struct _gncJobClass
52 {
53     QofInstanceClass parent_class;
54 };
55 
56 static QofLogModule log_module = GNC_MOD_BUSINESS;
57 
58 #define _GNC_MOD_NAME        GNC_ID_JOB
59 #define GNC_JOB_RATE         "job-rate"
60 
61 /* ================================================================== */
62 /* misc inline functions */
63 
64 static inline void mark_job (GncJob *job);
mark_job(GncJob * job)65 void mark_job (GncJob *job)
66 {
67     qof_instance_set_dirty(&job->inst);
68     qof_event_gen (&job->inst, QOF_EVENT_MODIFY, NULL);
69 }
70 
71 /* ================================================================== */
72 
73 enum
74 {
75     PROP_0,
76 //  PROP_ID,            /* Table */
77     PROP_NAME,          /* Table */
78 //  PROP_REFERENCE,     /* Table */
79 //  PROP_ACTIVE,        /* Table */
80 //  PROP_OWNER_TYPE,    /* Table */
81 //  PROP_OWNER,         /* Table */
82     PROP_PDF_DIRNAME,   /* KVP */
83 };
84 
85 /* GObject Initialization */
86 G_DEFINE_TYPE(GncJob, gnc_job, QOF_TYPE_INSTANCE);
87 
88 static void
gnc_job_init(GncJob * job)89 gnc_job_init(GncJob* job)
90 {
91 }
92 
93 static void
gnc_job_dispose(GObject * jobp)94 gnc_job_dispose(GObject *jobp)
95 {
96     G_OBJECT_CLASS(gnc_job_parent_class)->dispose(jobp);
97 }
98 
99 static void
gnc_job_finalize(GObject * jobp)100 gnc_job_finalize(GObject* jobp)
101 {
102     G_OBJECT_CLASS(gnc_job_parent_class)->finalize(jobp);
103 }
104 
105 static void
gnc_job_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)106 gnc_job_get_property (GObject         *object,
107                       guint            prop_id,
108                       GValue          *value,
109                       GParamSpec      *pspec)
110 {
111     GncJob *job;
112     gchar *key;
113 
114     g_return_if_fail(GNC_IS_JOB(object));
115 
116     job = GNC_JOB(object);
117     switch (prop_id)
118     {
119     case PROP_NAME:
120         g_value_set_string(value, job->name);
121         break;
122     case PROP_PDF_DIRNAME:
123         qof_instance_get_kvp (QOF_INSTANCE (job), value, 1, OWNER_EXPORT_PDF_DIRNAME);
124         break;
125     default:
126         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
127         break;
128     }
129 }
130 
131 static void
gnc_job_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)132 gnc_job_set_property (GObject         *object,
133                       guint            prop_id,
134                       const GValue          *value,
135                       GParamSpec      *pspec)
136 {
137     GncJob *job;
138     gchar *key;
139 
140     g_return_if_fail(GNC_IS_JOB(object));
141 
142     job = GNC_JOB(object);
143     g_assert (qof_instance_get_editlevel(job));
144 
145     switch (prop_id)
146     {
147     case PROP_NAME:
148         gncJobSetName(job, g_value_get_string(value));
149         break;
150     case PROP_PDF_DIRNAME:
151         qof_instance_set_kvp (QOF_INSTANCE (job), value, 1, OWNER_EXPORT_PDF_DIRNAME);
152         break;
153     default:
154         G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
155         break;
156     }
157 }
158 
159 /** Returns a list of my type of object which refers to an object.  For example, when called as
160         qof_instance_get_typed_referring_object_list(taxtable, account);
161     it will return the list of taxtables which refer to a specific account.  The result should be the
162     same regardless of which taxtable object is used.  The list must be freed by the caller but the
163     objects on the list must not.
164  */
165 static GList*
impl_get_typed_referring_object_list(const QofInstance * inst,const QofInstance * ref)166 impl_get_typed_referring_object_list(const QofInstance* inst, const QofInstance* ref)
167 {
168     /* Refers to nothing */
169     return NULL;
170 }
171 
172 static void
gnc_job_class_init(GncJobClass * klass)173 gnc_job_class_init (GncJobClass *klass)
174 {
175     GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
176     QofInstanceClass* qof_class = QOF_INSTANCE_CLASS(klass);
177 
178     gobject_class->dispose = gnc_job_dispose;
179     gobject_class->finalize = gnc_job_finalize;
180     gobject_class->set_property = gnc_job_set_property;
181     gobject_class->get_property = gnc_job_get_property;
182 
183     qof_class->get_display_name = NULL;
184     qof_class->refers_to_object = NULL;
185     qof_class->get_typed_referring_object_list = impl_get_typed_referring_object_list;
186 
187     g_object_class_install_property
188     (gobject_class,
189      PROP_NAME,
190      g_param_spec_string ("name",
191                           "Job Name",
192                           "The job name is an arbitrary string "
193                           "assigned by the user.  It is intended to "
194                           "a short character string that is displayed "
195                           "by the GUI as the job mnemonic.",
196                           NULL,
197                           G_PARAM_READWRITE));
198 
199     g_object_class_install_property
200     (gobject_class,
201      PROP_PDF_DIRNAME,
202      g_param_spec_string ("export-pdf-dir",
203                           "Export PDF Directory Name",
204                           "A subdirectory for exporting PDF reports which is "
205                           "appended to the target directory when writing them "
206                           "out. It is retrieved from preferences and stored on "
207                           "each 'Owner' object which prints items after "
208                           "printing.",
209                           NULL,
210                           G_PARAM_READWRITE));
211 }
212 
213 /* Create/Destroy Functions */
gncJobCreate(QofBook * book)214 GncJob *gncJobCreate (QofBook *book)
215 {
216     GncJob *job;
217 
218     if (!book) return NULL;
219 
220     job = g_object_new (GNC_TYPE_JOB, NULL);
221     qof_instance_init_data (&job->inst, _GNC_MOD_NAME, book);
222 
223     job->id = CACHE_INSERT ("");
224     job->name = CACHE_INSERT ("");
225     job->desc = CACHE_INSERT ("");
226     job->active = TRUE;
227 
228     /* GncOwner not initialized */
229     qof_event_gen (&job->inst, QOF_EVENT_CREATE, NULL);
230 
231     return job;
232 }
233 
gncJobDestroy(GncJob * job)234 void gncJobDestroy (GncJob *job)
235 {
236     if (!job) return;
237     qof_instance_set_destroying(job, TRUE);
238     gncJobCommitEdit (job);
239 }
240 
gncJobFree(GncJob * job)241 static void gncJobFree (GncJob *job)
242 {
243     if (!job) return;
244 
245     qof_event_gen (&job->inst, QOF_EVENT_DESTROY, NULL);
246 
247     CACHE_REMOVE (job->id);
248     CACHE_REMOVE (job->name);
249     CACHE_REMOVE (job->desc);
250 
251     switch (gncOwnerGetType (&(job->owner)))
252     {
253     case GNC_OWNER_CUSTOMER:
254         gncCustomerRemoveJob (gncOwnerGetCustomer(&job->owner), job);
255         break;
256     case GNC_OWNER_VENDOR:
257         gncVendorRemoveJob (gncOwnerGetVendor(&job->owner), job);
258         break;
259     default:
260         break;
261     }
262 
263     /* qof_instance_release (&job->inst); */
264     g_object_unref (job);
265 }
266 
267 
268 /* ================================================================== */
269 /* Set Functions */
270 
271 #define SET_STR(obj, member, str) { \
272         if (!g_strcmp0 (member, str)) return; \
273         gncJobBeginEdit (obj); \
274         CACHE_REPLACE (member, str); \
275         }
276 
gncJobSetID(GncJob * job,const char * id)277 void gncJobSetID (GncJob *job, const char *id)
278 {
279     if (!job) return;
280     if (!id) return;
281     SET_STR(job, job->id, id);
282     mark_job (job);
283     gncJobCommitEdit (job);
284 }
285 
gncJobSetName(GncJob * job,const char * name)286 void gncJobSetName (GncJob *job, const char *name)
287 {
288     if (!job) return;
289     if (!name) return;
290     SET_STR(job, job->name, name);
291     mark_job (job);
292     gncJobCommitEdit (job);
293 }
294 
gncJobSetReference(GncJob * job,const char * desc)295 void gncJobSetReference (GncJob *job, const char *desc)
296 {
297     if (!job) return;
298     if (!desc) return;
299     SET_STR(job, job->desc, desc);
300     mark_job (job);
301     gncJobCommitEdit (job);
302 }
303 
gncJobSetRate(GncJob * job,gnc_numeric rate)304 void gncJobSetRate (GncJob *job, gnc_numeric rate)
305 {
306     if (!job) return;
307     if (gnc_numeric_equal (gncJobGetRate(job), rate)) return;
308 
309     gncJobBeginEdit (job);
310     if (!gnc_numeric_zero_p(rate))
311     {
312         GValue v = G_VALUE_INIT;
313         g_value_init (&v, GNC_TYPE_NUMERIC);
314         g_value_set_boxed (&v, &rate);
315         qof_instance_set_kvp (QOF_INSTANCE (job), &v, 1, GNC_JOB_RATE);
316         g_value_unset (&v);
317     }
318     else
319     {
320         qof_instance_set_kvp (QOF_INSTANCE (job), NULL, 1, GNC_JOB_RATE);
321     }
322     mark_job (job);
323     gncJobCommitEdit (job);
324 }
325 
gncJobSetOwner(GncJob * job,GncOwner * owner)326 void gncJobSetOwner (GncJob *job, GncOwner *owner)
327 {
328     if (!job) return;
329     if (!owner) return;
330     if (gncOwnerEqual (owner, &(job->owner))) return;
331 
332     switch (gncOwnerGetType (owner))
333     {
334     case GNC_OWNER_CUSTOMER:
335     case GNC_OWNER_VENDOR:
336         break;
337     default:
338         PERR("Unsupported Owner type: %d", gncOwnerGetType(owner));
339         return;
340     }
341 
342     gncJobBeginEdit (job);
343 
344     switch (gncOwnerGetType (&(job->owner)))
345     {
346     case GNC_OWNER_CUSTOMER:
347         gncCustomerRemoveJob (gncOwnerGetCustomer(&job->owner), job);
348         break;
349     case GNC_OWNER_VENDOR:
350         gncVendorRemoveJob (gncOwnerGetVendor(&job->owner), job);
351         break;
352     default:
353         break;
354     }
355 
356     gncOwnerCopy (owner, &(job->owner));
357 
358     switch (gncOwnerGetType (&(job->owner)))
359     {
360     case GNC_OWNER_CUSTOMER:
361         gncCustomerAddJob (gncOwnerGetCustomer(&job->owner), job);
362         break;
363     case GNC_OWNER_VENDOR:
364         gncVendorAddJob (gncOwnerGetVendor(&job->owner), job);
365         break;
366     default:
367         break;
368     }
369 
370     mark_job (job);
371     gncJobCommitEdit (job);
372 }
373 
gncJobSetActive(GncJob * job,gboolean active)374 void gncJobSetActive (GncJob *job, gboolean active)
375 {
376     if (!job) return;
377     if (active == job->active) return;
378     gncJobBeginEdit (job);
379     job->active = active;
380     mark_job (job);
381     gncJobCommitEdit (job);
382 }
383 
384 static void
qofJobSetOwner(GncJob * job,QofInstance * ent)385 qofJobSetOwner (GncJob *job, QofInstance *ent)
386 {
387     if (!job || !ent)
388     {
389         return;
390     }
391 
392     gncJobBeginEdit (job);
393     qofOwnerSetEntity(&job->owner, ent);
394     mark_job (job);
395     gncJobCommitEdit (job);
396 }
397 
gncJobBeginEdit(GncJob * job)398 void gncJobBeginEdit (GncJob *job)
399 {
400     qof_begin_edit(&job->inst);
401 }
402 
gncJobOnError(QofInstance * inst,QofBackendError errcode)403 static void gncJobOnError (QofInstance *inst, QofBackendError errcode)
404 {
405     PERR("Job QofBackend Failure: %d", errcode);
406     gnc_engine_signal_commit_error( errcode );
407 }
408 
job_free(QofInstance * inst)409 static void job_free (QofInstance *inst)
410 {
411     GncJob *job = (GncJob *)inst;
412     gncJobFree (job);
413 }
414 
gncJobOnDone(QofInstance * qof)415 static void gncJobOnDone (QofInstance *qof) { }
416 
gncJobCommitEdit(GncJob * job)417 void gncJobCommitEdit (GncJob *job)
418 {
419     /* GnuCash 2.6.3 and earlier didn't handle job kvp's... */
420     if (qof_instance_has_kvp (QOF_INSTANCE (job)))
421         gnc_features_set_used (qof_instance_get_book (QOF_INSTANCE (job)), GNC_FEATURE_KVP_EXTRA_DATA);
422 
423     if (!qof_commit_edit (QOF_INSTANCE(job))) return;
424     qof_commit_edit_part2 (&job->inst, gncJobOnError,
425                            gncJobOnDone, job_free);
426 }
427 
428 /* ================================================================== */
429 /* Get Functions */
430 
gncJobGetID(const GncJob * job)431 const char * gncJobGetID (const GncJob *job)
432 {
433     if (!job) return NULL;
434     return job->id;
435 }
436 
gncJobGetName(const GncJob * job)437 const char * gncJobGetName (const GncJob *job)
438 {
439     if (!job) return NULL;
440     return job->name;
441 }
442 
gncJobGetReference(const GncJob * job)443 const char * gncJobGetReference (const GncJob *job)
444 {
445     if (!job) return NULL;
446     return job->desc;
447 }
448 
gncJobGetRate(const GncJob * job)449 gnc_numeric gncJobGetRate (const GncJob *job)
450 {
451     GValue v = G_VALUE_INIT;
452     gnc_numeric *rate = NULL;
453     gnc_numeric retval;
454     if (!job) return gnc_numeric_zero ();
455     qof_instance_get_kvp (QOF_INSTANCE (job), &v, 1, GNC_JOB_RATE);
456     if (G_VALUE_HOLDS_BOXED (&v))
457         rate = (gnc_numeric*)g_value_get_boxed (&v);
458     retval = rate ? *rate : gnc_numeric_zero ();
459     g_value_unset (&v);
460     return retval;
461 }
462 
gncJobGetOwner(GncJob * job)463 GncOwner * gncJobGetOwner (GncJob *job)
464 {
465     if (!job) return NULL;
466     return &(job->owner);
467 }
468 
gncJobGetActive(const GncJob * job)469 gboolean gncJobGetActive (const GncJob *job)
470 {
471     if (!job) return FALSE;
472     return job->active;
473 }
474 
475 static QofInstance*
qofJobGetOwner(GncJob * job)476 qofJobGetOwner (GncJob *job)
477 {
478     if (!job)
479     {
480         return NULL;
481     }
482     return QOF_INSTANCE(qofOwnerGetOwner(&job->owner));
483 }
484 
485 /* Other functions */
486 
gncJobCompare(const GncJob * a,const GncJob * b)487 int gncJobCompare (const GncJob * a, const GncJob *b)
488 {
489     if (!a && !b) return 0;
490     if (!a && b) return 1;
491     if (a && !b) return -1;
492 
493     return (g_strcmp0(a->id, b->id));
494 }
495 
gncJobEqual(const GncJob * a,const GncJob * b)496 gboolean gncJobEqual(const GncJob * a, const GncJob *b)
497 {
498     if (a == NULL && b == NULL) return TRUE;
499     if (a == NULL || b == NULL) return FALSE;
500 
501     g_return_val_if_fail(GNC_IS_JOB(a), FALSE);
502     g_return_val_if_fail(GNC_IS_JOB(b), FALSE);
503 
504     if (g_strcmp0(a->id, b->id) != 0)
505     {
506         PWARN("IDs differ: %s vs %s", a->id, b->id);
507         return FALSE;
508     }
509 
510     if (g_strcmp0(a->name, b->name) != 0)
511     {
512         PWARN("Names differ: %s vs %s", a->name, b->name);
513         return FALSE;
514     }
515 
516     if (g_strcmp0(a->desc, b->desc) != 0)
517     {
518         PWARN("Descriptions differ: %s vs %s", a->desc, b->desc);
519         return FALSE;
520     }
521 
522     if (!gnc_numeric_equal(gncJobGetRate(a), gncJobGetRate(b)))
523     {
524         PWARN("Rates differ");
525         return FALSE;
526     }
527 
528     if (a->active != b->active)
529     {
530         PWARN("Active flags differ");
531         return FALSE;
532     }
533 
534     /* FIXME: Need real tests */
535 #if 0
536     GncOwner      owner;
537 #endif
538 
539     return TRUE;
540 }
541 
542 /* ================================================================== */
543 /* Package-Private functions */
544 
_gncJobPrintable(gpointer item)545 static const char * _gncJobPrintable (gpointer item)
546 {
547     GncJob *c;
548     if (!item) return NULL;
549     c = item;
550     return c->name;
551 }
552 
553 static QofObject gncJobDesc =
554 {
555     DI(.interface_version = ) QOF_OBJECT_VERSION,
556     DI(.e_type            = ) _GNC_MOD_NAME,
557     DI(.type_label        = ) "Job",
558     DI(.create            = ) (gpointer)gncJobCreate,
559     DI(.book_begin        = ) NULL,
560     DI(.book_end          = ) NULL,
561     DI(.is_dirty          = ) qof_collection_is_dirty,
562     DI(.mark_clean        = ) qof_collection_mark_clean,
563     DI(.foreach           = ) qof_collection_foreach,
564     DI(.printable         = ) _gncJobPrintable,
565     DI(.version_cmp       = ) (int (*)(gpointer, gpointer)) qof_instance_version_cmp,
566 };
567 
gncJobRegister(void)568 gboolean gncJobRegister (void)
569 {
570     static QofParam params[] =
571     {
572         { JOB_ID, QOF_TYPE_STRING, (QofAccessFunc)gncJobGetID, (QofSetterFunc)gncJobSetID },
573         { JOB_NAME, QOF_TYPE_STRING, (QofAccessFunc)gncJobGetName, (QofSetterFunc)gncJobSetName },
574         { JOB_ACTIVE, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncJobGetActive, (QofSetterFunc)gncJobSetActive },
575         { JOB_REFERENCE, QOF_TYPE_STRING, (QofAccessFunc)gncJobGetReference, (QofSetterFunc)gncJobSetReference },
576         { JOB_RATE, QOF_TYPE_NUMERIC, (QofAccessFunc)gncJobGetRate, (QofSetterFunc)gncJobSetRate },
577         { JOB_OWNER, GNC_ID_OWNER, (QofAccessFunc)gncJobGetOwner, NULL },
578         { QOF_PARAM_ACTIVE, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncJobGetActive, NULL },
579         { QOF_PARAM_BOOK, QOF_ID_BOOK, (QofAccessFunc)qof_instance_get_book, NULL },
580         { QOF_PARAM_GUID, QOF_TYPE_GUID, (QofAccessFunc)qof_instance_get_guid, NULL },
581         { NULL },
582     };
583 
584     if (!qof_choice_create(GNC_ID_JOB))
585     {
586         return FALSE;
587     }
588     if (!qof_choice_add_class(GNC_ID_INVOICE, GNC_ID_JOB, INVOICE_OWNER))
589     {
590         return FALSE;
591     }
592 
593     qof_class_register (_GNC_MOD_NAME, (QofSortFunc)gncJobCompare, params);
594     qofJobGetOwner(NULL);
595     qofJobSetOwner(NULL, NULL);
596     return qof_object_register (&gncJobDesc);
597 }
598 
gncJobNextID(QofBook * book)599 gchar *gncJobNextID (QofBook *book)
600 {
601     return qof_book_increment_and_format_counter (book, _GNC_MOD_NAME);
602 }
603