'>' not supported between instances of 'datetime.datetime' and 'str'

Good day all
( Version nr reported further down)

Trying to extend my one workflow. I added the condition that is actually put as an example.

doc.creation > frappe.utils.add_to_date(frappe.utils.now_datetime(), days=-1, as_string=True, as_datetime=True) 

In the example code the days was 5, … I made it 1 to test.

The error I get is …

### App Versions

{
“erpnext”: “13.48.1”,
“frappe”: “13.49.3”
}

### Route

Form/Material Request/MAT-MR-2023-00012

### Trackeback

Traceback (most recent call last):
File “apps/frappe/frappe/app.py”, line 69, in application
response = frappe.api.handle()
File “apps/frappe/frappe/api.py”, line 55, in handle
return frappe.handler.handle()
File “apps/frappe/frappe/handler.py”, line 38, in handle
data = execute_cmd(cmd)
File “apps/frappe/frappe/handler.py”, line 76, in execute_cmd
return frappe.call(method, **frappe.form_dict)
File “apps/frappe/frappe/init.py”, line 1473, in call
return fn(*args, **newargs)
File “apps/frappe/frappe/model/workflow.py”, line 73, in get_transitions
if not is_transition_condition_satisfied(transition, doc):
File “apps/frappe/frappe/model/workflow.py”, line 100, in is_transition_condition_satisfied
return frappe.safe_eval(
File “apps/frappe/frappe/init.py”, line 2119, in safe_eval
return eval(code, eval_globals, eval_locals)
File “”, line 1, in
TypeError: ‘>’ not supported between instances of ‘datetime.datetime’ and ‘str’

### Request Data

{
“type”: “POST”,
“args”: {
“doc”: “{"name":"MAT-MR-2023-00012","owner":"Administrator","creation":"2023-04-13 17:04:16.389567","modified":"2023-04-13 17:04:25.372026","modified_by":"Administrator","idx":0,"docstatus":0,"workflow_state":"Approval Pending by Stock Manager","naming_series":"MAT-MR-.YYYY.-","title":"Purchase Request for M3 Nut","material_request_type":"Purchase","transfer_status":"","status":"Draft","transaction_date":"2023-04-13","schedule_date":"2023-04-14","company":"ABCD (PTY) LTD","set_warehouse":"Stores - AG","per_ordered":0,"per_received":0,"letter_head":"Letterhead B","doctype":"Material Request","items":[{"name":"2c07e0ef59","owner":"Administrator","creation":"2023-04-13 17:04:16.389567","modified":"2023-04-13 17:04:25.372026","modified_by":"Administrator","parent":"MAT-MR-2023-00012","parentfield":"items","parenttype":"Material Request","idx":1,"docstatus":0,"item_code":"SKU10001","item_name":"M3 Nut","schedule_date":"2023-04-14","description":"M3 Nut","item_group":"Raw Material","image":"","qty":55,"stock_uom":"Each","warehouse":"Stores - AG","uom":"Each","conversion_factor":1,"stock_qty":55,"min_order_qty":0,"projected_qty":-20,"actual_qty":0,"ordered_qty":0,"received_qty":0,"rate":0.05,"amount":2.75,"cost_center":"Main - AG","expense_account":"5111 - Cost of Goods Sold - AG","page_break":0,"doctype":"Material Request Item"}],"__onload":{"make_payment_via_journal_entry":0,"backflush_based_on":"Material Transferred for Subcontract"}}”
},
“headers”: {},
“error_handlers”: {},
“url”: “/api/method/frappe.model.workflow.get_transitions”
}

### Response Data

{
“exception”: “TypeError: ‘>’ not supported between instances of ‘datetime.datetime’ and ‘str’”
}


I replaced the ">" with "==" so I will see if this works ( tomorrow ) but I thought I would ask...
What would be an allowed operator? I
1 Like

Hi @willspenc,

I just explaining the example,

To resolve this error, you need to ensure that you are comparing or performing operations between objects of the same type. In this case, you can convert the string object to a datetime object using the datetime.strptime() method.

Here’s an example:

from datetime import datetime

date_str = '2022-03-05'
date_obj = datetime.strptime(date_str, '%Y-%m-%d')

if date_obj > datetime.now():
    print("Date is in the future")
else:
    print("Date is in the past")

In this example, we first convert the string date_str to a datetime object using datetime.strptime() , and then compare it with the current date and time using the greater than operator.

Maybe it becomes helpful for you.

Thank You!

2 Likes

Good day @NCP , thank you for taking the time

Thank you for your explanation. I fully understand that.

Perhaps I should just take a step back and describe my goal. I have an existing work-flow
that works fine. I want to add an additional approval “state” which should only be used
if the approval state is stuck at a level for x-number of days. And on opening up the particular
line in the “Transition” table, there was some sample code that seem to do just that. I copy it below.

Example:

doc.creation > frappe.utils.add_to_date(frappe.utils.now_datetime(), days=-5, as_string=True, as_datetime=True)

I also googled here… where it describes the utility function

https://frappeframework.com/docs/v13/user/en/api/utils

So, for this to work, doc.creation must be of the same type that is returned by “add_to_date”.

Looking at the parameters for the “add_to_date” utility function, “days” seems to be a valid
parameter ? The first parameter in the function-call add_to_date is “now_datetime()”
which is not of the same type as “days”.

So, yes I fully understand what you are saying … somewhere , values are compared with
each other that are not of the same type. I am not sure if that mismatch is
between “doc.creation and add_to_date” or if it is
between “now_datetime() and days”.

I was hoping that , if the “example” code was put in where you open up the line in the
“Transtions table”, that this type of error should not come up.

So I am unsure as to how to apply the knowledge that there is a type-mismatch. i.e. how
do I amend the conditional to solve the mismatch ?

Update : I had a look on the backend at tabMaterial Request and “creation” is a type datetime.

It seems ad_to_date returns a string. Does this mean I can do the following ?

doc.creation > datetime.strptime(frappe.utils.add_to_date(frappe.utils.now_datetime(), days=-5, as_string=True, as_datetime=True), '%Y-%m-%d') 

Update …

I also tried the frappe utils function “getdate” to convert the returned string from add_to_date
to datetime …

doc.creation > frappe.utils.getdate(frappe.utils.add_to_date(frappe.utils.now_datetime(), days=-5, as_string=True, as_datetime=True)) 

Not working…

Edit note:

Error is … ‘NoneType’ object is not callable

Actually, what do you want?

Get a date after 5 days?

Thank you for taking the time @NCP !

This is a conditional in a workflow transition table ( Material request). I want to expand the
existing workflow and want to add a state that only happens if the Material request was
created more than 5 days ago.

Hi @willspenc,

Please apply and try it.

from datetime import datetime
from frappe.utils import add_to_date

cd = doc.creation
creation = datetime.strptime(str(cd), '%Y-%m-%d %H:%M:%S').strftime("%Y-%m-%d")
creation > frappe.utils.add_to_date(frappe.utils.now_datetime(), days=-5, as_string=True, as_datetime=True)

Please set your days accordingly.

Thank You!

Thank you again @NCP

When I enter the code,as above, in the conditional block of the workflow transition table,
I get an error…

Invalid python code in line 1

I did some research and came up with this …

From this URL …

I see this … in lline 196

@typing.overload
def add_to_date(
	date,
	years=0,
	months=0,
	weeks=0,
	days=0,
	hours=0,
	minutes=0,
	seconds=0,
	as_string: Literal[False] = False,
	as_datetime: Literal[True] = True,
) -> datetime.datetime:

From what I see , is that, if I set the as_string=False, the return value from add_to_date should
be of type datetime.

So I tried entering the following into the Transition Table of my workflow …

doc.creation > frappe.utils.add_to_date(frappe.utils.now_datetime(), days=-5, as_string=False, as_datetime=True) 

Note the "as_string=False.

But this also gives me the original error .

‘>’ not supported between instances of ‘datetime.datetime’ and ‘str’

Check it, My side it’s worked.
we just pass the static value of cd.

1 Like

Good day @NCP

Thank you for your efforts.

Are you using a python development environment ? I am entering it in the Transition Table in the
Workflow-doc

This may be the reason why it is working in your case and not mine. I suspect that, in the
Transition Table I have a limited python library available.

Ohh :thinking: ,

I think, it’s tough for us but I will try on it and if it is successful then I will update the post.

Have a good day!
Thank You!

Thank you @NCP I do appreciate your effort to assist. I am also going to try and
work at it.

Have a good day you too !

Update

My experience level with python is low so it would be nice if someone can check if this is correct…

I loaded this code into a python IDE

from datetime import datetime
from frappe.utils import add_to_date


creation = datetime(year=2023, month=4, day=10)
if creation < add_to_date(datetime.now(), days=-5, as_string=False, as_datetime=True):
    print("Is true")
else:
    print("Is false")

When I adjusted “day” between 9,10 and 11, the true/false result followed correctly.

Note that
as_string=False
the conditional is “<”

I am having trouble converting my code to include calls to frappe.utils. It keeps on complaining
about “cache”

But, in the object inspector ,I had a look at “add_to_date” and it seems that it checks
if “date” passed to add_to_date, is a string and it then sets “as_string=True”. I assume that
this will over-ride whatever setting I make it (??)

def add_to_date(date, years=0, months=0, weeks=0, days=0, hours=0, minutes=0, seconds=0, as_string=False,
                as_datetime=False):
    """Adds `days` to the given date"""
    from dateutil.relativedelta import relativedelta

    if date == None:
        date = now_datetime()

    if hours:
        as_datetime = True

    if isinstance(date, string_types):
        as_string = True
        if " " in date:
            as_datetime = True
        date = parser.parse(date)

    date = date + relativedelta(years=years, months=months, weeks=weeks, days=days, hours=hours, minutes=minutes,
                                seconds=seconds)

    if as_string:
        if as_datetime:
            return date.strftime(DATETIME_FORMAT)
        else:
            return date.strftime(DATE_FORMAT)
    else:
        return date

This implies that, no matter what I make “as_string”, “add_to_date” will always return a string.
And when the compare is done to doc.creation ( which is of type datetime ) it will
yield the error that I have been getting all along.

Next I did a crazy test …

Just to ensure that the argument in the add_to_date is of type “datetime” I replace the
frappe.utils.now_datetime() with doc.creation (!!! )

So this is now my conditional ( which I entered into my workflow conditional )

doc.creation > frappe.utils.add_to_date(doc.creation, days=-5, as_string=False, as_datetime=True)

This will ensure that , “date” passed to add_to_date ,is of type datetime. This clears the error.
This implies that frappe.utils.now_datetime() is returning a string. I could not find
documentation for “now_datetime”.

In the object inspector I saw this…

def now_datetime():
    dt = convert_utc_to_user_timezone(datetime.datetime.utcnow())
    return dt.replace(tzinfo=None)

Update

Problem solved !!!

If it is difficult to make the return value of frappe.utils.add_to_date(… of type datetime, then the
other way would be to change the “doc.creation” to type String by using add_to_date.
Except, now we add “Zero” days !!!

Here’s my code. This should wait 5 days before this workflow would become active
under the “Actions” drop-down of the Doctype for which the workflow was created.

frappe.utils.add_to_date(doc.creation, days=0, as_string=True, as_datetime=True) < frappe.utils.add_to_date(frappe.utils.now_datetime(), days=-5, as_string=True, as_datetime=True)

2 Likes