Branch data Line data Source code
1 : : /********************************************************************\
2 : : * gncCustomer.c -- the Core Customer 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-commodity.h"
36 : :
37 : : #include "gncAddressP.h"
38 : : #include "gncBillTermP.h"
39 : : #include "gncInvoice.h"
40 : : #include "gncBusiness.h"
41 : :
42 : : #include "gncCustomer.h"
43 : : #include "gncCustomerP.h"
44 : : #include "gncJobP.h"
45 : : #include "gncTaxTableP.h"
46 : :
47 : : static gint cust_qof_event_handler_id = 0;
48 : : static void cust_handle_qof_events (QofInstance *entity, QofEventId event_type,
49 : : gpointer user_data, gpointer event_data);
50 : :
51 : : struct _gncCustomer
52 : : {
53 : : QofInstance inst;
54 : :
55 : : /* The following fields are identical to 'vendor' */
56 : : const char * id;
57 : : const char * name;
58 : : const char * notes;
59 : : GncBillTerm * terms;
60 : : GncAddress * addr;
61 : : gnc_commodity * currency;
62 : : GncTaxTable* taxtable;
63 : : gboolean taxtable_override;
64 : : GncTaxIncluded taxincluded;
65 : : gboolean active;
66 : : GList * jobs;
67 : : gnc_numeric * balance; /* cached customer balance, will not be stored */
68 : :
69 : : /* The following fields are unique to 'customer' */
70 : : gnc_numeric credit;
71 : : gnc_numeric discount;
72 : : GncAddress * shipaddr;
73 : : };
74 : :
75 : : struct _gncCustomerClass
76 : : {
77 : : QofInstanceClass parent_class;
78 : : };
79 : :
80 : : static QofLogModule log_module = GNC_MOD_BUSINESS;
81 : :
82 : : #define _GNC_MOD_NAME GNC_ID_CUSTOMER
83 : :
84 : : /* ============================================================== */
85 : : /* misc inline funcs */
86 : :
87 : : static inline void mark_customer (GncCustomer *customer);
88 : 75 : void mark_customer (GncCustomer *customer)
89 : : {
90 : 75 : qof_instance_set_dirty(&customer->inst);
91 : 75 : qof_event_gen (&customer->inst, QOF_EVENT_MODIFY, NULL);
92 : 75 : }
93 : :
94 : : /* ============================================================== */
95 : :
96 : : enum
97 : : {
98 : : PROP_0,
99 : : PROP_NAME,
100 : : PROP_PDF_DIRNAME,
101 : : PROP_LAST_POSTED,
102 : : PROP_PAYMENT_LAST_ACCT,
103 : : };
104 : :
105 : : /* GObject Initialization */
106 : 113 : G_DEFINE_TYPE(GncCustomer, gnc_customer, QOF_TYPE_INSTANCE)
107 : :
108 : : static void
109 : 33 : gnc_customer_init(GncCustomer* cust)
110 : : {
111 : 33 : }
112 : :
113 : : static void
114 : 21 : gnc_customer_dispose(GObject *custp)
115 : : {
116 : 21 : G_OBJECT_CLASS(gnc_customer_parent_class)->dispose(custp);
117 : 21 : }
118 : :
119 : : static void
120 : 21 : gnc_customer_finalize(GObject* custp)
121 : : {
122 : 21 : G_OBJECT_CLASS(gnc_customer_parent_class)->finalize(custp);
123 : 21 : }
124 : :
125 : : static void
126 : 4 : gnc_customer_get_property (GObject *object,
127 : : guint prop_id,
128 : : GValue *value,
129 : : GParamSpec *pspec)
130 : : {
131 : : GncCustomer *cust;
132 : 4 : g_return_if_fail(GNC_IS_CUSTOMER(object));
133 : :
134 : 4 : cust = GNC_CUSTOMER(object);
135 : 4 : switch (prop_id)
136 : : {
137 : 1 : case PROP_NAME:
138 : 1 : g_value_set_string(value, cust->name);
139 : 1 : break;
140 : 1 : case PROP_PDF_DIRNAME:
141 : 1 : qof_instance_get_kvp (QOF_INSTANCE (cust), value, 1, OWNER_EXPORT_PDF_DIRNAME);
142 : 1 : break;
143 : 1 : case PROP_LAST_POSTED:
144 : 1 : qof_instance_get_kvp (QOF_INSTANCE (cust), value, 1, LAST_POSTED_TO_ACCT);
145 : 1 : break;
146 : 1 : case PROP_PAYMENT_LAST_ACCT:
147 : 1 : qof_instance_get_kvp (QOF_INSTANCE (cust), value, 2, GNC_PAYMENT, GNC_LAST_ACCOUNT);
148 : 1 : break;
149 : 0 : default:
150 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
151 : 0 : break;
152 : : }
153 : : }
154 : :
155 : : static void
156 : 4 : gnc_customer_set_property (GObject *object,
157 : : guint prop_id,
158 : : const GValue *value,
159 : : GParamSpec *pspec)
160 : : {
161 : : GncCustomer *cust;
162 : :
163 : 4 : g_return_if_fail(GNC_IS_CUSTOMER(object));
164 : :
165 : 4 : cust = GNC_CUSTOMER(object);
166 : 4 : g_assert (qof_instance_get_editlevel(cust));
167 : :
168 : 4 : switch (prop_id)
169 : : {
170 : 1 : case PROP_NAME:
171 : 1 : gncCustomerSetName(cust, g_value_get_string(value));
172 : 1 : break;
173 : 1 : case PROP_PDF_DIRNAME:
174 : 1 : qof_instance_set_kvp (QOF_INSTANCE (cust), value, 1, OWNER_EXPORT_PDF_DIRNAME);
175 : 1 : break;
176 : 1 : case PROP_LAST_POSTED:
177 : 1 : qof_instance_set_kvp (QOF_INSTANCE (cust), value, 1, LAST_POSTED_TO_ACCT);
178 : 1 : break;
179 : 1 : case PROP_PAYMENT_LAST_ACCT:
180 : 1 : qof_instance_set_kvp (QOF_INSTANCE (cust), value, 2, GNC_PAYMENT, GNC_LAST_ACCOUNT);
181 : 1 : break;
182 : 0 : default:
183 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
184 : 0 : break;
185 : : }
186 : : }
187 : :
188 : : /** Return display name for this object */
189 : : static gchar*
190 : 0 : impl_get_display_name(const QofInstance* inst)
191 : : {
192 : : GncCustomer* cust;
193 : :
194 : 0 : g_return_val_if_fail(inst != NULL, FALSE);
195 : 0 : g_return_val_if_fail(GNC_IS_CUSTOMER(inst), FALSE);
196 : :
197 : 0 : cust = GNC_CUSTOMER(inst);
198 : : /* XXX internationalization of "Customer" */
199 : 0 : return g_strdup_printf("Customer %s", cust->name);
200 : : }
201 : :
202 : : /** Does this object refer to a specific object */
203 : : static gboolean
204 : 0 : impl_refers_to_object(const QofInstance* inst, const QofInstance* ref)
205 : : {
206 : : GncCustomer* cust;
207 : :
208 : 0 : g_return_val_if_fail(inst != NULL, FALSE);
209 : 0 : g_return_val_if_fail(GNC_IS_CUSTOMER(inst), FALSE);
210 : :
211 : 0 : cust = GNC_CUSTOMER(inst);
212 : :
213 : 0 : if (GNC_IS_BILLTERM(ref))
214 : : {
215 : 0 : return (cust->terms == GNC_BILLTERM(ref));
216 : : }
217 : 0 : else if (GNC_IS_TAXTABLE(ref))
218 : : {
219 : 0 : return (cust->taxtable == GNC_TAXTABLE(ref));
220 : : }
221 : :
222 : 0 : return FALSE;
223 : : }
224 : :
225 : : /** Returns a list of my type of object which refers to an object. For example, when called as
226 : : qof_instance_get_typed_referring_object_list(taxtable, account);
227 : : it will return the list of taxtables which refer to a specific account. The result should be the
228 : : same regardless of which taxtable object is used. The list must be freed by the caller but the
229 : : objects on the list must not.
230 : : */
231 : : static GList*
232 : 0 : impl_get_typed_referring_object_list(const QofInstance* inst, const QofInstance* ref)
233 : : {
234 : 0 : if (!GNC_IS_BILLTERM(ref) && !GNC_IS_TAXTABLE(ref))
235 : : {
236 : 0 : return NULL;
237 : : }
238 : :
239 : 0 : return qof_instance_get_referring_object_list_from_collection(qof_instance_get_collection(inst), ref);
240 : : }
241 : :
242 : : static void
243 : 11 : gnc_customer_class_init (GncCustomerClass *klass)
244 : : {
245 : 11 : GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
246 : 11 : QofInstanceClass* qof_class = QOF_INSTANCE_CLASS(klass);
247 : :
248 : 11 : gobject_class->dispose = gnc_customer_dispose;
249 : 11 : gobject_class->finalize = gnc_customer_finalize;
250 : 11 : gobject_class->set_property = gnc_customer_set_property;
251 : 11 : gobject_class->get_property = gnc_customer_get_property;
252 : :
253 : 11 : qof_class->get_display_name = impl_get_display_name;
254 : 11 : qof_class->refers_to_object = impl_refers_to_object;
255 : 11 : qof_class->get_typed_referring_object_list = impl_get_typed_referring_object_list;
256 : :
257 : 11 : g_object_class_install_property
258 : : (gobject_class,
259 : : PROP_NAME,
260 : : g_param_spec_string ("name",
261 : : "Customer Name",
262 : : "The customer is an arbitrary string "
263 : : "assigned by the user which provides the "
264 : : "customer name.",
265 : : NULL,
266 : : G_PARAM_READWRITE));
267 : :
268 : 11 : g_object_class_install_property
269 : : (gobject_class,
270 : : PROP_PDF_DIRNAME,
271 : : g_param_spec_string ("export-pdf-dir",
272 : : "Export PDF Directory Name",
273 : : "A subdirectory for exporting PDF reports which is "
274 : : "appended to the target directory when writing them "
275 : : "out. It is retrieved from preferences and stored on "
276 : : "each 'Owner' object which prints items after "
277 : : "printing.",
278 : : NULL,
279 : : G_PARAM_READWRITE));
280 : :
281 : 11 : g_object_class_install_property(
282 : : gobject_class,
283 : : PROP_LAST_POSTED,
284 : : g_param_spec_boxed("invoice-last-posted-account",
285 : : "Invoice Last Posted Account",
286 : : "The last account to which an invoice belonging to "
287 : : "this owner was posted.",
288 : : GNC_TYPE_GUID,
289 : : G_PARAM_READWRITE));
290 : :
291 : 11 : g_object_class_install_property(
292 : : gobject_class,
293 : : PROP_PAYMENT_LAST_ACCT,
294 : : g_param_spec_boxed("payment-last-account",
295 : : "Payment Last Account",
296 : : "The last account to which an payment belonging to "
297 : : "this owner was posted.",
298 : : GNC_TYPE_GUID,
299 : : G_PARAM_READWRITE));
300 : 11 : }
301 : :
302 : : /* Create/Destroy Functions */
303 : 34 : GncCustomer *gncCustomerCreate (QofBook *book)
304 : : {
305 : : GncCustomer *cust;
306 : :
307 : 34 : if (!book) return NULL;
308 : :
309 : 33 : cust = g_object_new (GNC_TYPE_CUSTOMER, NULL);
310 : 33 : qof_instance_init_data (&cust->inst, _GNC_MOD_NAME, book);
311 : :
312 : 33 : cust->id = CACHE_INSERT ("");
313 : 33 : cust->name = CACHE_INSERT ("");
314 : 33 : cust->notes = CACHE_INSERT ("");
315 : 33 : cust->addr = gncAddressCreate (book, &cust->inst);
316 : 33 : cust->taxincluded = GNC_TAXINCLUDED_USEGLOBAL;
317 : 33 : cust->active = TRUE;
318 : 33 : cust->jobs = NULL;
319 : 33 : cust->balance = NULL;
320 : :
321 : 33 : cust->discount = gnc_numeric_zero();
322 : 33 : cust->credit = gnc_numeric_zero();
323 : 33 : cust->shipaddr = gncAddressCreate (book, &cust->inst);
324 : :
325 : 33 : if (cust_qof_event_handler_id == 0)
326 : 11 : cust_qof_event_handler_id = qof_event_register_handler (cust_handle_qof_events, NULL);
327 : :
328 : 33 : qof_event_gen (&cust->inst, QOF_EVENT_CREATE, NULL);
329 : :
330 : 33 : return cust;
331 : : }
332 : :
333 : 20 : void gncCustomerDestroy (GncCustomer *cust)
334 : : {
335 : 20 : if (!cust) return;
336 : 20 : qof_instance_set_destroying(cust, TRUE);
337 : 20 : qof_instance_set_dirty (&cust->inst);
338 : 20 : gncCustomerCommitEdit (cust);
339 : : }
340 : :
341 : 20 : static void gncCustomerFree (GncCustomer *cust)
342 : : {
343 : 20 : if (!cust) return;
344 : :
345 : 20 : qof_event_gen (&cust->inst, QOF_EVENT_DESTROY, NULL);
346 : :
347 : 20 : CACHE_REMOVE (cust->id);
348 : 20 : CACHE_REMOVE (cust->name);
349 : 20 : CACHE_REMOVE (cust->notes);
350 : 20 : gncAddressBeginEdit (cust->addr);
351 : 20 : gncAddressDestroy (cust->addr);
352 : 20 : gncAddressBeginEdit (cust->shipaddr);
353 : 20 : gncAddressDestroy (cust->shipaddr);
354 : :
355 : 20 : gncJobFreeList (cust->jobs);
356 : 20 : g_list_free (cust->jobs);
357 : 20 : g_free (cust->balance);
358 : :
359 : 20 : if (!qof_book_shutting_down (qof_instance_get_book (QOF_INSTANCE(cust))))
360 : : {
361 : 9 : if (cust->terms)
362 : 0 : gncBillTermDecRef (cust->terms);
363 : 9 : if (cust->taxtable)
364 : 0 : gncTaxTableDecRef (cust->taxtable);
365 : : }
366 : :
367 : : /* qof_instance_release (&cust->inst); */
368 : 20 : g_object_unref (cust);
369 : : }
370 : :
371 : : /* ============================================================== */
372 : : /* Set Functions */
373 : :
374 : : #define SET_STR(obj, member, str) { \
375 : : if (!g_strcmp0 (member, str)) return; \
376 : : gncCustomerBeginEdit (obj); \
377 : : CACHE_REPLACE(member, str); \
378 : : }
379 : :
380 : 15 : void gncCustomerSetID (GncCustomer *cust, const char *id)
381 : : {
382 : 15 : if (!cust) return;
383 : 15 : if (!id) return;
384 : 15 : SET_STR(cust, cust->id, id);
385 : 15 : mark_customer (cust);
386 : 15 : gncCustomerCommitEdit (cust);
387 : : }
388 : :
389 : 11 : void gncCustomerSetName (GncCustomer *cust, const char *name)
390 : : {
391 : 11 : if (!cust) return;
392 : 11 : if (!name) return;
393 : 11 : SET_STR(cust, cust->name, name);
394 : 11 : mark_customer (cust);
395 : 11 : gncCustomerCommitEdit (cust);
396 : : }
397 : :
398 : 10 : void gncCustomerSetNotes (GncCustomer *cust, const char *notes)
399 : : {
400 : 10 : if (!cust) return;
401 : 10 : if (!notes) return;
402 : 10 : SET_STR(cust, cust->notes, notes);
403 : 10 : mark_customer (cust);
404 : 10 : gncCustomerCommitEdit (cust);
405 : : }
406 : :
407 : 0 : void gncCustomerSetTerms (GncCustomer *cust, GncBillTerm *terms)
408 : : {
409 : 0 : if (!cust) return;
410 : 0 : if (cust->terms == terms) return;
411 : :
412 : 0 : gncCustomerBeginEdit (cust);
413 : 0 : if (cust->terms)
414 : 0 : gncBillTermDecRef (cust->terms);
415 : 0 : cust->terms = terms;
416 : 0 : if (cust->terms)
417 : 0 : gncBillTermIncRef (cust->terms);
418 : 0 : mark_customer (cust);
419 : 0 : gncCustomerCommitEdit (cust);
420 : : }
421 : :
422 : 8 : void gncCustomerSetTaxIncluded (GncCustomer *cust, GncTaxIncluded taxincl)
423 : : {
424 : 8 : if (!cust) return;
425 : 8 : if (taxincl == cust->taxincluded) return;
426 : 7 : gncCustomerBeginEdit (cust);
427 : 7 : cust->taxincluded = taxincl;
428 : 7 : mark_customer (cust);
429 : 7 : gncCustomerCommitEdit (cust);
430 : : }
431 : :
432 : 10 : void gncCustomerSetActive (GncCustomer *cust, gboolean active)
433 : : {
434 : 10 : if (!cust) return;
435 : 10 : if (active == cust->active) return;
436 : 8 : gncCustomerBeginEdit (cust);
437 : 8 : cust->active = active;
438 : 8 : mark_customer (cust);
439 : 8 : gncCustomerCommitEdit (cust);
440 : : }
441 : :
442 : 2 : void gncCustomerSetDiscount (GncCustomer *cust, gnc_numeric discount)
443 : : {
444 : 2 : if (!cust) return;
445 : 2 : if (gnc_numeric_equal (discount, cust->discount)) return;
446 : 1 : gncCustomerBeginEdit (cust);
447 : 1 : cust->discount = discount;
448 : 1 : mark_customer (cust);
449 : 1 : gncCustomerCommitEdit (cust);
450 : : }
451 : :
452 : 2 : void gncCustomerSetCredit (GncCustomer *cust, gnc_numeric credit)
453 : : {
454 : 2 : if (!cust) return;
455 : 2 : if (gnc_numeric_equal (credit, cust->credit)) return;
456 : 1 : gncCustomerBeginEdit (cust);
457 : 1 : cust->credit = credit;
458 : 1 : mark_customer (cust);
459 : 1 : gncCustomerCommitEdit (cust);
460 : : }
461 : :
462 : 14 : void gncCustomerSetCurrency (GncCustomer *cust, gnc_commodity *currency)
463 : : {
464 : 14 : if (!cust || !currency) return;
465 : 14 : if (cust->currency && gnc_commodity_equal (cust->currency, currency)) return;
466 : 14 : gncCustomerBeginEdit (cust);
467 : 14 : cust->currency = currency;
468 : 14 : mark_customer (cust);
469 : 14 : gncCustomerCommitEdit (cust);
470 : : }
471 : :
472 : 1 : void gncCustomerSetTaxTableOverride (GncCustomer *customer, gboolean override)
473 : : {
474 : 1 : if (!customer) return;
475 : 1 : if (customer->taxtable_override == override) return;
476 : 0 : gncCustomerBeginEdit (customer);
477 : 0 : customer->taxtable_override = override;
478 : 0 : mark_customer (customer);
479 : 0 : gncCustomerCommitEdit (customer);
480 : : }
481 : :
482 : 0 : void gncCustomerSetTaxTable (GncCustomer *customer, GncTaxTable *table)
483 : : {
484 : 0 : if (!customer) return;
485 : 0 : if (customer->taxtable == table) return;
486 : :
487 : 0 : gncCustomerBeginEdit (customer);
488 : 0 : if (customer->taxtable)
489 : 0 : gncTaxTableDecRef (customer->taxtable);
490 : 0 : if (table)
491 : 0 : gncTaxTableIncRef (table);
492 : 0 : customer->taxtable = table;
493 : 0 : mark_customer (customer);
494 : 0 : gncCustomerCommitEdit (customer);
495 : : }
496 : :
497 : : /* Note that JobList changes do not affect the "dirtiness" of the customer */
498 : 12 : void gncCustomerAddJob (GncCustomer *cust, GncJob *job)
499 : : {
500 : 12 : if (!cust) return;
501 : 12 : if (!job) return;
502 : :
503 : 12 : if (g_list_index(cust->jobs, job) == -1)
504 : 12 : cust->jobs = g_list_insert_sorted (cust->jobs, job,
505 : : (GCompareFunc)gncJobCompare);
506 : :
507 : 12 : qof_event_gen (&cust->inst, QOF_EVENT_MODIFY, NULL);
508 : : }
509 : :
510 : 2 : void gncCustomerRemoveJob (GncCustomer *cust, GncJob *job)
511 : : {
512 : : GList *node;
513 : :
514 : 2 : if (!cust) return;
515 : 2 : if (!job) return;
516 : :
517 : 2 : node = g_list_find (cust->jobs, job);
518 : 2 : if (!node)
519 : : {
520 : : /* PERR ("split not in account"); */
521 : : }
522 : : else
523 : : {
524 : 2 : cust->jobs = g_list_remove_link (cust->jobs, node);
525 : 2 : g_list_free_1 (node);
526 : : }
527 : 2 : qof_event_gen (&cust->inst, QOF_EVENT_MODIFY, NULL);
528 : : }
529 : :
530 : 109 : void gncCustomerBeginEdit (GncCustomer *cust)
531 : : {
532 : 109 : qof_begin_edit (&cust->inst);
533 : 109 : }
534 : :
535 : 0 : static void gncCustomerOnError (QofInstance *inst, QofBackendError errcode)
536 : : {
537 : 0 : PERR("Customer QofBackend Failure: %d", errcode);
538 : 0 : gnc_engine_signal_commit_error( errcode );
539 : 0 : }
540 : :
541 : 70 : static void gncCustomerOnDone (QofInstance *inst)
542 : : {
543 : 70 : GncCustomer *cust = (GncCustomer *) inst;
544 : 70 : gncAddressClearDirty (cust->addr);
545 : 70 : gncAddressClearDirty (cust->shipaddr);
546 : 70 : }
547 : :
548 : 20 : static void cust_free (QofInstance *inst)
549 : : {
550 : 20 : GncCustomer *cust = (GncCustomer *) inst;
551 : 20 : gncCustomerFree (cust);
552 : 20 : }
553 : :
554 : 109 : void gncCustomerCommitEdit (GncCustomer *cust)
555 : : {
556 : 109 : if (!qof_commit_edit (QOF_INSTANCE(cust))) return;
557 : 90 : qof_commit_edit_part2 (&cust->inst, gncCustomerOnError,
558 : : gncCustomerOnDone, cust_free);
559 : : }
560 : :
561 : : /* ============================================================== */
562 : : /* Get Functions */
563 : :
564 : 4 : const char * gncCustomerGetID (const GncCustomer *cust)
565 : : {
566 : 4 : if (!cust) return NULL;
567 : 4 : return cust->id;
568 : : }
569 : :
570 : 120 : const char * gncCustomerGetName (const GncCustomer *cust)
571 : : {
572 : 120 : if (!cust) return NULL;
573 : 120 : return cust->name;
574 : : }
575 : :
576 : 24 : GncAddress * gncCustomerGetAddr (const GncCustomer *cust)
577 : : {
578 : 24 : if (!cust) return NULL;
579 : 24 : return cust->addr;
580 : : }
581 : :
582 : : static void
583 : 1 : qofCustomerSetAddr (GncCustomer *cust, QofInstance *addr_ent)
584 : : {
585 : : GncAddress *addr;
586 : :
587 : 1 : if (!cust || !addr_ent)
588 : : {
589 : 0 : return;
590 : : }
591 : 1 : addr = (GncAddress*)addr_ent;
592 : 1 : if (addr == cust->addr)
593 : : {
594 : 0 : return;
595 : : }
596 : 1 : if (cust->addr != NULL)
597 : : {
598 : 1 : gncAddressBeginEdit(cust->addr);
599 : 1 : gncAddressDestroy(cust->addr);
600 : : }
601 : 1 : gncCustomerBeginEdit(cust);
602 : 1 : cust->addr = addr;
603 : 1 : gncCustomerCommitEdit(cust);
604 : : }
605 : :
606 : : static void
607 : 1 : qofCustomerSetShipAddr (GncCustomer *cust, QofInstance *ship_addr_ent)
608 : : {
609 : : GncAddress *ship_addr;
610 : :
611 : 1 : if (!cust || !ship_addr_ent)
612 : : {
613 : 0 : return;
614 : : }
615 : 1 : ship_addr = (GncAddress*)ship_addr_ent;
616 : 1 : if (ship_addr == cust->shipaddr)
617 : : {
618 : 0 : return;
619 : : }
620 : 1 : if (cust->shipaddr != NULL)
621 : : {
622 : 1 : gncAddressBeginEdit(cust->shipaddr);
623 : 1 : gncAddressDestroy(cust->shipaddr);
624 : : }
625 : 1 : gncCustomerBeginEdit(cust);
626 : 1 : cust->shipaddr = ship_addr;
627 : 1 : gncCustomerCommitEdit(cust);
628 : : }
629 : :
630 : 3 : GncAddress * gncCustomerGetShipAddr (const GncCustomer *cust)
631 : : {
632 : 3 : if (!cust) return NULL;
633 : 3 : return cust->shipaddr;
634 : : }
635 : :
636 : 2 : const char * gncCustomerGetNotes (const GncCustomer *cust)
637 : : {
638 : 2 : if (!cust) return NULL;
639 : 2 : return cust->notes;
640 : : }
641 : :
642 : 1 : GncBillTerm * gncCustomerGetTerms (const GncCustomer *cust)
643 : : {
644 : 1 : if (!cust) return NULL;
645 : 1 : return cust->terms;
646 : : }
647 : :
648 : 1 : GncTaxIncluded gncCustomerGetTaxIncluded (const GncCustomer *cust)
649 : : {
650 : 1 : if (!cust) return GNC_TAXINCLUDED_USEGLOBAL;
651 : 1 : return cust->taxincluded;
652 : : }
653 : :
654 : 3 : gnc_commodity * gncCustomerGetCurrency (const GncCustomer *cust)
655 : : {
656 : 3 : if (!cust) return NULL;
657 : 3 : return cust->currency;
658 : : }
659 : :
660 : 16 : gboolean gncCustomerGetActive (const GncCustomer *cust)
661 : : {
662 : 16 : if (!cust) return FALSE;
663 : 16 : return cust->active;
664 : : }
665 : :
666 : 2 : gnc_numeric gncCustomerGetDiscount (const GncCustomer *cust)
667 : : {
668 : 2 : if (!cust) return gnc_numeric_zero();
669 : 2 : return cust->discount;
670 : : }
671 : :
672 : 2 : gnc_numeric gncCustomerGetCredit (const GncCustomer *cust)
673 : : {
674 : 2 : if (!cust) return gnc_numeric_zero();
675 : 2 : return cust->credit;
676 : : }
677 : :
678 : 1 : gboolean gncCustomerGetTaxTableOverride (const GncCustomer *customer)
679 : : {
680 : 1 : if (!customer) return FALSE;
681 : 1 : return customer->taxtable_override;
682 : : }
683 : :
684 : 1 : GncTaxTable* gncCustomerGetTaxTable (const GncCustomer *customer)
685 : : {
686 : 1 : if (!customer) return NULL;
687 : 1 : return customer->taxtable;
688 : : }
689 : :
690 : 6 : GList * gncCustomerGetJoblist (const GncCustomer *cust, gboolean show_all)
691 : : {
692 : 6 : if (!cust) return NULL;
693 : :
694 : 6 : if (show_all)
695 : : {
696 : 4 : return (g_list_copy (cust->jobs));
697 : : }
698 : : else
699 : : {
700 : 2 : GList *list = NULL, *iterator;
701 : 4 : for (iterator = cust->jobs; iterator; iterator = iterator->next)
702 : : {
703 : 2 : GncJob *j = iterator->data;
704 : 2 : if (gncJobGetActive (j))
705 : 1 : list = g_list_prepend (list, j);
706 : : }
707 : 2 : return g_list_reverse (list);
708 : : }
709 : : }
710 : :
711 : 12 : gboolean gncCustomerIsDirty (GncCustomer *cust)
712 : : {
713 : 12 : if (!cust) return FALSE;
714 : 18 : return (qof_instance_is_dirty(&cust->inst) ||
715 : 18 : gncAddressIsDirty (cust->addr) ||
716 : 6 : gncAddressIsDirty (cust->shipaddr));
717 : : }
718 : :
719 : : /* Other functions */
720 : :
721 : 0 : int gncCustomerCompare (const GncCustomer *a, const GncCustomer *b)
722 : : {
723 : 0 : if (!a && !b) return 0;
724 : 0 : if (!a && b) return 1;
725 : 0 : if (a && !b) return -1;
726 : :
727 : 0 : return(strcmp(a->name, b->name));
728 : : }
729 : :
730 : : gboolean
731 : 4 : gncCustomerEqual(const GncCustomer *a, const GncCustomer *b)
732 : : {
733 : 4 : if (a == NULL && b == NULL) return TRUE;
734 : 4 : if (a == NULL || b == NULL) return FALSE;
735 : :
736 : 4 : g_return_val_if_fail(GNC_IS_CUSTOMER(a), FALSE);
737 : 4 : g_return_val_if_fail(GNC_IS_CUSTOMER(b), FALSE);
738 : :
739 : 4 : if (g_strcmp0(a->id, b->id) != 0)
740 : : {
741 : 0 : PWARN("IDs differ: %s vs %s", a->id, b->id);
742 : 0 : return FALSE;
743 : : }
744 : :
745 : 4 : if (g_strcmp0(a->name, b->name) != 0)
746 : : {
747 : 0 : PWARN("Names differ: %s vs %s", a->name, b->name);
748 : 0 : return FALSE;
749 : : }
750 : :
751 : 4 : if (g_strcmp0(a->notes, b->notes) != 0)
752 : : {
753 : 0 : PWARN("Notes differ: %s vs %s", a->notes, b->notes);
754 : 0 : return FALSE;
755 : : }
756 : :
757 : 4 : if (!gncBillTermEqual(a->terms, b->terms))
758 : : {
759 : 0 : PWARN("Bill terms differ");
760 : 0 : return FALSE;
761 : : }
762 : :
763 : 4 : if (!gnc_commodity_equal(a->currency, b->currency))
764 : : {
765 : 0 : PWARN("currencies differ");
766 : 0 : return FALSE;
767 : : }
768 : :
769 : 4 : if (!gncTaxTableEqual(a->taxtable, b->taxtable))
770 : : {
771 : 0 : PWARN("tax tables differ");
772 : 0 : return FALSE;
773 : : }
774 : :
775 : 4 : if (a->taxtable_override != b->taxtable_override)
776 : : {
777 : 0 : PWARN("Tax table override flags differ");
778 : 0 : return FALSE;
779 : : }
780 : :
781 : 4 : if (a->taxincluded != b->taxincluded)
782 : : {
783 : 0 : PWARN("Tax included flags differ");
784 : 0 : return FALSE;
785 : : }
786 : :
787 : 4 : if (a->active != b->active)
788 : : {
789 : 0 : PWARN("Active flags differ");
790 : 0 : return FALSE;
791 : : }
792 : :
793 : 4 : if (!gncAddressEqual(a->addr, b->addr))
794 : : {
795 : 0 : PWARN("addresses differ");
796 : 0 : return FALSE;
797 : : }
798 : 4 : if (!gncAddressEqual(a->shipaddr, b->shipaddr))
799 : : {
800 : 0 : PWARN("addresses differ");
801 : 0 : return FALSE;
802 : : }
803 : :
804 : 4 : if (!gnc_numeric_equal(a->credit, b->credit))
805 : : {
806 : 0 : PWARN("Credit amounts differ");
807 : 0 : return FALSE;
808 : : }
809 : :
810 : 4 : if (!gnc_numeric_equal(a->discount, b->discount))
811 : : {
812 : 0 : PWARN("Discount amounts differ");
813 : 0 : return FALSE;
814 : : }
815 : :
816 : : /* FIXME: Need to check jobs list
817 : : GList * jobs;
818 : : */
819 : :
820 : 4 : return TRUE;
821 : : }
822 : :
823 : : /**
824 : : * Listen for qof events.
825 : : *
826 : : * - If the address of a customer has changed, mark the customer as dirty.
827 : : * - If a lot related to a customer has changed, clear the customer's
828 : : * cached balance as it likely has become invalid.
829 : : *
830 : : * @param entity Entity for the event
831 : : * @param event_type Event type
832 : : * @param user_data User data registered with the handler
833 : : * @param event_data Event data passed with the event.
834 : : */
835 : : static void
836 : 90955 : cust_handle_qof_events (QofInstance *entity, QofEventId event_type,
837 : : gpointer user_data, gpointer event_data)
838 : : {
839 : : /* Handle address change events */
840 : 90955 : if ((GNC_IS_ADDRESS (entity) &&
841 : 57 : (event_type & QOF_EVENT_MODIFY) != 0))
842 : : {
843 : 12 : if (GNC_IS_CUSTOMER (event_data))
844 : : {
845 : 8 : GncCustomer* cust = GNC_CUSTOMER (event_data);
846 : 8 : gncCustomerBeginEdit (cust);
847 : 8 : mark_customer (cust);
848 : 8 : gncCustomerCommitEdit (cust);
849 : : }
850 : 12 : return;
851 : : }
852 : :
853 : : /* Handle lot change events */
854 : 90943 : if (GNC_IS_LOT (entity))
855 : : {
856 : 663 : GNCLot *lot = GNC_LOT (entity);
857 : : GncOwner lot_owner;
858 : 663 : const GncOwner *end_owner = NULL;
859 : 663 : GncInvoice *invoice = gncInvoiceGetInvoiceFromLot (lot);
860 : :
861 : : /* Determine the owner associated with the lot */
862 : 663 : if (invoice)
863 : : /* Invoice lots */
864 : 360 : end_owner = gncOwnerGetEndOwner (gncInvoiceGetOwner (invoice));
865 : 303 : else if (gncOwnerGetOwnerFromLot (lot, &lot_owner))
866 : : /* Pre-payment lots */
867 : 97 : end_owner = gncOwnerGetEndOwner (&lot_owner);
868 : :
869 : 663 : if (gncOwnerGetType (end_owner) == GNC_OWNER_CUSTOMER)
870 : : {
871 : : /* Clear the cached balance */
872 : 393 : GncCustomer* cust = gncOwnerGetCustomer (end_owner);
873 : 393 : g_free (cust->balance);
874 : 393 : cust->balance = NULL;
875 : : }
876 : 663 : return;
877 : : }
878 : : }
879 : :
880 : : /* ============================================================== */
881 : : /* Package-Private functions */
882 : 84 : static const char * _gncCustomerPrintable (gpointer item)
883 : : {
884 : : // GncCustomer *c = item;
885 : 84 : if (!item) return "failed";
886 : 0 : return gncCustomerGetName((GncCustomer*)item);
887 : : }
888 : :
889 : : static void
890 : 11 : destroy_customer_on_book_close(QofInstance *ent, gpointer data)
891 : : {
892 : 11 : GncCustomer* c = GNC_CUSTOMER(ent);
893 : :
894 : 11 : gncCustomerBeginEdit(c);
895 : 11 : gncCustomerDestroy(c);
896 : 11 : }
897 : :
898 : : /** Handles book end - frees all customers from the book
899 : : *
900 : : * @param book Book being closed
901 : : */
902 : : static void
903 : 156 : gnc_customer_book_end(QofBook* book)
904 : : {
905 : : QofCollection *col;
906 : :
907 : 156 : col = qof_book_get_collection(book, GNC_ID_CUSTOMER);
908 : 156 : qof_collection_foreach(col, destroy_customer_on_book_close, NULL);
909 : 156 : }
910 : :
911 : : static QofObject gncCustomerDesc =
912 : : {
913 : : DI(.interface_version = ) QOF_OBJECT_VERSION,
914 : : DI(.e_type = ) _GNC_MOD_NAME,
915 : : DI(.type_label = ) "Customer",
916 : : DI(.create = ) (gpointer)gncCustomerCreate,
917 : : DI(.book_begin = ) NULL,
918 : : DI(.book_end = ) gnc_customer_book_end,
919 : : DI(.is_dirty = ) qof_collection_is_dirty,
920 : : DI(.mark_clean = ) qof_collection_mark_clean,
921 : : DI(.foreach = ) qof_collection_foreach,
922 : : DI(.printable = ) (const char * (*)(gpointer))gncCustomerGetName,
923 : : DI(.version_cmp = ) (int (*)(gpointer, gpointer)) qof_instance_version_cmp,
924 : : };
925 : :
926 : 84 : gboolean gncCustomerRegister (void)
927 : : {
928 : : static QofParam params[] =
929 : : {
930 : : { CUSTOMER_ID, QOF_TYPE_STRING, (QofAccessFunc)gncCustomerGetID, (QofSetterFunc)gncCustomerSetID },
931 : : { CUSTOMER_NAME, QOF_TYPE_STRING, (QofAccessFunc)gncCustomerGetName, (QofSetterFunc)gncCustomerSetName },
932 : : { CUSTOMER_NOTES, QOF_TYPE_STRING, (QofAccessFunc)gncCustomerGetNotes, (QofSetterFunc)gncCustomerSetNotes },
933 : : {
934 : : CUSTOMER_DISCOUNT, QOF_TYPE_NUMERIC, (QofAccessFunc)gncCustomerGetDiscount,
935 : : (QofSetterFunc)gncCustomerSetDiscount
936 : : },
937 : : {
938 : : CUSTOMER_CREDIT, QOF_TYPE_NUMERIC, (QofAccessFunc)gncCustomerGetCredit,
939 : : (QofSetterFunc)gncCustomerSetCredit
940 : : },
941 : : { CUSTOMER_ADDR, GNC_ID_ADDRESS, (QofAccessFunc)gncCustomerGetAddr, (QofSetterFunc)qofCustomerSetAddr },
942 : : { CUSTOMER_SHIPADDR, GNC_ID_ADDRESS, (QofAccessFunc)gncCustomerGetShipAddr, (QofSetterFunc)qofCustomerSetShipAddr },
943 : : {
944 : : CUSTOMER_TT_OVER, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncCustomerGetTaxTableOverride,
945 : : (QofSetterFunc)gncCustomerSetTaxTableOverride
946 : : },
947 : : { CUSTOMER_TERMS, GNC_ID_BILLTERM, (QofAccessFunc)gncCustomerGetTerms, (QofSetterFunc)gncCustomerSetTerms },
948 : : { QOF_PARAM_ACTIVE, QOF_TYPE_BOOLEAN, (QofAccessFunc)gncCustomerGetActive, (QofSetterFunc)gncCustomerSetActive },
949 : : { QOF_PARAM_BOOK, QOF_ID_BOOK, (QofAccessFunc)qof_instance_get_book, NULL },
950 : : { QOF_PARAM_GUID, QOF_TYPE_GUID, (QofAccessFunc)qof_instance_get_guid, NULL },
951 : : { NULL },
952 : : };
953 : :
954 : 84 : qof_class_register (_GNC_MOD_NAME, (QofSortFunc)gncCustomerCompare, params);
955 : : /* temp */
956 : 84 : _gncCustomerPrintable(NULL);
957 : 84 : return qof_object_register (&gncCustomerDesc);
958 : : }
959 : :
960 : 0 : gchar *gncCustomerNextID (QofBook *book)
961 : : {
962 : 0 : return qof_book_increment_and_format_counter (book, _GNC_MOD_NAME);
963 : : }
964 : :
965 : : const gnc_numeric*
966 : 0 : gncCustomerGetCachedBalance (GncCustomer *cust)
967 : : {
968 : 0 : return cust->balance;
969 : : }
970 : :
971 : 0 : void gncCustomerSetCachedBalance (GncCustomer *cust, const gnc_numeric *new_bal)
972 : : {
973 : 0 : if (!new_bal)
974 : : {
975 : 0 : if (cust->balance)
976 : : {
977 : 0 : g_free (cust->balance);
978 : 0 : cust->balance = NULL;
979 : : }
980 : 0 : return;
981 : : }
982 : :
983 : 0 : if (!cust->balance)
984 : 0 : cust->balance = g_new0 (gnc_numeric, 1);
985 : :
986 : 0 : *cust->balance = *new_bal;
987 : : }
|