Acumatica - Require File be Attached - c#

In Bills and Adjustments, an error message "Please upload invoice" needs to display if user tries to save without attaching/uploading a document.
I created a bool field, UsrFilesAttached, that does not persist. On Rowselected event, i get a count, set bool if 0 or not.
I tried updating AP.APRegister DAC to [PXUIRequired(typeof(Where>))]
I tried something else in the BLC but I can't find it now.
//in APInvoiceEntry
protected void APInvoice_RowSelected(PXCache cache, PXRowSelectedEventArgs e)
{
var inv = (APInvoice)e.Row;
bool attachedFiles = PXNoteAttribute.GetFileNotes(cache, cache.Current).Length != 0;
cache.SetValueExt<APRegisterExt.usrFilesAttached>(inv, attachedFiles);
}
// in DAC AP.APRegister
[PXBool]
[PXUIField(DisplayName="UsrFilesAttached")]
[PXDefault]
[PXUIRequired(typeof(Where<usrFilesAttached, Equal<False>>))]
I expect that if UsrFilesAttached is false an error will appear. I am able to save record whether UsrFilesAttached is true or false. Also, how do I add a custom error message?

This morning, I had a different thought about how to tackle this and it worked. I started over with this and it works:
protected void APInvoice_Hold_FieldUpdated(PXCache cache, PXFieldUpdatedEventArgs e)
{
var inv = (APInvoice)e.Row;
if (inv == null)
return;
bool attachedFiles = PXNoteAttribute.GetFileNotes(cache, cache.Current).Length != 0;
if (attachedFiles == false)
{
cache.RaiseExceptionHandling<APRegister.hold>(inv, null, new PXSetPropertyException("Please attach invoice", PXErrorLevel.Error));
inv.Hold = true;
}
}
It makes better sense to do the check when trying to release the hold anyway. It can probably be improved on, so please teach me if you know a cleaner way. :)

Related

Umbraco 7, How to change a custom property in MemberService.Saved

I am struggling with changing a simple true / false flag in member properties once the Is Approved flag is set to true for the first time. I can change the property but the value is not saved / committed. I have tried both MemberService.Saved and MemberService.Saving. I am quite new to Umbraco so may have missed something obvious.
protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
MemberService.Saved += MemberService_Saved;
}
void MemberService_Saved(IMemberService sender, Umbraco.Core.Events.SaveEventArgs<IMember> e)
{
foreach (var member in e.SavedEntities)
{
if (!member.IsNewEntity())
{
var dirtyProperties = member.Properties.Where(x => x.WasDirty()).Select(p => p.Alias);
if (dirtyProperties.Contains("umbracoMemberApproved"))
{
if (member.IsApproved && !member.GetValue<bool>("approvalEmailSent"))
{
//Send Email to Customer
//new SmtpClient().Send(mail);
var prop = member.Properties["approvalEmailSent"];
prop.Value = true;
var propValue = member.GetValue<bool>("approvalEmailSent");
//Have verified propValue is now true
sender.Save(member);
}
}
}
}
}
Strangely I can find another member, make the property change and save it fine, just not the member sent through in e.SavedEntities.
TIA
Maybe try doing this instead?
member.SetValue("approvalEmailSent", true);
sender.Save(member, false);
When in MemberService.Saved, tell the .Save method to not raise any further events, just to make sure no infinite loops will happen.
This appears to me to be some sort of bug, after much debugging I have found the following:
Even when setting the raise events flag to false on the save, it still comes back through the method again.
On the second pass through you must set the value of the property you are changing again, even though it should already be saved / committed. I did not need to save again. I believe this is a bug
The original changed by the user property's was dirty flag is no longer set the second time through. So the original check for this was stopping the property I want to change being set again.
I had to have the was dirty check so that on the second pass through I did not send the email again.
As a note even changing something unrelated in a member using the umbraco front end will cause my method to run twice.
protected override void ApplicationStarted(UmbracoApplicationBase umbracoApplication, ApplicationContext applicationContext)
{
MemberService.Saved += MemberService_Saved;
}
void MemberService_Saved(IMemberService sender, Umbraco.Core.Events.SaveEventArgs<IMember> e)
{
foreach (var member in e.SavedEntities)
{
if (!member.IsNewEntity())
{
if (member.IsApproved && !member.GetValue<bool>("approvalEmailSent"))
{
member.SetValue("approvalEmailSent", true);
var dirtyProperties = member.Properties.Where(x => x.WasDirty()).Select(p => p.Alias);
if (dirtyProperties.Contains("umbracoMemberApproved"))
{
//Email Customer
//new SmtpClient().Send(mail);
sender.Save(member, false);
}
}
}
}
}

C# String comparison not working

I'm having this wierd problem within the application I'm currently working on.
string searchText = "onMouseOver=\"CallList_onMouseOver(this);\" id=\"";
List<int> searchOrders = AllIndexesOf(scraper.clientBrowser.DocumentText, searchText);
StringBuilder sb = new StringBuilder();
for (int i = 0; i < searchOrders.Count; i++)
{
string order = scraper.clientBrowser.DocumentText.Substring(searchOrders[i] + searchText.Length, 6);
scraper.clientBrowser.Document.GetElementById(order).InvokeMember("Click");
for (int j = 0; j < scraper.clientBrowser.Document.Window.Frames.Count; j++)
{
if (scraper.clientBrowser.Document.Window.Frames[j].Document != null && scraper.clientBrowser.Document.Window.Frames[j].Document.Body != null)
{
string orderText = scraper.clientBrowser.Document.Window.Frames[j].Document.Body.InnerText ?? "Nope";
//MessageBox.Show(j + Environment.NewLine + orderText);
if (!orderText.Contains("Nope"))
{
sb.AppendLine(orderText + Environment.NewLine);
}
}
}
}
Clipboard.SetText(sb.ToString());
The thing is, whenever I uncomment the MessageBox.Show, I can clearly see orderText is filled with another value than "Nope", the Stringbuilder gets filled, and the correct text is copied.
However if I comment the Messagebox.Show, the outcome of this loop is always "Nope". I'm stuck here, I have no idea what could cause something like this.
The scraper.clientBrowser is a System.Windows.Forms.WebBrowser.
Update:
Solved the issue by waiting for the document to be loaded, created this mechanism:
public bool DocumentLoaded
{
get { return documentLoaded; }
set { documentLoaded = value; }
}
private void wb_DocumentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
this.DocumentLoaded = true;
this.clientBrowser = sender as WebBrowser;
}
void clientBrowser_Navigating(object sender, WebBrowserNavigatingEventArgs e)
{
this.DocumentLoaded = false;
}
Then in the class I'm using:
while(!scraper.DocumentLoaded)
{
System.Threading.Thread.Sleep(100);
}
It sounds like you need to ensure that the page is fully loaded, like there might be a race condition. I would suggest wiring up the WebBrowser.DocumentCompleted event, and then attempting your scrapping logic.
Update
I overlooked this initially, this certainly has something to do with your issue. The line where you are invoking a click, like so scraper.clientBrowser.Document.GetElementById(order).InvokeMember("Click");. This is done in the iteration, which will more than likely manipulate the DOM -- will it not? I suggest going about this problem entirely different. What are you trying to achieve exactly, (not how you're trying to do it)?
With this alone, I would suggest that you refer to this SO Q/A and look at how they're waiting for the click to finish.
Only one thing I can guest here:
When you uncomment MessageBox.Show, at the time the message box show the info, the clientBrowser use this time to finish loading page. Then when you press OK on message box, the page is load completed, so you get the result. When you comment it, you dont wai for page loaded, so the result is diffent.

Validation Logic

I am trying to create some validation for a form I have. There are two text boxes and two radio buttons on the form. My logic for this validation I know is a little rusty at the moment so any suggestions would be great.
Here is the code for what I have so far:
Keep in mind that the int errors is a public variable in the class
Start Button code:
private void btnStart_Click(object sender, EventArgs e)
{
errors = validateForm();
//Here I want the user to be able to fix any errors where I am little
stuck on that logic at the moment
//validate the form
while (errors > 0)
{
validateForm();
errors = validateForm();
}
}
ValidateForm Method:
private int validateForm()
{
errors = 0;
//check the form if there are any unentered values
if (txtDest.Text == "")
{
errors++;
}
if (txtExt.Text == "")
{
errors++;
}
if (validateRadioBtns() == true)
{
errors++;
}
return errors;
}
ValidateRadioBtns Method:
private Boolean validateRadioBtns()
{
//flag - false: selected, true: none selected
Boolean blnFlag = false;
//both of the radio buttons are unchecked
if (radAll.Checked == false && radOther.Checked == false)
{
blnFlag = true;
}
//check if there is a value entered in the text box if other is checked
else if(radOther.Checked == true && txtExt.Text == "")
{
blnFlag = true;
}
return blnFlag;
}
Overall I feel like this can somehow be more stream lined which I am fairly stuck on.
Any suggestions would be greatly appreciated since I know this is such a nooby question.
Well first since you have said that you want to validate for non-entered values, did you consider white spaces as an entry? since someone can just press space and then your validation would pass.
Aside from that, you might want to indicate which textbox they did not fill out or which group they did not click, it seems like you are using web forms so here is a walkthrough http://msdn.microsoft.com/en-us/library/vstudio/a0z2h4sw(v=vs.100).aspx.
If you are using windows forms you can use this walkthrough http://msdn.microsoft.com/en-us/library/ms229603(v=vs.110).aspx.
If you need to keep the existing logic, I would suggest extracting the repeating logic into separate functions and temporary btnFlag is not necessary also as you can return true and return false at the end.
private Boolean validateRadioBtns()
{
if (radAll.Checked == false && radOther.Checked == false)
return true;
else if(radOther.Checked == true && txtExt.Text.Trim().Length == 0 ) //just a quick sample of how you can trim the spaces and check for length
return true;
return false;
}
See the documentation for the validation patterns. You have chosen the explicit validation strategy, for which you would use the ContainerControl.ValidateChildren method and either perform your "Start" action or not.
Windows Forms has dedicated events for validation that allow you to react accordingly for each of your controls. You'll use Control.Validating and Control.Validated events.
So, unless ValidateChildren returns true, you don't need to initiate your "Start" action, i.e. the logic would become trivial.
P.S. you also probably don't need the errors variable as a class member since you return it from your validation function. For showing the error, prefer the "Tell, Don't Ask" idea by separating the error visualization in a separate component.

Resting a private variable that should not happen

I have a strange problem I'm checking in my code behind the user if he is active or not with as simple if .. in my Page_Load method as you can see here
private TimeReport paramTR;
private ZevUser zevUser;
protected void Page_Load(object sender, EventArgs e)
{
ZevUser user = ZevUser.GetById(Int32.Parse(Session["SessionId"].ToString()));
if (user == null)
{
this.Response.Redirect("~/About.aspx");
}
this.getParameters();
if (!this.IsPostBack)
{
if (paramTR.ZevUser.Active == 0)
{
this.Response.Redirect("~/TimeReporting/TimeReportPanel.aspx");
}
this.bindData();
}
}
But when I make a go throw to this method I get allays nullreferenceexception why so ever .. but the private ZevUser variable is not null it's full..
I really don't have a clue why is this happing, it would be really cool if someone could explain me this why this is happening
Thanks for help and fast answer
You need to break your code down so you can debug it easier or add logging if you cannot debug this code locally.
Remember that when debugging something, the worse mistake you can make is to make assumptions. Start from the beginning and follow the process through. Don't assume that the problem is something and don't assume that the problem can't be something:
I've included a broken down, more readable version below. You can now add logging around this or easily add breakpoints:
private TimeReport paramTR;
private ZevUser zevUser;
protected void Page_Load(object sender, EventArgs e)
{
this.getParameters();
if (!this.IsPostBack)
{
if ((this.paramTR != null) &&
(this.paramTR.ZevUser != null) &&
(this.paramTR.ZevUser.Active == 0))
{
this.Response.Redirect("~/TimeReporting/TimeReportPanel.aspx");
}
this.bindData();
}
string sessionId = Session["SessionId"] as string;
if (sessionId != null)
{
int session = int32.Parse(sessionId);
ZevUser user = ZevUser.GetById(session);
if (user == null)
{
this.Response.Redirect("~/About.aspx");
}
}
}
Why are you passing the session id to ZevUser.GetById()? I would expect this to take a user id, or for the method to be called something like ZevUser.GetBySessionId(). At the moment it's quite confusing.
This line is causing the issue:
ZevUser user = ZevUser.GetById(Int32.Parse(Session["SessionId"].ToString()));
This is because Session["SessionId"] can be null, and is null in this case.
If you are looking to get the SessionId that is set by ASP.net, then use this.Session.SessionID (source).
If you are storing a value in Session["SessionId"] that you are trying to retrieve, then do a null-check first:
if (Session["SessionId"] != null) { ...
You should consider testing the SessionId variable before using it by doing something like that :
if (!string.IsNullOrEmpty(Session["SessionId"].ToString()))
ZevUser user = ZevUser.GetById(Int32.Parse(Session["SessionId"].ToString()));
The best way to debug your exception is to enable debug when the exception is thrown.
For this, go to Debug>>Exceptions
and then enable first four checkboxes (maybe) and then try debugging the project. You will be halted at the position from where the exception will be thrown.

Save System.Windows.Forms.Keys as Setting

I'm making a program that uses hotkeys for various things.
These hotkeys will be user-settable, and thus it'd be nice if those would be saved on exit.
Since the amount of keys on a keyboard is pretty large (including the OEM keys and such), I don't feel much warmth towards a huge monstrosity of IF clausules.
I tried using the builtin settings thingamajigg (Properties.Settings.Default etc) But it doesn't seem to save the keys properly. (That or I'm doing something wrong.)
This is the code I'm using for that right now:
// (..some code ommitted)
if (comboBox_hotkeys_uploadclipboard_modifier.SelectedText != "" && comboBox_hotkeys_uploadclipboard_key.SelectedText != "")
{
if (comboBox_hotkeys_uploadclipboard_modifier.SelectedText == "None")
Properties.Settings.Default.hotkeys_uploadclipboard_modifier = 0;
else
Properties.Settings.Default.hotkeys_uploadclipboard_modifier = modifierdict[comboBox_hotkeys_uploadclipboard_modifier.SelectedText];
Properties.Settings.Default.hotkeys_uploadclipboard_key = keydict[comboBox_hotkeys_uploadclipboard_key.SelectedText];
}
Properties.Settings.Default.Save();
return true;
And at the beginning of the program i do:
Dictionary<string, uint> modifierdict = new Dictionary<string, uint>();
Dictionary<string, Keys> keydict = new Dictionary<string, Keys>();
public Form_stuff()
{
for (int i = 0; i < Enum.GetNames(typeof(ModifierKeysH)).Length; i++)
{
modifierdict.Add(Enum.GetNames(typeof(ModifierKeysH))[i], (uint)Enum.GetValues(typeof(ModifierKeysH)).GetValue(i));
}
for (int i = 0; i < Enum.GetNames(typeof(Keys)).Length; i++)
{
keydict.Add(Enum.GetNames(typeof(Keys))[i], (Keys)Enum.GetValues(typeof(Keys)).GetValue(i));
}
}
But, it doesn't seem to actually save the keys, or load them on the next program launch. (yes I have code in place for that part too)
Does anyone know a(n easy) way?
EDIT:
Here's the code i use for setting the hotkeys, without the interop parts etc, those are hosted elsewhere in the code:
public void SetKeyboardHooks()
{
if (!(kbhook == null))
kbhook.Dispose();
kbhook = new KeyboardHook();
//Set clipboard upload hotkey
kbhook.RegisterHotKey((ModifierKeysH)Properties.Settings.Default.hotkeys_uploadclipboard_modifier, Properties.Settings.Default.hotkeys_uploadclipboard_key);
kbhook.KeyPressed += new EventHandler<KeyPressedEventArgs>(kbhook_KeyPressed);
}
void kbhook_KeyPressed(object sender, KeyPressedEventArgs e)
{
if (e.Modifier == (ModifierKeysH)Properties.Settings.Default.hotkeys_uploadclipboard_modifier && e.Key == Properties.Settings.Default.hotkeys_uploadclipboard_key)
{
string url = Functions.UploadClipboard();
Clipboard.SetText(url);
hificon.ShowBalloonTip(5000, "Upload succesful!", "Uploaded to: " + url,ToolTipIcon.Info);
}
}
As you can see the code is far from finished. This is just a roadblock here i'm trying to overcome..
The main thing that doesn't seem to be working is the fact that the hotkeys don't actually get SAVED. settings doesn't seem to like System.Windows.Forms.Keys, and then also skips the ModifierkeysH part, which are saved as uint.
False alarm people, it works just like it should, except for some reason the Comboboxes didn't return a value with .SelectedValue, instead you have to use .SelectedItem for some reason.
In any case, it works flawlessly now. thanks for everyone's time and may this thread slap someone in the face in the future for not using .SelectedItem .

Categories